C++ CGI und mySQL

Ein CGI-Programm in C++ zu schreiben ist – eigentlich – ziemlich einfach:

#include 

using namespace std;

int main()
{
	cout << "Content-Type: text/html" << endl << endl;
	
	cout << "Hello CGI" << endl;
	cout << "

C++ says Hi to the CGI API

" << endl; cout << "" << endl; return 0; }

Das ganze kann man jetzt durch einen C++ Compiler jagen, in das cgi-bin Verzeichniss des Webservers verschrieben, chmod +x, und dann zB unter http://webserver/cgi-bin/test.cgi ansehen. Nicht sehr spannend, da man diese statische Seite ja auch direkt in HTML schreiben hätte können und sich den Umweg über CGI sparen hätte können.

Interessant wird es also, wenn man mit C++ über CGI dynamische Webseiten (dynamisch nicht im Sinne von JavaScript, sondern eher ähnlich wie PHP) generieren möchte. Ein erster Schritt wäre hier, die GET-Variablen auszulesen. Die GET-Variablen werden von der CGI in die Environment-Variable QUERY_STRING geschrieben, und zwar in folgendem Format:
[key1]=[value1]&[key2]=[value2]&....&[keyN]=[valueN]
Wir müssen uns also eine Funktion zum Parsen dieses Strings schreiben:

void getVariables(std::unordered_map &variables)
{
	// fetch the query string
	char* raw_query_string = getenv("QUERY_STRING");

	// query string empty?
	if (raw_query_string == NULL) 
	{
		variables.clear();
		return variables;
	}
	
	// key and value into a stringstream, init with an empty stream
	// to make sure get-queries such as ?step=1&confirm are parsed 
	// correctly
	std::stringstream key, value;

	key.str(std::string(""));
	value.str(std::string(""));

	// are we currently parsing a key or a value?
	bool isKey = true;

	// loop until we read all raw data
	while (*raw_query_string != '')
	{
		// if we read a &, we'll change or type to key and store 
		// are previous key and value variables into out map
		// and clear them
		if (*raw_query_string == '&') 
		{ 
			variables[key.str()] = value.str();
			isKey = true; 
			key.str(std::string(""));
			value.str(std::string(""));
		}
		// if we read a =, we'll change or type to value
		else if (*raw_query_string == '=') 
		{ 
			isKey = false; 
		}
		// anything else goes into the currently active stream
		else 
		{
			if (isKey) { key << *raw_query_string; }
			else { value << *raw_query_string; }
		}
		// move pointer to next char
		raw_query_string++;
	}
	
	// store last key value pair, because query-strings 
	// normaly don't end on &
	variables[key.str()] = value.str();
	
	return variables;
}

Das Problem ist, dass die keys und values URL-codiert sind, wenn man also Text verarbeiten möchte muss man sie noch entsprechend dekodieren. Das ist aber an dieser Stelle erstmal Aufgabe des Lesers 😉

Natürlich möchte man jetzt nicht nur GET-Variablen lesen, sondern auch Daten empfangen können, die per POST geschickt werden. Diese schickt der WebServer an stdin des CGI-Scripts. Und leider nicht -terminiert, aber das ist kein Problem, da die Umgebungsvariable CONTENT_LENGTH die genaue Länge der POST-Daten enthält. Die POST Daten sind in gleichen Format wie die GET-Daten. Das Parsen funktioniert also entsprechend ähnlich, allerdings muss man erst beispielsweise per fread die Daten aus dem stdin in ein char-buffer lesen und -terminieren bevor man sie durch entsprechende parsing Funktion schicken kann.

Mit diesen zwei Funktionalitäten kann man schon eine Menge anstellen (wie zB ein Datei-basiertes Gästebuch), allerdings muss man beim Ausgeben von Benutzereingaben daran denken diese Entsprechend zu Maskieren, damit man keine XSS-Attacken und Ähnliches erlaubt. Dafür habe ich mir folgende kleine Hilfsfunktion geschrieben:

std::string htmlspecialchars(std::string input)
{
	replaceAll("&", "&", input);

	replaceAll("", ">", input);

	replaceAll("'", "'", input);
	replaceAll(""", """, input);

	return input;
}

void replaceAll(std::string search, std::string replace, std::string &str)
{
	size_t currPos = 0;
	size_t findPos;
	while((findPos = str.find(search, currPos)) != std::string::npos) 
	{
		str.replace(findPos, search.size(), replace);
		currPos = findPos + replace.size();
	}
}

Jetzt wird’s entlich interessant: Wir möchten in unserer C++ CGI-Anwendung natürlich auch (wie gewohnt aus PHP) eine mySQL-Datenbank ansprechen. Ich habe zuerst ziemlich lange mit dem MySQL Connector/C++ rumprobiert, hab das ganze allerdings nicht wirklich vernünftig zum Laufen bekommen (wenn Details gewünscht sind, kann ich gerne noch einen weiteren Blogpost dazu schreiben :-D). Viele Probleme die bei mir aufgetreten sind waren wohl nicht unbekannt und sind (teilweise) sogar ohne Lösung auf StackOverflow zu finden. Dort bin ich dann auch auf die Idee gekommen einfach die MySQL C-API zu nehmen. Gesagt getan, ich hab mir einen kleinen Wrapper dafür geschrieben und ab dort konnte ich ohne Probleme über C++ mit meiner mySQL Datenbank reden. Um die C-API zu benutzen, muss man einfach von der MySQL Homepage den „C-Connector“ runterladen, das include-Directory im Compiler mit einbinden, das lib-Directory dem Linker geben und mit der libmysql linken. Dann kann man die Funktionen per #include „mysql.h“ laden und verwenden. Je nach Projekt kann es nötig sein noch die SOCKET-Header (unter windows winsock2.h, unter linux sys/socket.h und arpa/inet.h) einzubinden.

Kleines Beispiel-Programm:

#include 

#include "mysql.h"

using namespace std;

int main()
{
	cout << "Content-Type: text/html" << endl << endl;
	
	cout << "nmySQL TEST" << endl;
	
	MYSQL *conn = mysql_init(NULL);
	
	if (mysql_real_connect(conn, "localhost", "root", "", "testdatabase", NULL, 0, NULL) == NULL)
	{
		printf("mySQL Error #%u: %s 
n", mysql_errno(conn), mysql_error(conn)); return 0; } if (mysql_real_query(conn, "SELECT name, age FROM clients") != 0) { printf("mySQL Error #%u: %s
n", mysql_errno(conn), mysql_error(conn)); return 0; } cout << "

Client age table:

" << endl; MYSQL_RES *result = mysql_store_result(conn); MYSQL_ROW row; while (row = mysql_fetch_row(result)) { printf("n", row[0], row[1]); } mysql_free_result(result); mysql_close(conn); cout << "
Name age
%s%d
" << endl; return 0; }

Da ich ja am TWLan-Projekt mitarbeite (www.twlan.org) habe ich als Test mal die Startseite und die Registration in C++ implementiert. Dafür habe ich noch eine kleine C++ HTML Template Klasse geschrieben, ist allerdings nichts besonderes. Nun habe ich die PHP-Startseite und die C++ CGI-Startseite mal (ohne Caching) gegeneinander im Punkto Geschwindigkeit gegenübergestellt:

PHP-TWLan: 0.2501s 0.0743s 0.0771s 0.0693s 0.0672s 0.0635s 0.0775s
AVG:	   0.097s

CGI-TWLan: 0.0220s 0.0321s 0.0142s 0.0354s 0.0199s 0.0194s 0.0195s
AVG:	   0.023s

Die CGI-Version ist, selbst bei nur 2-Datenbank-Abfragen, 4x schneller als der PHP-Code. Der sehr hohe Wert bei der ersten PHP-Zeitmessung erklärt sich übrigens dadurch, dass die Zend-Engine beim ersten durchlauf den PHP-Opcode cached. Ein solches Ergebnis war zu erwarten – und wenn man es dann mal selbst erlebt ist es doch sehr motivierend weitere Web-Anwendungen in C++ zu schreiben. Wenn man dann noch FastCGI unterstützt, und zB lighttpd als Webserver verwendet, dann hat man wirklich die ultimativ-schnelle Web-Anwendung 🙂

Das „TWLanCGI“ Projekt gibts im TWLan-Forum übrigens für Windows zum Download (hier).

 

WebSocketServer

Gerade eben habe ich die erste öffentliche Version meines C++ WebSocketServers veröffentlicht. Es gibt eigentlich nicht viel dazu zu sagen, alle Informationen stehen auf der Webseite: WebSocketServer.

Zusammengefasst ist es ein kleiner Server, der das HTML5-WebSocket Protokoll unterstützt. Um Anwendungen für den Server zu schreiben, kann man diese entweder in C++ oder in LUA einbauen!

 

ReTI IDE & Simulator (Technische Informatik)

Da ich aktuell an der Veranstaltung Technische Informatik an der Uni teilnehme war es im letzten Übungsblatt Aufgabe Programm-Code für einen einfachen Prozessor (ReTI oder auch RESA) zu schreiben. Das vorgeschlagene Tool dafür (Neumi) kann entsprechenden Programm-Code zwar super visualisieren, ist allerdings beim „coden“ nicht sehr hilfreich da es kein vernünftiges Feedback bei Fehlern gibt und es auch keine gute Eingabemaske für den Code gibt. Deshalb hab ich mich hingesetzt und eine kleine HTML-JavaScript-IDE mit einer ReTI-Simulation in PHP zusammengebaut. Das ganze ist unter reti.agrafix.net zu finden! Ich habe auch eine kleine Dokumentation dazu geschrieben, es sollte sich also alles von selbst erklären.
Ein paar Worte vielleicht noch zur „Simulation“: Die Simulation ist eher ein Debugger, da der Code nicht assembliert sondern direkt interpretiert wird. Das ist notwendig um zielgenaues Feedback bei (Laufzeit-)Fehlern zu geben. Deshalb ist es nicht möglich auf Speicherzellen zuzugreifen, die Anweisungen/Befehle enthalten. (Mit dieser Einschränkung konnte man dennoch alle Aufgabenstellungen bis jetzt lösen 😉 ) Solange man das also nicht benötigt, kann der Simulator den korrekten Endspeicherzustand berechnen.

Zur ReTI IDE & Simulator

Für die HTML-JavaScript-IDE bzw. dessen Code-Editor habe ich übrigens auf das CodeMirror 2 Framework gebaut. Super Framework, leider muss man erstmal ein bisschen Dokumentation lesen bis man damit zu guten Ergebnisen kommt!

 

C++ & Lua WebSocketServer

Hi,

ich möchte eigentlich nur ein kurzes Update zu meinem Projekt C++ & Lua WebSocketServer geben: Das ganze läuft schon recht gut, nur leider ist noch ein kleiner Bug in der Implementierung der HYBI10-En/Dekodierung der den Server ab und zu zum Abstürzen bringt. Das ist natürlich schlecht und muss deshalb erst noch behoben werden!

Just a small update on the C++ & Lua WebSocketServer: Due to a bug in my hybi10-implementation the release will be delayed until I fix that problem – stay tuned!

-agrafix

 

Manager’s Life Update

Gerade habe ich ein neues Manager’s Life Update eingespielt. Es enthält das versprochene Casino mit dem Poker Multiplayer!

Das Poker-Spiel ist komplett in HTML5+Canvas, JavaScript und PHP realisiert und sollte auf jedem HTML5-Endgerät funktionieren.

Außerdem habe ich noch viele kleinere Bugs behoben und ein paar Änderungen an der Sprite-Engine vorgenommen.

Viel Spaß beim Pokern! 😉

 

Manager’s Life V2

Die Entwicklung von Manager’s Life V2 geht langsam auf ein Release zu – deshalb wird die aktuelle Version am 25.11.2011 beendet! Wir bedanken uns bei allen, die bisher das Spiel gespielt haben und würden uns freuen euch wieder bei Version 2 begrüßen zu dürfen.

Das alte Spielkonzept wird in Version 2 komplett über den Haufen geworden und das Spiel wurde von Grund auf neu Programmiert. Erhalten bleibt eigentlich nur das Design! Ich möchte jetzt noch nicht zu viel verraten, aber es wird aufjedenfall ein Online-Spiel, von dem es in dieser Form erst sehr wenige gibt. Es nutzt die modernen HTML5 und JavaScript Techniken komplett aus, ist sehr grafisch und funktioniert auch auf mobilen Endgeräten.

Lasst euch überraschen!

Wer einen Beta-Key möchte, um in der Closed Beta mitzuspielen, schickt bitte eine Email an allypage@gmail.com. Die Keys sind jedoch nur sehr begrenzt erhältlich!

Manager’s Life

 

Zippyshare Downloads automatisieren

Zur Zeit arbeite ich an einem Downloadmanager, der alle gängigen Download-Seiten unterstützen soll. Neulich hat zippyshare.com etwas eingebaut um genau diesen „automatischen“ Downloads einen Riegel vorzuschieben: Der Download-Link wird jetzt per JavaScript „verschlüsselt“. Ich werde jetzt hier keinen Beispiel-Zippyshare-Downloadlink posten, das findet man mit etwas googlen schnell selbst 😉

Nun möchte ich erläutern, wie ich den Download-Link automatisch auslese:
Zuerst entnehme ich der Seite per Regulärem Ausdruck die benötigten JavaScript Variablen:
xHTML-Code der Stelle

var zipdata = eval(eval(eval(((function() {var parzDiXdet= (((function() {var hutByDojpZ= eval(379);var BvLCmMYDEQB= ((function() {var qefrKDjxbJ= eval(51);var DKbCbnceoJN= ((((18+(3*3)))+((function() {var kzCDpmQUwq= eval(eval(43));var UoRHKeZvyaH= 23;return (kzCDpmQUwq+UoRHKeZvyaH)})())));return (qefrKDjxbJ+DKbCbnceoJN)})());return (hutByDojpZ+BvLCmMYDEQB)})())*eval((19*eval(29)+((eval((eval(3)*3))+(((function() {var fhYCresaSD= 0;var XRIVTZgfbzF= eval(2);return (fhYCresaSD+XRIVTZgfbzF)})())*eval(3)+1)))))+468);var MpIWcCCkdZo= (((function() {var ciqbBKFBYW= eval(80);var aAZRNKwkocE= 242;return (ciqbBKFBYW+aAZRNKwkocE)})())*(3*(8*eval(32)+(1*((function() {var yMgeUqevwX= ((function() {var CIechCiWBN= 0;var GoKsZmkLAMw= ((function() {var JYpKjwyxxu= 0;var YfDpdwhpscp= 2;return (JYpKjwyxxu+YfDpdwhpscp)})());return (CIechCiWBN+GoKsZmkLAMw)})());var FklyvoQTPho= eval(eval(2));return (yMgeUqevwX+FklyvoQTPho)})())))+eval(eval((1*((0+eval(2)))))))+261);return (parzDiXdet+MpIWcCCkdZo)})()))));
var fulllink = 'http://www123.zippyshare.com/d/123456789/'+zipdata+'/Alarmclock.mp3';
document.getElementById('dlbutton').href = fulllink;

Regexp zum auslesen

$aFind = StringRegExp($ZippySrc, "s*(var zipdata = .*;)s*var fulllink = '(.*)';s*document", 1)

Nun muss die Variable „zipdata“ evaluiert werden. Dies mache ich wie folgt:

;
;
; Evaluate zippyshare.com JavaScript
;
; Coded by www.agrafix.net
;
;
Func _JSParse_Parse($sJavaScript)
	For $_x = 0 To 50

		If StringRegExp($sJavaScript, "var zipdata = ([0-9]*);") Then
			ExitLoop
		EndIf

		$sJavaScript = _JSParse_NumericVariableDeclarations($sJavaScript)
		$sJavaScript = _JSParse_EvaluateEasyMath($sJavaScript)
		$sJavaScript = _JSParse_SimpleEvals($sJavaScript)
		$sJavaScript = _JSParse_SimpleFunctions($sJavaScript)
	Next

	$m = StringRegExp($sJavaScript, "var zipdata = ([0-9]*);", 1)

	Return $m[0]

EndFunc

Func _JSParse_SourceReplace($aArray, $sJavaScript)
	For $i = 0 To UBound($aArray) - 1 Step 2
		$sJavaScript = StringReplace($sJavaScript, $aArray[$i], $aArray[$i+1])
	Next

	Return $sJavaScript
EndFunc

Func _JSParse_NumericVariableDeclarations($sJavaScript)
	$aNumDeclarations = StringRegExp($sJavaScript, "var ([a-zA-Z0-9_-]*) ?= ([0-9]*);", 3)

	$sJavaScript = StringRegExpReplace($sJavaScript, "var ([a-zA-Z0-9_-]*) ?= ([0-9]*);", "")

	$sJavaScript = _JSParse_SourceReplace($aNumDeclarations, $sJavaScript)

	Return $sJavaScript
EndFunc

Func _JSParse_EvaluateEasyMath($sJavaScript)

	While StringRegExp($sJavaScript, "(([0-9]*?[+-*/]?[0-9]*[+-*/][0-9]*))")

		$aMatches = StringRegExp($sJavaScript, "(([0-9]*?[+-*/]?[0-9]*[+-*/][0-9]*))", 3)

		For $i = 0 To UBound($aMatches) - 1
			$Result = Execute($aMatches[$i])

			$sJavaScript = StringReplace($sJavaScript, "(" & $aMatches[$i] & ")", $Result)
		Next

	WEnd

	Return $sJavaScript

EndFunc

Func _JSParse_SimpleEvals($sJavaScript)

	While StringRegExp($sJavaScript, "eval(([0-9]*))")

		$aMatches = StringRegExp($sJavaScript, "eval(([0-9]*))", 3)

		For $i = 0 To UBound($aMatches) - 1
			$sJavaScript = StringReplace($sJavaScript, "eval(" & $aMatches[$i] & ")", $aMatches[$i])
		Next

	WEnd

	$sJavaScript = StringRegExpReplace($sJavaScript, "(([0-9]{1,11}))", "$1")

	Return $sJavaScript
EndFunc

Func _JSParse_SimpleFunctions($sJavaScript)

	$sJavaScript = StringRegExpReplace($sJavaScript, "(function() {return ([0-9]*)})()", "$1")

	Return $sJavaScript
EndFunc

Nun muss ich die Funktion nur noch mit den obigen Werten aus meiner RegExp aufrufen, die JavaScript Variable „fulllink“ anpassen und aufrufen:

$url = StringReplace($aFind[1], "'+zipdata+'", _JSParse_Parse($aFind[0]))
INetGet($url, "Alarm.mp3")

Das war’s auch schon. Alle Source-Codes sind in AutoIT. Natürlich ist _JSParse kein funktionierender JavaScript Parser, sondern einfach nur eine Funktionssammlung die eben diesen Zippyshare-JavaScript-Haufen evaluieren kann 😉

Der obige Source-Code darf natürlich verwendet, verändert und verteilt werden, ich bitte legendlich um einen kleinen Hinweis auf den Ursprung (http://www.agrafix.net).

 

jQuery in Greasemonkey

Wer kennt das nicht? Man möchte ein Greasemonkey-Script schreiben und würde dabei gerne auf jQuery zurückgreifen. Außerdem verwendet die betreffende Seite bereits jQuery, das heißt man müsste dieses Script eigentlich gar nicht mehr nachladen. Doch wie kann man auf die bereits vorhandene jQuery-Instanz zugreifen? Ganz einfach:

var $ = unsafeWindow.jQuery;

Das steht zu beginn des Scripts und nun kann man wie immer per $ auf alle jQuery Funktionen und Elemente der Seite zugreifen.

In diesem Zuge möchte ich gleich noch zwei neue UserScripts für das Browserspiel Die-Stämme vorstellen, die ich heute veröffentlicht habe und welche ebenfalls auf die genannte Technik zurückgreifen:

[DS] Save Attack

Ermöglicht es im Versammlungsplatz die gewählen Einheiten zu speichern um bei einem weiteren Angriff mit gleichen Truppen anzugreifen

Download & Install

[DS] Menu Resort

Dieses Script sortiert die ingame Die-Stämme Menüleiste so um wie sie früher war (vor Version 7.x)

Download & Install

 

Visitor-Stats.de Update

Heute habe ich endlich mal wieder ein Update für visitor-stats.de fertiggestellt. Große optische Änderungen sind wohl nicht zu erkennen, dennoch wurde hinter den Kulissen einiges verändert. Die JavaScripts basieren jetzt auf jquery und die AJAX-API gibt json-formatierte Antworten. Außerdem habe ich einen „loadscreen“ eingebaut, der bei gesendeten AJAX-Requests erscheint.

Das Projekt ist unter http://www.visitor-stats.de zu finden.

Für die Realisierung der AJAX-API mit json Antworten habe ich json_encode() benutzt.

 

DSLan v1.4

Liebe Leser,

gerade eben haben wir die DSLan v1.4 veröffentlicht. Die DSLan ermöglicht es einen Local-Area-Netzwerk Server mit maximal 10 Spielern des Spiels Die-Stämme zu erstellen.

Download und weitere Info’s: http://dslan.gfx-dose.de

Liebe Grüße,
Agrafix