Master Web Technology(2008/09) – SEO Contest

logo-master-3

Nonstante tutto il Seo Contest organizzato nel Master in Tecnologie del Web dell’università di L’Aquila continua. Abbiamo assistito alle lezioni presso la Oracle di Roma, la quale non ringrazierò mai abbastanza per avermi ospitato, insieme ai miei colleghi, in quei giorni convulsi e disorientati. Come ogni anno il principale attore del contest e del corso è stato il famoso Giorgio Taverniti, il quale non ha bisogno di presentazioni nell’ambito SEO. Il Seo contest di quest’anno ha una tematica particolare, ossia cercare di capire come Google riesce ad intercettare i contenuti duplicati.  Il tema del progetto è cercare di prendere i contenuti duplicati da più fonti in modo automatico (o tramite mashup o tramite operazioni di information retrieval) ed istruire un’algoritmo che (con supervisione umana) capisca quanto testo miscelare da ogni fonte. Inutile dire che l’interesse di studio su questo tema è alto sia dal punto di vista tecnico che in ambito SEO. Io sono impegnato, insieme ad altri mie colleghi, nella promozione di un sito che ha come tema le riserve del lazio. Se avete voglia di promuoverci, potete scaricare il depliant di promozione, ve ne saremo grati.

Compilare codice python da buffer o stringa [PoC]

Premettendo che la compilazione di un file python non e’ assolutamente obbligatoria e che l’interprete riesce a riconoscere benessimo come e quando richiamare un file precompilato quando si fa l’import di un modulo, è possbile modificare la funzione py_compile.compile() in modo tale da permette la compilazione di un sorgente contenuto in un buffer anzichè in un file sorgente. Esempio:

[Proof of concept]

import __builtin__
import imp
import marshal
import os
import sys
import traceback
 
MAGIC = imp.get_magic()
 
 
# Define an internal helper according to the platform
if os.name == "mac":
    import MacOS
    def set_creator_type(file):
        MacOS.SetCreatorAndType(file, 'Pyth', 'PYC ')
else:
    def set_creator_type(file):
        pass
 
def wr_long(f, x):
    """Internal; write a 32-bit int to a file in little-endian order."""
    f.write(chr( x        & 0xff))
    f.write(chr((x >> 8)  & 0xff))
    f.write(chr((x >> 16) & 0xff))
    f.write(chr((x >> 24) & 0xff))
 
 
def compileString(code, filename, ext=".pyc"):
 
    if type(code) is not str:
        raise "Error, code malformed -> type_code(buffer)";
 
    if code and code[-1] != '\n':
        code = code + '\n';
    try:
        codeobject = __builtin__.compile(code, filename, 'exec')
        print codeobject.co_code
    except Exception:
        print "Error in the code"
    cfile = filename + ext;
    fc = open(cfile, 'wb')
    fc.write('\0\0\0\0')
    timestamp = long(os.fstat(fc.fileno()).st_mtime)
    wr_long(fc,timestamp)
    marshal.dump(codeobject, fc)
    fc.flush()
    fc.seek(0,0)
    fc.write(MAGIC)
    fc.close()
    set_creator_type(cfile)
 
compileString("print 0;", "pppr");

Linux Kernel Debug con Eclipse

Molto spesso le persone mi guardano stralunate quando gli dico che sto cercando di mettere mano al codice del kernel linux per cercare di renderlo modulare. La prima reazione è senz’altro quella di dire: “a che serve? Tanto linux va bene così com’è.”.  La mia risposta è senz’altro banale quanto ovvia,  non pretendo mica di diventare ricco con una cosa che faccio durante quelle domeniche pomeriggio molto noise, anche perchè, stando a L’Aquila, non è che ci sia proprio un granchè da fare. C’è chi gioca a scacchi su internet, chi guarda la tv o chi semplicemente dorme sul divano. A me piace provare nuove cose al pc e/o leggere un bel librozzo di teoria, di quella sana teoria che sa far storcere il naso a tutti. Le difficoltà che si incontrano quando si cerca di capire come funziona il kernel sono senza dubbio capire che, benchè si conosca il C e l’assembler, se non si ha un libro o manuale di riferimento del kernel non si va molto lontano. Linux sarà open-source, ma quando a documentazione del kernel lascia un po a desiderare, quanto meno la cartella doc dei sorgenti ha uno stile organizzativo che a me non piace e i commenti sul codice  sono semplicemente… semplicemente non ci sono! Per iniziare con la comprensione, consiglio a tutti di leggere almeno questi due libri, facili e molto esaustivi:

Se invece si ritiene che non si hanno le capacità o le basi per la programmazione di sistema in linux, un’altro ottimo testo per imparare ad usare le syscall di linux è: Linux System Programming. Partendo da questi libri, ma che non siamo di certo obbligati a leggerli per intero per renderci conto di come vanno le cose, passiamo a sporcarci le mani. Ovviamente, se devi modificare il codice del kernel linux, usa linux come sistema per il tuo ambiente operativo. Molti nello sviluppo di applicazione embedded Linux-based usano windows equipaggiato con cygwin,  tool-chain  e croos-compilatori. Ora capisco chi deve implementare su una architettura diversa, tipo un’ARM o Mips il quale ha senz’altro bosogno di un cross-compilatore, ma complicarsi la vita per configurare e compilare il kernel su un OS diverso e’ come iniziare a studiare matematica partendo dall’ipotesi di Riemann, questo per dire che è una congettura sperare di compilare il kernel usando Windows.  Con linux ed eclipse e’ estramamente semplice avere un’ambiente per la visione e modifica del codice e anche per effettuarne il debug in modo molto versatile con GDB. Riporto di seguito il link dove potete trovare un’interessate tutorial, con tanto di screen-shot per configurare la cosa: http://issaris.blogspot.com/2007/12/download-linux-kernel-sourcecode-from.html. Vi informo che dovrete procuravi, oltre ai sorgenti del kernel linux una versione recente di Eclipse, anche se credo sia meglio prendere la 3.4 o ganymede per gli esperimenti. Per far partire eclipse sotto ubuntu assicuratevi di aver installato la Java Virtual Machine rilasciata da SUN e non quella open-source (OpenJVM). Il motivo è che la seconda è molto meno performante ed eclipse potrebbe metterci una vita per caricare.  Un’altro ottimo tool per il debug è ovviamente un simulatore e Qemu sembra proprio fare al caso, in quanto ci permette di testare il kernel senza neanche aver bisogno di costruire un’immagine del disco.

Verificare l’esistenza di una destinazione (javascript, ajax e scriptacolous)

Ci troviamo di fronte ad un problema comune in siti che trattano di vacanze o che hanno a che fare con posizioni geografiche (geoname) per alcune destinazioni. I requisiti ci chiedono di fornire un servizio di ricerca di una posizione geografica, come fare? Si potrebbe tentare di definire una ricerca guidata, fornendo all’inizio una form per selezionare il continente e lo stato, per poi raffinarla. E’ un classico dilemma di information retrival quando si cerca di rispondere alla domanda: “E’ meglio usare un motore di ricerca o una web directory per ottenere le informazioni che sto cercando?”.  La faccenda è abbastanza controversa, perchè noi potremmo cercare una destinazione di cui non conosciamo il continente o lo stato.In questo articolo fornirò un modo per interrogare un webservice esterno tramite javascript per farci restituire una o più destinazioni geografiche che corrisponderanno meglio alla nostra keyword inserita. Partiamo subito con il codice che ci servirà per fare la chiamata asincrona lato client.

/**
 * funzione javascript per una richiesta asincrona al webservice geoname
 * (gestita da uno script php)
 *
 * @param destinazione
 *            string prende in input la distanizione di un utente
 * @param IdDivElement
 *            string prende l'id dell'elemento div dove visualizzare il
 *            contenuto
 * @param cntx
 * 		     string specifica il contesto della ricerca del geoname
 * 		            ossia per la locazione corrente o per le destinazione preferite etc..
 *
 *
 *
 */
function verifyDestination(destinazione, IdDivElement,cnxt) {
 
	// Elemento dove visualizzare i risultati
	IdContent = IdDivElement;
 
	context = cnxt;
 
	if(destinazione == "" || destinazione == undefined){
 
		Ext.MessageBox.alert('Error', 'Please, insert a location');
		return;
 
	}
	//alert(destinazione);
 
	// e' possibile crittografare l'url per renderlo
	// illegibile, oltre ad utilizzare l'offuscamento del codice
	var geonameWS = "verify_destination.php?q=";
	geonameWS = geonameWS + destinazione;
 
	//alert(geonameWS);
 
	if (window.XMLHttpRequest) {
		// Mozilla based
		request = new XMLHttpRequest();
 
	} else if (window.ActiveXObject) {
 
		request = new ActiveXObject("Msxml2.XMLHTTP");
		if (!request) {
 
			request = new ActiveXObject("Microsoft.XMLHTTP");
		}
	}
	if (typeof request == 'undefined') {
 
		alert("Ajax not supported!");
		return;
	}
 
	// TODO: Visualizzare un messaggio di attesa dei risultati
 
	var indicator = document.getElementById('indicator'+context);
	indicator.style.display = "";
 
	request.onreadystatechange = callback;
	request.open("GET", geonameWS, true);
	request.send(null);
}

Fino a questo punto le operazioni sono ovvie e classiche per ciascuna chiamata asincrona con javascript. E’ da notare come il nostro file php che inoltra la chiamata al webservice esterno si chiama verify_destination.php, al qual passiamo  un parametro che rappresenterà la nostra destinazione ricercata. Infatti, come da titolo dell’articolo noi siamo interessati a validare una località, ossia verificare che l’utente inserisce effettivamente il nome di una località esistente, non ad esempio Topolinia. Vi sono altri aspetti sul codice, come il contesto (cntx) e l’id dell’elemento div per la visualizzazione che saranno trattati in seguito. Una volta inoltrata la richiesta il nostro script php ci restituirà i risultati in formato xml che noi andremo a parsare. Lo script verify_destination è molto semplice, poichè il webservice accetta chiamate http.

define ( "MAXROWS", 5 );
define ( "FEATURECLASS", "P" );
 
if(!isset($_SESSION['user']['username'])){
	echo 'You are trying a DoS or DDoS attack, your IP address is tracked';
	exit();
}
$destination = $_GET ['q'];
//$destination = addcslashes($destination,"'");
//se uso il web service
 
$destination = stripcslashes($destination);
include_once ("include/ws_geoname.inc.php");

Il file incluso nello script fa la semplice chiamata http e restituisce il document xml.

header ( 'Content-Type: text/xml' );
$url = 'http://ws.geonames.org/search?q=';
$destination = urlencode($destination);
$param = $destination.'&maxRows=5'.'&featureClass='.FEATURECLASS.'&style=FULL';
$url.=$param;
$req = file_get_contents ( $url );
echo $req;

Come si può vedere con la funzione file_get_contents($url) viene fatta una richiesta http per scaricare il documento xml dal webservice e con una semplice echo ridirezioniamo il contenuto al client. Come da codice, nel sorgente javascript mostrato abbiamo dichiarato una funziona callback che verrà richiamata quando il client riceverà la richiesta. La funziona javascript si chiama semplicemente callback.

function callback() {
 
	if (request.readyState == 4) {
		if (request.status == 200) {
 
			// XML
			xmlDoc = request.responseXML;
			// document.write(xmlDoc);
 
			//TODO: disabilitare il messaggio di attesa dei risultati
 
			var indicator = document.getElementById('indicator'+context);
			indicator.style.display = "none";
 
			display();
 
		} else {
			alert("Service no working..");
		}
 
	}
}

Il codice precedente è quasi standard in una chiamata asincrona con ajax, quando la richiesta è stata ricevuta il browser prende il contenuto e viene dichiarato che è in formato xml con il metodo request.responseXML. Ora che abbiamo il contenuto salvato in una variabile con scope globale all’interno dello script è possibile parsarlo è recuperare il codice che ci serve.

/**
 * Visualizza i risultati nella div predefinita
 */
function display() {
 
	var results;
	var name;
	var countryName;
	var adminName;
	var numRes;
	var lat;
	var lng;
	var geonameId;
	var continentCode;
 
	document.getElementById(IdContent).innerHTML="";
 
	try {
		var totalRes = xmlDoc.getElementsByTagName("totalResultsCount");
 
		numRes = totalRes[0].firstChild.nodeValue;
 
		results = "Number of results " + numRes + " ";
 
		results += "
 
<ul>";
 
		var geonames = xmlDoc.getElementsByTagName("geoname");
		// alert(geonames[0].childNodes[1].nodeName);
		for ( var i = 0; i &lt; geonames.length; i++) {
 
			var childs = geonames[i].childNodes;
			// Scorro tutti nodi figli dell'elemento geoname
			for ( var j = 0; j &lt; childs.length; j++) {
 
				if (childs[j].nodeName == "name") {
					name = childs[j].firstChild.nodeValue;
				} else if (childs[j].nodeName == "countryName") {
					countryName = childs[j].firstChild.nodeValue;
				} else if (childs[j].nodeName == "adminName1") {
					adminName = childs[j].firstChild.nodeValue;
					break; // Se sono arrivato qui stop
				} else if (childs[j].nodeName == "lat") {
					lat = childs[j].firstChild.nodeValue;
				} else if (childs[j].nodeName == "lng") {
					lng = childs[j].firstChild.nodeValue;
				} else if (childs[j].nodeName == "geonameId") {
					geonameId = childs[j].firstChild.nodeValue;
				} else if (childs[j].nodeName == "continentCode") {
					continentCode = childs[j].firstChild.nodeValue;
				}
			} // end for
 
			name = addslashes(name);
			adminName = addslashes(adminName);
			countryName = addslashes(countryName);
 
 
			results += "<li><a href=\"javascript:selectDestination(";
			results += "'" + name + "'," + "'" + adminName + "','"
					+ countryName + "','" + lat + "','" + lng + "','"
					+ geonameId + "','" + continentCode +"')\">";
			results += name + "," + adminName + "," + countryName
					+ "</a> </li>";
 
 
		}// end for
 
	       results += "</ul>";
 
 
		//alert(results);
 
	} catch (e) {
		alert("An exception occurred at:" + e.name + " " + " Error msg:"
				+ e.message);
	}
 
	// creo il div con le informazioni
	var div = document.getElementById(IdContent);
 
	subdiv = document.createElement("div");
 
	if(numRes == '0'){
 
		results+="please insert a different location";
 
	}
 
	subdiv.innerHTML = results;
	div.appendChild(subdiv);
 
	/***************************************************************************
	 * Chiamata al framework scriptaculous
	 **************************************************************************/
 
	Effect.SlideDown(IdContent);
 
	return;
 
}

Ho cercato di formattare il codice il meglio possibile, ma i limiti del plugin di wordpress non permettono di fare di più (almeno per ora). Per sopperire a tale inconveniente allegherò il codice a fondo artcolo. Lo script precendente visualizza i risultati tramite scriptacolous dando un effetto a tendina scorrevole che appare una volta ricevuti i risultati. Nel caso di poter usare questo script in più elementi di una form, come ad esempio due input form. In ultimo abbiamo bisogno di un modo per permettere all’utente di selezionare una destinazione e modificare gli elementi html predefiniti.

/**
 * @author Gianfranco Murador
 * @param name
 *            string nome del geoname
 * @param adminName
 *            string regione del geoname
 * @param countryName
 *            string stato del geoname
 * @param lat
 *            string latitudine
 * @param lng
 *            string longitudine
 * @param geonameId
 *            string identificatore
 *
 *
 * Selezione il geoname e setta alcuni campi di input nascosti per la form
 *
 **/
 
function selectDestination(name, adminName, countryName, lat, lng, geonameId, continentCode) {
 
	var inputGeoname = document.getElementById('inputGeoname'+context);
	inputGeoname.value = name + "," + adminName + "," + countryName;
 
	alert("lat"+context);
 
	var latitudine = document.getElementById('lat'+context);
	latitudine.value = lat;
 
	var longitudine = document.getElementById('lng'+context);
	longitudine.value = lng;
 
	var geoId = document.getElementById('geonameId'+context);
	geoId.value = geonameId;
 
	var n = document.getElementById('name'+context);
	n.value = name;
 
	var cName = document.getElementById('countryName'+context);
	cName.value = countryName;
 
	var cCode = document.getElementById('continentCode'+context);
	cCode.value = continentCode;
 
	var aName = document.getElementById('adminName'+context);
	aName.value = adminName;
 
	/***************************************************************************
	 * Chiamata al framework scriptaculous
	 **************************************************************************/
 
	$(IdContent).hide();
 
	// Pulisco il contenuto dell'elemento div
	var div = document.getElementById(IdContent);
	inputGeoname.setAttribute('readonly','readonly');
	div.innerHTML = "";
	var buttonChange = document.getElementById('buttonChange'+context);
	buttonChange.style.display = "";
 
	var button = document.getElementById('button'+context);
	button.style.display ="none";
 
	inputGeoname.style.borderColor="red";
 
}

Con questo script bloccheremo l’input text per la validazione e useremo un’altro button per sbloccarlo se l’utente ha sbagliato a selezionare la destinazione. Da notare come il codice è quasi del tutto indipendente, nel funzionamento, da scriptacolous, infatti potremmo usare un diverso controllo per la visualizzazione delle risposte. Per cambiare la destinazione uso la seguente function:

function changeDestination(cntxt){
 
	var inputGeoname = document.getElementById('inputGeoname'+cntxt);
	inputGeoname.removeAttribute('readonly');
	inputGeoname.style.borderColor = "";
 
	var buttonChange = document.getElementById('buttonChange'+cntxt);
	buttonChange.style.display = "none";
 
	var button = document.getElementById('button'+cntxt);
	button.style.display ="";
 
}
 
/****
 * Questa funzione aggiunge un carattere
 * di escape per permettere l'inserimento
 * di carattere contente un apice. Lato server
 * php deve ripristinare gli apici
 *
 * ****/
 
function addslashes(str) {
 
	return (str+'').replace(/([\\"'])/g, "\\$1").replace(/\0/g, "\\0");
	}

Il nostro file html per funzionare deve contenere dei tag del genere per far funzionare gli script definiti precedentemente:

<!-- GESTIONE LOCAZIONI FUTURE -->
<input id="inputGeonameDestinazioniFuture" name="inputGeonameDestinazioniFuture" type="text" />;
 
<div class="buttonLink">
<input id="buttonChangeDestinazioniFuture" class="formButton" style="display: none" onclick="javascript:changeDestination('DestinazioniFuture')" type="button" value="Change" />
<input id="buttonDestinazioniFuture" class="formButton" onclick="javascript:verifyDestination(document.getElementById('inputGeonameDestinazioniFuture').value,'geonameDestinazioniFuture','DestinazioniFuture')" type="button" value="Validate Location" /></div>
 
 
<div id="uploadProgressFuture" style="display: none;">
<img style="vertical-align: middle;" src="profile_edit_locations.php_files/loading.gif" alt="" />
<strong>Validating, please wait...</strong></div>

Come ultimo accorgimento è bene ricordarsi di includere lo script nell’header o in qualche altra parte, come includere anche gli script di scriptacolous necessari per l’effetto desiderato.  Allegherò il codice non appena possibile.

[Off-Topic] Toxicity

Conversion, software version 7.0,
Looking at life through the eyes of a tire hub,
Eating seeds as a past time activity,
The toxicity of our city, of our city,

New, what do you own the world?
How do you own disorder, disorder,
Now, somewhere between the sacred silence,
Sacred silence and sleep,
Somewhere, between the sacred silence and sleep,
Disorder, disorder, disorder.

More wood for their fires, loud neighbors,
Flashlight reveries caught in the headlights of a truck,
Eating seeds as a past time activity,
The toxicity of our city, of our city,

New, what do you own the world?
How do you own disorder, disorder,
Now, somewhere between the sacred silence,
Sacred silence and sleep,
Somewhere, between the sacred silence and sleep,
Disorder, disorder, disorder. {x2}

When I became the sun,
I shone life into the man’s hearts,
When I became the sun,
I shone life into the man’s hearts.