libgda r3270 - in trunk: . doc/C doc/C/tmpl libgda providers/firebird providers/jdbc providers/mysql providers/postgres providers/skel-implementation/capi providers/sqlite tools tools/binreloc



Author: vivien
Date: Fri Dec 26 14:37:32 2008
New Revision: 3270
URL: http://svn.gnome.org/viewvc/libgda?rev=3270&view=rev

Log:
2008-12-26  Vivien Malerba <malerba gnome-db org>

	* libgda/gda-quark-list.c: remove leading and trailing white spaces in both
	key and value for each (<key>=<value>) pair (if a key or a value needs white
	spaces, they need to encode them as "%20")
	* tools/: improvements to the GDA SQL console's web server part:
	  - added authentication, based on a token (a string) the client must enter upon
	    request (then uses a cookie)
	  - added a terminal emulator to enter commands as one would do on a normal xterm
	  - reworked the CSS
	  - added binreloc feature
	* libgda/gda-data-model.c: applied patch to correct a bug in
	gda_data_model_get_typed_value_at(), thanks to Tim Lapawa
	* providers/skel-implementation/capi/capi_specs_create_table.xml.in:
	* providers/firebird/firebird_specs_create_table.xml.in:
	* providers/sqlite/sqlite_specs_create_table.xml.in:
	* providers/jdbc/jdbc_specs_create_table.xml.in:
	* providers/mysql/mysql_specs_create_table.xml.in
	* providers/postgres/postgres_specs_create_table.xml.in: don't define any column
	when creating a GdaServerOperation for table creation, fixes bug #565618
	* doc/C:
	* libgda/gda-connection.c: doc. update


Added:
   trunk/tools/binreloc/   (props changed)
   trunk/tools/binreloc/Makefile.am
   trunk/tools/binreloc/binreloc.c
   trunk/tools/binreloc/binreloc.h
   trunk/tools/binreloc/sql-binreloc.c
   trunk/tools/binreloc/sql-binreloc.h
   trunk/tools/cnc.js
   trunk/tools/gda-print.css
   trunk/tools/gda.css
   trunk/tools/irb.css
   trunk/tools/irb.js
   trunk/tools/jquery.js
   trunk/tools/md5.js
   trunk/tools/mouseapp_2.js
   trunk/tools/mouseirb_2.js
Modified:
   trunk/ChangeLog
   trunk/configure.in
   trunk/doc/C/gettingstarted.xml
   trunk/doc/C/libgda-4.0-sections.txt
   trunk/doc/C/tmpl/gda-data-model-iter.sgml
   trunk/doc/C/tmpl/gda-server-provider.sgml
   trunk/libgda/gda-connection.c
   trunk/libgda/gda-data-model.c
   trunk/libgda/gda-quark-list.c
   trunk/providers/firebird/firebird_specs_create_table.xml.in
   trunk/providers/jdbc/jdbc_specs_create_table.xml.in
   trunk/providers/mysql/mysql_specs_create_table.xml.in
   trunk/providers/postgres/postgres_specs_create_table.xml.in
   trunk/providers/skel-implementation/capi/capi_specs_create_table.xml.in
   trunk/providers/sqlite/sqlite_specs_create_table.xml.in
   trunk/tools/Makefile.am
   trunk/tools/command-exec.c
   trunk/tools/command-exec.h
   trunk/tools/gda-sql.c
   trunk/tools/gda-sql.h
   trunk/tools/html-doc.c
   trunk/tools/html-doc.h
   trunk/tools/web-server.c
   trunk/tools/web-server.h

Modified: trunk/configure.in
==============================================================================
--- trunk/configure.in	(original)
+++ trunk/configure.in	Fri Dec 26 14:37:32 2008
@@ -1490,6 +1490,7 @@
 libgda-report/RML/trml2pdf/Makefile
 libgda-xslt/Makefile
 tools/Makefile
+tools/binreloc/Makefile
 testing/Makefile
 tests/Makefile
 tests/parser/Makefile

Modified: trunk/doc/C/gettingstarted.xml
==============================================================================
--- trunk/doc/C/gettingstarted.xml	(original)
+++ trunk/doc/C/gettingstarted.xml	Fri Dec 26 14:37:32 2008
@@ -374,7 +374,7 @@
 	/* Set parameter's values */
 	/* table name */
 	if (!gda_server_operation_set_value_at (op, "products", &error, "/TABLE_DEF_P/TABLE_NAME")) goto on_set_error;
-        if (!gda_server_operation_set_value_at (op, "InnoDB", &error, "/TABLE_OPTIONS_P/TABLE_ENGINE")) goto on_set_error;
+	if (!gda_server_operation_set_value_at (op, "InnoDB", &error, "/TABLE_OPTIONS_P/TABLE_ENGINE")) goto on_set_error;
 
 	/* "id' field */
 	i = 0;

Modified: trunk/doc/C/libgda-4.0-sections.txt
==============================================================================
--- trunk/doc/C/libgda-4.0-sections.txt	(original)
+++ trunk/doc/C/libgda-4.0-sections.txt	Fri Dec 26 14:37:32 2008
@@ -358,7 +358,7 @@
 gda_data_model_iter_get_value_for_field
 gda_data_model_iter_set_value_at
 gda_data_model_iter_is_valid
-gda_data_model_iter_move_at_row
+gda_data_model_iter_move_to_row
 gda_data_model_iter_move_next
 gda_data_model_iter_move_prev
 gda_data_model_iter_get_row

Modified: trunk/doc/C/tmpl/gda-data-model-iter.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-data-model-iter.sgml	(original)
+++ trunk/doc/C/tmpl/gda-data-model-iter.sgml	Fri Dec 26 14:37:32 2008
@@ -138,7 +138,7 @@
 @Returns: 
 
 
-<!-- ##### FUNCTION gda_data_model_iter_move_at_row ##### -->
+<!-- ##### FUNCTION gda_data_model_iter_move_to_row ##### -->
 <para>
 
 </para>

Modified: trunk/doc/C/tmpl/gda-server-provider.sgml
==============================================================================
--- trunk/doc/C/tmpl/gda-server-provider.sgml	(original)
+++ trunk/doc/C/tmpl/gda-server-provider.sgml	Fri Dec 26 14:37:32 2008
@@ -293,7 +293,7 @@
 @provider: 
 @cnc: 
 @string: 
- prefered_type: 
+ preferred_type: 
 @dbms_type: 
 @Returns: 
 

Modified: trunk/libgda/gda-connection.c
==============================================================================
--- trunk/libgda/gda-connection.c	(original)
+++ trunk/libgda/gda-connection.c	Fri Dec 26 14:37:32 2008
@@ -3233,7 +3233,7 @@
  *
  * Note: it's up to the caller to make sure the information contained within @cnc's associated #GdaMetaStore
  * is up to date using gda_connection_update_meta_store() (it can become outdated if the database's schema
- * is accessed from outside of Libgda).
+ * is modified).
  *
  * For more information about the returned data model's attributes, or about the @meta_type and @... filter arguments,
  * see <link linkend="GdaConnectionMetaTypeHead">this description</link>.

Modified: trunk/libgda/gda-data-model.c
==============================================================================
--- trunk/libgda/gda-data-model.c	(original)
+++ trunk/libgda/gda-data-model.c	Fri Dec 26 14:37:32 2008
@@ -602,20 +602,23 @@
 		cvalue = (GDA_DATA_MODEL_GET_CLASS (model)->i_get_value_at) (model, col, row, error);
 
 	if (cvalue) {
-		if (nullok && (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL) && (G_VALUE_TYPE (cvalue) != expected_type)) {
-			cvalue = NULL;
+		if (nullok && 
+		    (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL) && 
+		    (G_VALUE_TYPE (cvalue) != expected_type)) {
 			g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_VALUE_TYPE_ERROR,
-				     _("Data model returned value of invalid '%s' type"), gda_g_type_to_string (G_VALUE_TYPE (cvalue)));
+				     _("Data model returned value of invalid '%s' type"), 
+				     gda_g_type_to_string (G_VALUE_TYPE (cvalue)));
+			cvalue = NULL;
 		}
 		else if (!nullok && (G_VALUE_TYPE (cvalue) != expected_type)) {
-			cvalue = NULL;
-			if (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL)
+			if (G_VALUE_TYPE (cvalue) == GDA_TYPE_NULL)
 				g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_VALUE_TYPE_ERROR,
 					      "%s", _("Data model returned invalid NULL value"));
 			else
 				g_set_error (error, GDA_DATA_MODEL_ERROR, GDA_DATA_MODEL_VALUE_TYPE_ERROR,
 					     _("Data model returned value of invalid '%s' type"), 
 					     gda_g_type_to_string (G_VALUE_TYPE (cvalue)));
+			cvalue = NULL;
 		}
 	}
 	return cvalue;

Modified: trunk/libgda/gda-quark-list.c
==============================================================================
--- trunk/libgda/gda-quark-list.c	(original)
+++ trunk/libgda/gda-quark-list.c	Fri Dec 26 14:37:32 2008
@@ -176,10 +176,11 @@
 	gchar **arr;
 
 	g_return_if_fail (qlist != NULL);
-	g_return_if_fail (string != NULL);
+	if (!string || !*string)
+		return;
 
-	if (cleanup != FALSE)
-		gda_quark_list_clear(qlist);
+	if (cleanup)
+		gda_quark_list_clear (qlist);
 
 	arr = (gchar **) g_strsplit (string, ";", 0);
 	if (arr) {
@@ -192,7 +193,9 @@
 			if (pair && pair[0]) {
 				gchar *name = pair[0];
 				gchar *value = pair[1];
+				g_strstrip (name);
 				gda_rfc1738_decode (name);
+				g_strstrip (value);
 				gda_rfc1738_decode (value);
 				g_hash_table_insert (qlist->hash_table, 
 						     (gpointer) name, (gpointer) value);
@@ -238,9 +241,6 @@
 void
 gda_quark_list_remove (GdaQuarkList *qlist, const gchar *name)
 {
-	gpointer orig_key;
-	gpointer orig_value;
-
 	g_return_if_fail (qlist != NULL);
 	g_return_if_fail (name != NULL);
 

Modified: trunk/providers/firebird/firebird_specs_create_table.xml.in
==============================================================================
--- trunk/providers/firebird/firebird_specs_create_table.xml.in	(original)
+++ trunk/providers/firebird/firebird_specs_create_table.xml.in	Fri Dec 26 14:37:32 2008
@@ -15,17 +15,6 @@
     <gda_array_field id="COLUMN_PKEY" _name="Primary key" gdatype="gboolean"/>
     <gda_array_field id="COLUMN_DEFAULT" _name="Default" _descr="Default value" gdatype="gchararray"/>
     <gda_array_field id="COLUMN_CHECK" _name="Check" _descr="Check constraint" gdatype="gchararray"/>
-    <gda_array_data>
-        <gda_array_row>
-            <gda_value>id</gda_value>
-            <gda_value>integer</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value></gda_value>
-            <gda_value></gda_value>
-        </gda_array_row>
-    </gda_array_data>
   </gda_array>
 
   <!-- other table constraints -->

Modified: trunk/providers/jdbc/jdbc_specs_create_table.xml.in
==============================================================================
--- trunk/providers/jdbc/jdbc_specs_create_table.xml.in	(original)
+++ trunk/providers/jdbc/jdbc_specs_create_table.xml.in	Fri Dec 26 14:37:32 2008
@@ -15,17 +15,6 @@
     <gda_array_field id="COLUMN_PKEY" _name="Primary key" gdatype="gboolean"/>
     <gda_array_field id="COLUMN_DEFAULT" _name="Default" _descr="Default value" gdatype="gchararray"/>
     <gda_array_field id="COLUMN_CHECK" _name="Check" _descr="Check constraint" gdatype="gchararray"/>
-    <gda_array_data>
-        <gda_array_row>
-            <gda_value>id</gda_value>
-            <gda_value>integer</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value></gda_value>
-            <gda_value></gda_value>
-        </gda_array_row>
-    </gda_array_data>
   </gda_array>
 
   <!-- other table constraints -->

Modified: trunk/providers/mysql/mysql_specs_create_table.xml.in
==============================================================================
--- trunk/providers/mysql/mysql_specs_create_table.xml.in	(original)
+++ trunk/providers/mysql/mysql_specs_create_table.xml.in	Fri Dec 26 14:37:32 2008
@@ -26,21 +26,6 @@
     <gda_array_field id="COLUMN_DEFAULT" _name="Default" _descr="Default value" gdatype="gchararray"/>
     <gda_array_field id="COLUMN_CHECK" _name="Check" _descr="Check constraint" gdatype="gchararray"/>
     <gda_array_field id="COLUMN_COMMENT" _name="Comment" _descr="Check constraint" gdatype="gchararray"/>
-    <gda_array_data>
-        <gda_array_row>
-            <gda_value>id</gda_value>
-            <gda_value>int</gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value>FALSE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>FALSE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value>Primary key</gda_value>
-        </gda_array_row>
-    </gda_array_data>
   </gda_array>
 
   <!-- foreign key spec -->

Modified: trunk/providers/postgres/postgres_specs_create_table.xml.in
==============================================================================
--- trunk/providers/postgres/postgres_specs_create_table.xml.in	(original)
+++ trunk/providers/postgres/postgres_specs_create_table.xml.in	Fri Dec 26 14:37:32 2008
@@ -38,20 +38,6 @@
     <gda_array_field id="COLUMN_PKEY" _name="Primary key" gdatype="gboolean"/>
     <gda_array_field id="COLUMN_DEFAULT" _name="Default" _descr="Default value" gdatype="gchararray"/>
     <gda_array_field id="COLUMN_CHECK" _name="Check" _descr="Check constraint" gdatype="gchararray"/>
-    <gda_array_data>
-        <gda_array_row>
-            <gda_value>id</gda_value>
-            <gda_value>serial</gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value>TRUE</gda_value>
-	    <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value></gda_value>
-            <gda_value></gda_value>
-        </gda_array_row>
-    </gda_array_data>
   </gda_array>
 
   <!-- foreign key spec -->

Modified: trunk/providers/skel-implementation/capi/capi_specs_create_table.xml.in
==============================================================================
--- trunk/providers/skel-implementation/capi/capi_specs_create_table.xml.in	(original)
+++ trunk/providers/skel-implementation/capi/capi_specs_create_table.xml.in	Fri Dec 26 14:37:32 2008
@@ -15,17 +15,6 @@
     <gda_array_field id="COLUMN_PKEY" _name="Primary key" gdatype="gboolean"/>
     <gda_array_field id="COLUMN_DEFAULT" _name="Default" _descr="Default value" gdatype="gchararray"/>
     <gda_array_field id="COLUMN_CHECK" _name="Check" _descr="Check constraint" gdatype="gchararray"/>
-    <gda_array_data>
-        <gda_array_row>
-            <gda_value>id</gda_value>
-            <gda_value>integer</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value></gda_value>
-            <gda_value></gda_value>
-        </gda_array_row>
-    </gda_array_data>
   </gda_array>
 
   <!-- other table constraints -->

Modified: trunk/providers/sqlite/sqlite_specs_create_table.xml.in
==============================================================================
--- trunk/providers/sqlite/sqlite_specs_create_table.xml.in	(original)
+++ trunk/providers/sqlite/sqlite_specs_create_table.xml.in	Fri Dec 26 14:37:32 2008
@@ -27,20 +27,6 @@
     <!-- To translators: "Compare method" refers to the method SQLite has to compare values -->
     <gda_array_field id="COLUMN_COLLATE" _name="Compare method" _descr="Collation name (BINARY|NOCASE)" gdatype="gchararray"/>
     <gda_array_field id="COLUMN_CONFLICT" _name="Conflict" _descr="Conflict resolution method (ROLLBACK|ABORT|FAIL|IGNORE|REPLACE" gdatype="gchararray"/>
-    <gda_array_data>
-        <gda_array_row>
-            <gda_value>id</gda_value>
-            <gda_value>integer</gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value>FALSE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value>FALSE</gda_value>
-            <gda_value>TRUE</gda_value>
-            <gda_value isnull="t"></gda_value>
-            <gda_value isnull="t"></gda_value>
-        </gda_array_row>
-    </gda_array_data>
   </gda_array>
 
   <!-- foreign key spec: not supported by SQLite -->

Modified: trunk/tools/Makefile.am
==============================================================================
--- trunk/tools/Makefile.am	(original)
+++ trunk/tools/Makefile.am	Fri Dec 26 14:37:32 2008
@@ -1,3 +1,5 @@
+SUBDIRS = binreloc
+
 bin_PROGRAMS = \
 	gda-list-config-4.0 \
 	gda-sql-4.0 \
@@ -27,10 +29,14 @@
 	tools-input.h \
 	tools-input.c \
 	command-exec.h \
-	command-exec.c 
+	command-exec.c \
+	$(top_srcdir)/libgda/global.h \
+	$(top_srcdir)/libgda/md5.h \
+	$(top_srcdir)/libgda/md5c.c
 
 gda_sql_4_0_LDADD = \
         $(top_builddir)/libgda/libgda-4.0.la \
+	binreloc/libgdasql_binreloc-4.0.la \
         $(LIBGDA_LIBS) \
 	$(READLINE_LIB) \
         $(HISTORY_LIB)
@@ -70,4 +76,16 @@
         $(top_builddir)/libgda/libgda-4.0.la \
         $(LIBGDA_LIBS)
 
+webdatadir = $(datadir)/libgda-4.0/web
+webdata_DATA = \
+	cnc.js \
+	md5.js \
+	jquery.js \
+	mouseapp_2.js \
+	mouseirb_2.js \
+	irb.js \
+	gda.css \
+	gda-print.css \
+	irb.css
+
 EXTRA_DIST = gda-sql.ico

Added: trunk/tools/binreloc/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/tools/binreloc/Makefile.am	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,22 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir) \
+	-I$(top_builddir) \
+	$(LIBGDA_CFLAGS) \
+	$(BINRELOC_CFLAGS) \
+	-DLIBGDAPREFIX=\""$(prefix)"\" \
+	-DLIBGDADATA=\""$(datadir)"\" \
+	-DLIBGDALIB=\""$(libdir)"\" \
+	-DLIBGDALIBEXEC=\""$(libexecdir)"\" \
+	-DLIBGDABIN=\""$(bindir)"\" \
+	-DLIBGDASBIN=\""$(sbindir)"\" \
+	-DLIBGDASYSCONF=\""$(sysconfdir)"\"
+
+noinst_LTLIBRARIES = libgdasql_binreloc-4.0.la
+
+libgdasql_binreloc_4_0_la_SOURCES = \
+	sql-binreloc.h \
+	sql-binreloc.c
+
+EXTRA_DIST = binreloc.c binreloc.h

Added: trunk/tools/binreloc/binreloc.c
==============================================================================
--- (empty file)
+++ trunk/tools/binreloc/binreloc.c	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,696 @@
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h lai chello nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+#ifndef __BINRELOC_C__
+#define __BINRELOC_C__
+
+#ifdef ENABLE_BINRELOC
+	#include <sys/types.h>
+	#include <sys/stat.h>
+	#include <unistd.h>
+#endif /* ENABLE_BINRELOC */
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+#include "binreloc.h"
+
+G_BEGIN_DECLS
+
+
+/** @internal
+ * Find the canonical filename of the executable. Returns the filename
+ * (which must be freed) or NULL on error. If the parameter 'error' is
+ * not NULL, the error code will be stored there, if an error occurred.
+ */
+static char *
+_br_find_exe (GbrInitError *error)
+{
+#ifndef ENABLE_BINRELOC
+	if (error)
+		*error = _SQL_GBR_INIT_ERROR_DISABLED;
+	return NULL;
+#else
+	char *path, *path2, *line, *result;
+	size_t buf_size;
+	ssize_t size;
+	struct stat stat_buf;
+	FILE *f;
+
+	/* Read from /proc/self/exe (symlink) */
+	if (sizeof (path) > SSIZE_MAX)
+		buf_size = SSIZE_MAX - 1;
+	else
+		buf_size = PATH_MAX - 1;
+	path = (char *) g_try_malloc (buf_size);
+	if (path == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = _SQL_GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+	path2 = (char *) g_try_malloc (buf_size);
+	if (path2 == NULL) {
+		/* Cannot allocate memory. */
+		if (error)
+			*error = _SQL_GBR_INIT_ERROR_NOMEM;
+		g_free (path);
+		return NULL;
+	}
+
+	strncpy (path2, "/proc/self/exe", buf_size - 1);
+
+	while (1) {
+		int i;
+
+		size = readlink (path2, path, buf_size - 1);
+		if (size == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* readlink() success. */
+		path[size] = '\0';
+
+		/* Check whether the symlink's target is also a symlink.
+		 * We want to get the final target. */
+		i = stat (path, &stat_buf);
+		if (i == -1) {
+			/* Error. */
+			g_free (path2);
+			break;
+		}
+
+		/* stat() success. */
+		if (!S_ISLNK (stat_buf.st_mode)) {
+			/* path is not a symlink. Done. */
+			g_free (path2);
+			return path;
+		}
+
+		/* path is a symlink. Continue loop and resolve this. */
+		strncpy (path, path2, buf_size - 1);
+	}
+
+
+	/* readlink() or stat() failed; this can happen when the program is
+	 * running in Valgrind 2.2. Read from /proc/self/maps as fallback. */
+
+	buf_size = PATH_MAX + 128;
+	line = (char *) g_try_realloc (path, buf_size);
+	if (line == NULL) {
+		/* Cannot allocate memory. */
+		g_free (path);
+		if (error)
+			*error = _SQL_GBR_INIT_ERROR_NOMEM;
+		return NULL;
+	}
+
+	f = fopen ("/proc/self/maps", "r");
+	if (f == NULL) {
+		g_free (line);
+		if (error)
+			*error = _SQL_GBR_INIT_ERROR_OPEN_MAPS;
+		return NULL;
+	}
+
+	/* The first entry should be the executable name. */
+	result = fgets (line, (int) buf_size, f);
+	if (result == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = _SQL_GBR_INIT_ERROR_READ_MAPS;
+		return NULL;
+	}
+
+	/* Get rid of newline character. */
+	buf_size = strlen (line);
+	if (buf_size <= 0) {
+		/* Huh? An empty string? */
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = _SQL_GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+	if (line[buf_size - 1] == 10)
+		line[buf_size - 1] = 0;
+
+	/* Extract the filename; it is always an absolute path. */
+	path = strchr (line, '/');
+
+	/* Sanity check. */
+	if (strstr (line, " r-xp ") == NULL || path == NULL) {
+		fclose (f);
+		g_free (line);
+		if (error)
+			*error = _SQL_GBR_INIT_ERROR_INVALID_MAPS;
+		return NULL;
+	}
+
+	path = g_strdup (path);
+	g_free (line);
+	fclose (f);
+	return path;
+#endif /* ENABLE_BINRELOC */
+}
+
+
+/** @internal
+ * Find the canonical filename of the executable which owns symbol.
+ * Returns a filename which must be freed, or NULL on error.
+ */
+static char *
+_br_find_exe_for_symbol (const void *symbol, GbrInitError *error)
+{
+#ifndef ENABLE_BINRELOC
+	if (error)
+		*error = _SQL_GBR_INIT_ERROR_DISABLED;
+	return (char *) NULL;
+#else
+	#define SIZE PATH_MAX + 100
+	FILE *f;
+	size_t address_string_len;
+	char *address_string, line[SIZE], *found;
+
+	if (symbol == NULL)
+		return (char *) NULL;
+
+	f = fopen ("/proc/self/maps", "r");
+	if (f == NULL)
+		return (char *) NULL;
+
+	address_string_len = 4;
+	address_string = (char *) g_try_malloc (address_string_len);
+	found = (char *) NULL;
+
+	while (!feof (f)) {
+		char *start_addr, *end_addr, *end_addr_end, *file;
+		void *start_addr_p, *end_addr_p;
+		size_t len;
+
+		if (fgets (line, SIZE, f) == NULL)
+			break;
+
+		/* Sanity check. */
+		if (strstr (line, " r-xp ") == NULL || strchr (line, '/') == NULL)
+			continue;
+
+		/* Parse line. */
+		start_addr = line;
+		end_addr = strchr (line, '-');
+		file = strchr (line, '/');
+
+		/* More sanity check. */
+		if (!(file > end_addr && end_addr != NULL && end_addr[0] == '-'))
+			continue;
+
+		end_addr[0] = '\0';
+		end_addr++;
+		end_addr_end = strchr (end_addr, ' ');
+		if (end_addr_end == NULL)
+			continue;
+
+		end_addr_end[0] = '\0';
+		len = strlen (file);
+		if (len == 0)
+			continue;
+		if (file[len - 1] == '\n')
+			file[len - 1] = '\0';
+
+		/* Get rid of "(deleted)" from the filename. */
+		len = strlen (file);
+		if (len > 10 && strcmp (file + len - 10, " (deleted)") == 0)
+			file[len - 10] = '\0';
+
+		/* I don't know whether this can happen but better safe than sorry. */
+		len = strlen (start_addr);
+		if (len != strlen (end_addr))
+			continue;
+
+
+		/* Transform the addresses into a string in the form of 0xdeadbeef,
+		 * then transform that into a pointer. */
+		if (address_string_len < len + 3) {
+			address_string_len = len + 3;
+			address_string = (char *) g_try_realloc (address_string, address_string_len);
+		}
+
+		memcpy (address_string, "0x", 2);
+		memcpy (address_string + 2, start_addr, len);
+		address_string[2 + len] = '\0';
+		sscanf (address_string, "%p", &start_addr_p);
+
+		memcpy (address_string, "0x", 2);
+		memcpy (address_string + 2, end_addr, len);
+		address_string[2 + len] = '\0';
+		sscanf (address_string, "%p", &end_addr_p);
+
+
+		if (symbol >= start_addr_p && symbol < end_addr_p) {
+			found = file;
+			break;
+		}
+	}
+
+	g_free (address_string);
+	fclose (f);
+
+	if (found == NULL)
+		return (char *) NULL;
+	else
+		return g_strdup (found);
+#endif /* ENABLE_BINRELOC */
+}
+
+
+static gchar *exe = NULL;
+
+static void set_gerror (GError **error, GbrInitError errcode);
+
+
+/** Initialize the BinReloc library (for applications).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the application's canonical filename.
+ *
+ * @note If you want to use BinReloc for a library, then you should call
+ *       _sql_gbr_init_lib() instead.
+ *
+ * @param error  If BinReloc failed to initialize, then the error report will
+ *               be stored in this variable. Set to NULL if you don't want an
+ *               error report. See the #GbrInitError for a list of error
+ *               codes.
+ *
+ * @returns TRUE on success, FALSE if BinReloc failed to initialize.
+ */
+gboolean
+_sql_gbr_init (GError **error)
+{
+	GbrInitError errcode;
+
+	/* Locate the application's filename. */
+	exe = _br_find_exe (&errcode);
+	if (exe != NULL)
+		/* Success! */
+		return TRUE;
+	else {
+		/* Failed :-( */
+		set_gerror (error, errcode);
+		return FALSE;
+	}
+}
+
+
+/** Initialize the BinReloc library (for libraries).
+ *
+ * This function must be called before using any other BinReloc functions.
+ * It attempts to locate the calling library's canonical filename.
+ *
+ * @note The BinReloc source code MUST be included in your library, or this
+ *       function won't work correctly.
+ *
+ * @returns TRUE on success, FALSE if a filename cannot be found.
+ */
+gboolean
+_sql_gbr_init_lib (GError **error)
+{
+	GbrInitError errcode;
+
+	exe = _br_find_exe_for_symbol ((const void *) "", &errcode);
+	if (exe != NULL)
+		/* Success! */
+		return TRUE;
+	else {
+		/* Failed :-( */
+		set_gerror (error, errcode);
+		return exe != NULL;
+	}
+}
+
+
+static void
+set_gerror (GError **error, GbrInitError errcode)
+{
+	gchar *error_message;
+
+	if (error == NULL)
+		return;
+
+	switch (errcode) {
+	case _SQL_GBR_INIT_ERROR_NOMEM:
+		error_message = "Cannot allocate memory.";
+		break;
+	case _SQL_GBR_INIT_ERROR_OPEN_MAPS:
+		error_message = "Unable to open /proc/self/maps for reading.";
+		break;
+	case _SQL_GBR_INIT_ERROR_READ_MAPS:
+		error_message = "Unable to read from /proc/self/maps.";
+		break;
+	case _SQL_GBR_INIT_ERROR_INVALID_MAPS:
+		error_message = "The file format of /proc/self/maps is invalid.";
+		break;
+	case _SQL_GBR_INIT_ERROR_DISABLED:
+		error_message = "Binary relocation support is disabled.";
+		break;
+	default:
+		error_message = "Unknown error.";
+		break;
+	};
+	g_set_error (error, g_quark_from_static_string ("GBinReloc"),
+		     errcode, "%s", error_message);
+}
+
+
+/** Find the canonical filename of the current application.
+ *
+ * @param default_exe  A default filename which will be used as fallback.
+ * @returns A string containing the application's canonical filename,
+ *          which must be freed when no longer necessary. If BinReloc is
+ *          not initialized, or if the initialization function failed,
+ *          then a copy of default_exe will be returned. If default_exe
+ *          is NULL, then NULL will be returned.
+ */
+gchar *
+_sql_gbr_find_exe (const gchar *default_exe)
+{
+	if (exe == NULL) {
+		/* BinReloc is not initialized. */
+		if (default_exe != NULL)
+			return g_strdup (default_exe);
+		else
+			return NULL;
+	}
+	return g_strdup (exe);
+}
+
+
+/** Locate the directory in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(exename)
+ * \endcode
+ *
+ * @param default_dir  A default directory which will used as fallback.
+ * @return A string containing the directory, which must be freed when no
+ *         longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_dir
+ *         will be returned. If default_dir is NULL, then NULL will be
+ *         returned.
+ */
+gchar *
+_sql_gbr_find_exe_dir (const gchar *default_dir)
+{
+	if (exe == NULL) {
+		/* BinReloc not initialized. */
+		if (default_dir != NULL)
+			return g_strdup (default_dir);
+		else
+			return NULL;
+	}
+
+	return g_path_get_dirname (exe);
+}
+
+
+/** Locate the prefix in which the current application is installed.
+ *
+ * The prefix is generated by the following pseudo-code evaluation:
+ * \code
+ * dirname(dirname(exename))
+ * \endcode
+ *
+ * @param default_prefix  A default prefix which will used as fallback.
+ * @return A string containing the prefix, which must be freed when no
+ *         longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_prefix
+ *         will be returned. If default_prefix is NULL, then NULL will be
+ *         returned.
+ */
+gchar *
+_sql_gbr_find_prefix (const gchar *default_prefix)
+{
+	gchar *dir1, *dir2;
+
+	if (exe == NULL) {
+		/* BinReloc not initialized. */
+		if (default_prefix != NULL)
+			return g_strdup (default_prefix);
+		else
+			return NULL;
+	}
+
+	dir1 = g_path_get_dirname (exe);
+	dir2 = g_path_get_dirname (dir1);
+	g_free (dir1);
+	return dir2;
+}
+
+
+/** Locate the application's binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/bin"
+ * \endcode
+ *
+ * @param default_bin_dir  A default path which will used as fallback.
+ * @return A string containing the bin folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_bin_dir will
+ *         be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+gchar *
+_sql_gbr_find_bin_dir (const gchar *default_bin_dir)
+{
+	gchar *prefix, *dir;
+
+	prefix = _sql_gbr_find_prefix (NULL);
+	if (prefix == NULL) {
+		/* BinReloc not initialized. */
+		if (default_bin_dir != NULL)
+			return g_strdup (default_bin_dir);
+		else
+			return NULL;
+	}
+
+	dir = g_build_filename (prefix, "bin", NULL);
+	g_free (prefix);
+	return dir;
+}
+
+
+/** Locate the application's superuser binary folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/sbin"
+ * \endcode
+ *
+ * @param default_sbin_dir  A default path which will used as fallback.
+ * @return A string containing the sbin folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_sbin_dir will
+ *         be returned. If default_bin_dir is NULL, then NULL will be returned.
+ */
+gchar *
+_sql_gbr_find_sbin_dir (const gchar *default_sbin_dir)
+{
+	gchar *prefix, *dir;
+
+	prefix = _sql_gbr_find_prefix (NULL);
+	if (prefix == NULL) {
+		/* BinReloc not initialized. */
+		if (default_sbin_dir != NULL)
+			return g_strdup (default_sbin_dir);
+		else
+			return NULL;
+	}
+
+	dir = g_build_filename (prefix, "sbin", NULL);
+	g_free (prefix);
+	return dir;
+}
+
+
+/** Locate the application's data folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share"
+ * \endcode
+ *
+ * @param default_data_dir  A default path which will used as fallback.
+ * @return A string containing the data folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_data_dir
+ *         will be returned. If default_data_dir is NULL, then NULL will be
+ *         returned.
+ */
+gchar *
+_sql_gbr_find_data_dir (const gchar *default_data_dir)
+{
+	gchar *prefix, *dir;
+
+	prefix = _sql_gbr_find_prefix (NULL);
+	if (prefix == NULL) {
+		/* BinReloc not initialized. */
+		if (default_data_dir != NULL)
+			return g_strdup (default_data_dir);
+		else
+			return NULL;
+	}
+
+	dir = g_build_filename (prefix, "share", NULL);
+	g_free (prefix);
+	return dir;
+}
+
+
+/** Locate the application's localization folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/share/locale"
+ * \endcode
+ *
+ * @param default_locale_dir  A default path which will used as fallback.
+ * @return A string containing the localization folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_locale_dir will be returned.
+ *         If default_locale_dir is NULL, then NULL will be returned.
+ */
+gchar *
+_sql_gbr_find_locale_dir (const gchar *default_locale_dir)
+{
+	gchar *data_dir, *dir;
+
+	data_dir = _sql_gbr_find_data_dir (NULL);
+	if (data_dir == NULL) {
+		/* BinReloc not initialized. */
+		if (default_locale_dir != NULL)
+			return g_strdup (default_locale_dir);
+		else
+			return NULL;
+	}
+
+	dir = g_build_filename (data_dir, "locale", NULL);
+	g_free (data_dir);
+	return dir;
+}
+
+
+/** Locate the application's library folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/lib"
+ * \endcode
+ *
+ * @param default_lib_dir  A default path which will used as fallback.
+ * @return A string containing the library folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the
+ *         initialization function failed, then a copy of default_lib_dir will be returned.
+ *         If default_lib_dir is NULL, then NULL will be returned.
+ */
+gchar *
+_sql_gbr_find_lib_dir (const gchar *default_lib_dir)
+{
+	gchar *prefix, *dir;
+
+	prefix = _sql_gbr_find_prefix (NULL);
+	if (prefix == NULL) {
+		/* BinReloc not initialized. */
+		if (default_lib_dir != NULL)
+			return g_strdup (default_lib_dir);
+		else
+			return NULL;
+	}
+
+	dir = g_build_filename (prefix, "lib", NULL);
+	g_free (prefix);
+	return dir;
+}
+
+
+/** Locate the application's libexec folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/libexec"
+ * \endcode
+ *
+ * @param default_libexec_dir  A default path which will used as fallback.
+ * @return A string containing the libexec folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the initialization
+ *         function failed, then a copy of default_libexec_dir will be returned.
+ *         If default_libexec_dir is NULL, then NULL will be returned.
+ */
+gchar *
+_sql_gbr_find_libexec_dir (const gchar *default_libexec_dir)
+{
+	gchar *prefix, *dir;
+
+	prefix = _sql_gbr_find_prefix (NULL);
+	if (prefix == NULL) {
+		/* BinReloc not initialized. */
+		if (default_libexec_dir != NULL)
+			return g_strdup (default_libexec_dir);
+		else
+			return NULL;
+	}
+
+	dir = g_build_filename (prefix, "libexec", NULL);
+	g_free (prefix);
+	return dir;
+}
+
+
+/** Locate the application's configuration files folder.
+ *
+ * The path is generated by the following pseudo-code evaluation:
+ * \code
+ * prefix + "/etc"
+ * \endcode
+ *
+ * @param default_etc_dir  A default path which will used as fallback.
+ * @return A string containing the etc folder's path, which must be freed when
+ *         no longer necessary. If BinReloc is not initialized, or if the initialization
+ *         function failed, then a copy of default_etc_dir will be returned.
+ *         If default_etc_dir is NULL, then NULL will be returned.
+ */
+gchar *
+_sql_gbr_find_etc_dir (const gchar *default_etc_dir)
+{
+	gchar *prefix, *dir;
+
+	prefix = _sql_gbr_find_prefix (NULL);
+	if (prefix == NULL) {
+		/* BinReloc not initialized. */
+		if (default_etc_dir != NULL)
+			return g_strdup (default_etc_dir);
+		else
+			return NULL;
+	}
+
+	dir = g_build_filename (prefix, "etc", NULL);
+	g_free (prefix);
+	return dir;
+}
+
+
+G_END_DECLS
+
+#endif /* __BINRELOC_C__ */

Added: trunk/tools/binreloc/binreloc.h
==============================================================================
--- (empty file)
+++ trunk/tools/binreloc/binreloc.h	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,73 @@
+/*
+ * BinReloc - a library for creating relocatable executables
+ * Written by: Hongli Lai <h lai chello nl>
+ * http://autopackage.org/
+ *
+ * This source code is public domain. You can relicense this code
+ * under whatever license you want.
+ *
+ * See http://autopackage.org/docs/binreloc/ for
+ * more information and how to use this.
+ */
+
+/*
+ * To avoid name clashes with other libraries which might also use BinReloc, all the API
+ * has been prefixed with _gda
+ */
+
+#ifndef __BINRELOC_H__
+#define __BINRELOC_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+
+/** These error codes can be returned by br_init(), br_init_lib(), gbr_init() or gbr_init_lib(). */
+typedef enum {
+	/** Cannot allocate memory. */
+	_SQL_GBR_INIT_ERROR_NOMEM,
+	/** Unable to open /proc/self/maps; see errno for details. */
+	_SQL_GBR_INIT_ERROR_OPEN_MAPS,
+	/** Unable to read from /proc/self/maps; see errno for details. */
+	_SQL_GBR_INIT_ERROR_READ_MAPS,
+	/** The file format of /proc/self/maps is invalid; kernel bug? */
+	_SQL_GBR_INIT_ERROR_INVALID_MAPS,
+	/** BinReloc is disabled (the ENABLE_BINRELOC macro is not defined). */
+	_SQL_GBR_INIT_ERROR_DISABLED
+} GbrInitError;
+
+
+#ifndef BINRELOC_RUNNING_DOXYGEN
+/* Mangle symbol names to avoid symbol collisions with other ELF objects. */
+	#define _sql_gbr_find_exe         fnYM49765777344607__sql_gbr_find_exe
+	#define _sql_gbr_find_exe_dir     fnYM49765777344607__sql_gbr_find_exe_dir
+	#define _sql_gbr_find_prefix      fnYM49765777344607__sql_gbr_find_prefix
+	#define _sql_gbr_find_bin_dir     fnYM49765777344607__sql_gbr_find_bin_dir
+	#define _sql_gbr_find_sbin_dir    fnYM49765777344607__sql_gbr_find_sbin_dir
+	#define _sql_gbr_find_data_dir    fnYM49765777344607__sql_gbr_find_data_dir
+	#define _sql_gbr_find_locale_dir  fnYM49765777344607__sql_gbr_find_locale_dir
+	#define _sql_gbr_find_lib_dir     fnYM49765777344607__sql_gbr_find_lib_dir
+	#define _sql_gbr_find_libexec_dir fnYM49765777344607__sql_gbr_find_libexec_dir
+	#define _sql_gbr_find_etc_dir     fnYM49765777344607__sql_gbr_find_etc_dir
+
+
+#endif
+gboolean _sql_gbr_init             (GError **error);
+gboolean _sql_gbr_init_lib         (GError **error);
+
+gchar   *_sql_gbr_find_exe         (const gchar *default_exe);
+gchar   *_sql_gbr_find_exe_dir     (const gchar *default_dir);
+gchar   *_sql_gbr_find_prefix      (const gchar *default_prefix);
+gchar   *_sql_gbr_find_bin_dir     (const gchar *default_bin_dir);
+gchar   *_sql_gbr_find_sbin_dir    (const gchar *default_sbin_dir);
+gchar   *_sql_gbr_find_data_dir    (const gchar *default_data_dir);
+gchar   *_sql_gbr_find_locale_dir  (const gchar *default_locale_dir);
+gchar   *_sql_gbr_find_lib_dir     (const gchar *default_lib_dir);
+gchar   *_sql_gbr_find_libexec_dir (const gchar *default_libexec_dir);
+gchar   *_sql_gbr_find_etc_dir     (const gchar *default_etc_dir);
+
+
+G_END_DECLS
+
+#endif /* __BINRELOC_H__ */

Added: trunk/tools/binreloc/sql-binreloc.c
==============================================================================
--- (empty file)
+++ trunk/tools/binreloc/sql-binreloc.c	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,261 @@
+/* GDA common library
+ * Copyright (C) 2007 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "sql-binreloc.h"
+/* include source file as mentionned in gbr_init_lib()'s doc */
+#include "binreloc.c"
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+/* Remember HMODULE to retrieve path to it lateron */
+static HMODULE hdllmodule = NULL;
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
+{
+	switch(fdwReason)
+	{
+	case DLL_PROCESS_ATTACH:
+		hdllmodule = (HMODULE)hinstDLL;
+		break;
+	}
+
+	return TRUE;
+}
+#elif HAVE_CARBON
+#include <Carbon/Carbon.h>
+#endif
+
+
+/**
+ * sql_gbr_init
+ */
+void
+sql_gbr_init (void)
+{
+#ifdef G_OS_WIN32
+	/* nothing */
+#elif HAVE_CARBON
+	/* nothing */
+#else
+	_sql_gbr_init_lib (NULL);
+#endif
+}
+
+/*
+ * sql_gbr_get_file_path
+ */
+gchar *
+sql_gbr_get_file_path (GdaPrefixDir where, ...)
+{
+	gchar *prefix = NULL;
+	gchar *tmp, *file_part;
+	va_list ap;
+	gchar **parts;
+	gint size, i;
+	const gchar *prefix_dir_name = NULL;
+	gint prefix_len = strlen (LIBGDAPREFIX);
+
+	/*
+	g_print ("LIBGDAPREFIX = %s\n", LIBGDAPREFIX);
+	g_print ("LIBGDABIN = %s\n", LIBGDABIN);
+	g_print ("LIBGDASBIN = %s\n", LIBGDASBIN);
+	g_print ("LIBGDADATA = %s\n",LIBGDADATA );
+	g_print ("LIBGDALIB = %s\n", LIBGDALIB);
+	g_print ("LIBGDALIBEXEC = %s\n",LIBGDALIBEXEC );
+	g_print ("LIBGDASYSCONF = %s\n", LIBGDASYSCONF);
+	*/
+
+#ifdef G_OS_WIN32
+	wchar_t path[MAX_PATH];
+	gchar* p;
+#endif
+
+	switch (where) {
+	default:
+	case SQL_NO_DIR:
+		break;
+	case SQL_BIN_DIR:
+		tmp = LIBGDABIN;
+#ifndef G_OS_WIN32
+		if (! g_str_has_prefix (tmp, LIBGDAPREFIX) || (tmp [prefix_len] != G_DIR_SEPARATOR)) 
+			prefix = g_strdup (tmp);
+		else
+			prefix_dir_name = tmp + prefix_len + 1;
+#else
+		prefix_dir_name = "bin";
+#endif
+		break;
+	case SQL_SBIN_DIR:
+		tmp = LIBGDASBIN;
+#ifndef G_OS_WIN32
+		if (! g_str_has_prefix (tmp, LIBGDAPREFIX) || (tmp [prefix_len] != G_DIR_SEPARATOR)) 
+			prefix = g_strdup (tmp);
+		else
+			prefix_dir_name = tmp + prefix_len + 1;
+#else
+		prefix_dir_name = "sbin";
+#endif
+		break;
+	case SQL_DATA_DIR:
+		tmp = LIBGDADATA;
+#ifndef G_OS_WIN32
+		if (! g_str_has_prefix (tmp, LIBGDAPREFIX) || (tmp [prefix_len] != G_DIR_SEPARATOR)) 
+			prefix = g_strdup (tmp);
+		else
+			prefix_dir_name = tmp + prefix_len + 1;
+#else
+		prefix_dir_name = "share";
+#endif		
+		break;
+	case SQL_LOCALE_DIR:
+		tmp = LIBGDADATA;
+#ifndef G_OS_WIN32
+		if (! g_str_has_prefix (tmp, LIBGDAPREFIX) || (tmp [prefix_len] != G_DIR_SEPARATOR)) {
+			prefix = g_strdup (tmp);
+			prefix_dir_name = "locale";
+		}
+		else
+			prefix_dir_name = "share" G_DIR_SEPARATOR_S "locale";
+#else
+		prefix_dir_name = "share" G_DIR_SEPARATOR_S "locale";
+#endif
+		break;
+	case SQL_LIB_DIR:
+		tmp = LIBGDALIB;
+#ifndef G_OS_WIN32
+		if (! g_str_has_prefix (tmp, LIBGDAPREFIX) || (tmp [prefix_len] != G_DIR_SEPARATOR)) 
+			prefix = g_strdup (tmp);
+		else 
+			prefix_dir_name = tmp + prefix_len + 1;
+#else
+		prefix_dir_name = "lib";
+#endif
+		break;
+	case SQL_LIBEXEC_DIR:
+		tmp = LIBGDALIBEXEC;
+#ifndef G_OS_WIN32
+		if (! g_str_has_prefix (tmp, LIBGDAPREFIX) || (tmp [prefix_len] != G_DIR_SEPARATOR)) 
+			prefix = g_strdup (tmp);
+		else
+			prefix_dir_name = tmp + prefix_len + 1;
+#else
+		prefix_dir_name = "libexec";
+#endif
+		break;
+	case SQL_ETC_DIR:
+		tmp = LIBGDASYSCONF;
+#ifndef G_OS_WIN32
+		if (! g_str_has_prefix (tmp, LIBGDAPREFIX) || (tmp [prefix_len] != G_DIR_SEPARATOR)) 
+			prefix = g_strdup (tmp);
+		else
+			prefix_dir_name = tmp + prefix_len + 1;
+#else
+		prefix_dir_name = "etc";
+#endif
+		break;
+	}
+
+#ifdef SQL_DEBUG_NO
+	g_print ("%s ()\n", __FUNCTION__);
+#endif
+
+	if (!prefix) {
+		/* prefix part for each OS */
+#ifdef G_OS_WIN32
+		/* Get from location of libgda DLL */
+		GetModuleFileNameW (hdllmodule, path, MAX_PATH);
+		prefix = g_utf16_to_utf8 (path, -1, NULL, NULL, NULL);
+		if ((p = strrchr (prefix, G_DIR_SEPARATOR)) != NULL)
+			*p = '\0';
+		
+		p = strrchr (prefix, G_DIR_SEPARATOR);
+		if (p && (g_ascii_strcasecmp (p + 1, "bin") == 0 ||
+			  g_ascii_strcasecmp (p + 1, "lib") == 0))
+			*p = '\0';
+#elif HAVE_CARBON
+#define MAXLEN 500
+		ProcessSerialNumber myProcess;
+		FSRef bundleLocation;
+		unsigned char bundlePath[MAXLEN];
+		
+		if ((GetCurrentProcess (&myProcess) == noErr) &&
+		    (GetProcessBundleLocation (&myProcess, &bundleLocation) == noErr) &&
+		    (FSRefMakePath (&bundleLocation, bundlePath, MAXLEN) == noErr)) {
+			prefix = g_path_get_dirname ((const char*) bundlePath);
+			if (g_str_has_suffix (prefix, "bin"))
+				prefix [strlen (prefix) - 3] = 0;
+		}
+		else
+			g_warning ("Could not get PREFIX (using Mac OS X Carbon)");
+#else
+		if (!prefix)
+			prefix = _sql_gbr_find_prefix (LIBGDAPREFIX);
+#endif
+	}
+
+	if (!prefix || !*prefix)
+		return NULL;
+       
+	/* filename part */
+	size = 10;
+	parts = g_new0 (gchar *, size);
+	va_start (ap, where);
+	for (i = 0, tmp = va_arg (ap, gchar *); tmp; tmp = va_arg (ap, gchar *)) {
+		if (i == size - 1) {
+			size += 10;
+			parts = g_renew (gchar *, parts, size);
+		}
+		parts[i] = g_strdup (tmp);
+#ifdef SQL_DEBUG_NO
+		g_print ("\t+ %s\n", tmp);
+#endif
+		i++;
+	}
+	parts[i] = NULL;
+	va_end (ap);
+
+	file_part = g_build_filenamev (parts);
+	g_strfreev (parts);
+
+	/* result */
+	if (prefix_dir_name)
+		tmp = g_build_filename (prefix, prefix_dir_name, file_part, NULL);
+	else
+		tmp = g_build_filename (prefix, file_part, NULL);
+
+	if (!g_file_test (tmp, G_FILE_TEST_EXISTS) &&
+	    g_str_has_suffix (prefix, "libgda")) {
+		/* test if we are in the sources */
+		g_free (tmp);
+		if (prefix_dir_name)
+			tmp = g_build_filename (LIBGDAPREFIX, prefix_dir_name, file_part, NULL);
+		else
+			tmp = g_build_filename (LIBGDAPREFIX, file_part, NULL);
+	}
+
+	g_free (prefix);
+	g_free (file_part);
+#ifdef SQL_DEBUG_NO
+	g_print ("\t=> %s\n", tmp);
+#endif
+
+	return tmp;
+}

Added: trunk/tools/binreloc/sql-binreloc.h
==============================================================================
--- (empty file)
+++ trunk/tools/binreloc/sql-binreloc.h	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,49 @@
+/* GDA common library
+ * Copyright (C) 2007 The GNOME Foundation.
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __SQL_BINRELOC_H__
+#define __SQL_BINRELOC_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/*
+ * Locating files
+ */
+typedef enum {
+	SQL_NO_DIR,
+	SQL_BIN_DIR,
+	SQL_SBIN_DIR,
+	SQL_DATA_DIR,
+	SQL_LOCALE_DIR,
+	SQL_LIB_DIR,
+	SQL_LIBEXEC_DIR,
+	SQL_ETC_DIR
+} GdaPrefixDir;
+
+void     sql_gbr_init          (void);
+gchar   *sql_gbr_get_file_path (GdaPrefixDir where, ...);
+
+G_END_DECLS
+
+#endif

Added: trunk/tools/cnc.js
==============================================================================
--- (empty file)
+++ trunk/tools/cnc.js	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,14 @@
+function refreshCncList() {
+$.ajax({
+	url: "~cnclist",
+	cache: false,
+	success: function(html){
+			$("#cnclist").replaceWith(html);
+			setTimeout("refreshCncList()",10000);
+		}
+	});
+}
+
+$(document).ready(function(){		
+		setTimeout("refreshCncList()",10000);
+});

Modified: trunk/tools/command-exec.c
==============================================================================
--- trunk/tools/command-exec.c	(original)
+++ trunk/tools/command-exec.c	Fri Dec 26 14:37:32 2008
@@ -140,86 +140,6 @@
 	g_free (res);
 }
 
-static GdaInternalCommand *
-find_command (GdaInternalCommandsList *commands_list, const gchar *command_str, gboolean *command_complete)
-{
-	GdaInternalCommand *command = NULL;
-	GSList *list;
-	gint length;
-
-	if (!command_str || ((*command_str != '\\') && (*command_str != '.')))
-		return NULL;
-
-	length = strlen (command_str + 1);
-	for (list = commands_list->name_ordered; list; list = list->next) {
-		command = (GdaInternalCommand*) list->data;
-		if (!strncmp (command->name, command_str + 1, MIN (length, strlen (command->name)))) {
-			gint l;
-			gchar *ptr;
-			for (ptr = command->name, l = 0; *ptr && (*ptr != ' '); ptr++, l++);
-				
-			if (length == l)
-				break;
-			else
-				command = NULL;
-		}
-		else
-			command = NULL;
-	}
-
-	/* FIXME */
-	if (command_complete)
-		*command_complete = TRUE;
-
-	return command;
-}
-
-static gint
-commands_compare_name (GdaInternalCommand *a, GdaInternalCommand *b)
-{
-	gint cmp, alength, blength;
-	if (!a->name || !b->name) {
-		g_warning (_("Invalid unnamed command"));
-		if (!a->name) {
-			if (b->name)
-				return 1;
-			else
-				return 0;
-		}
-		else
-			return -1;
-	}
-	alength = strlen (a->name);
-	blength = strlen (b->name);
-	cmp = strncmp (a->name, b->name, MIN (alength, blength));
-	if (cmp == 0) 
-		return blength - alength;
-	else
-		return cmp;
-}
-
-static gint
-commands_compare_group (GdaInternalCommand *a, GdaInternalCommand *b)
-{
-	if (!a->group) {
-		if (b->group)
-			return 1;
-		else
-			return 0;
-	}
-	else {
-		if (b->group) {
-			gint cmp = strcmp (a->group, b->group);
-			if (cmp)
-				return cmp;
-			else 
-				return commands_compare_name (a, b);
-		}
-		else
-			return -1;
-	}
-}
-
 /*
  * Small tokenizer
  */
@@ -276,7 +196,7 @@
 }
 
 /* default function to split arguments */
-static gchar **
+gchar **
 default_gda_internal_commandargs_func (const gchar *string)
 {
 	gchar **array = NULL;
@@ -322,78 +242,8 @@
 	return array;
 }
 
-
-/*
- * gda_internal_command_execute
- *
- * Executes a command starting by \ such as:
- */
-GdaInternalCommandResult *
-gda_internal_command_execute (GdaInternalCommandsList *commands_list,
-			      GdaConnection *cnc, const gchar *command_str, GError **error)
-{
-	GdaInternalCommand *command;
-	gboolean command_complete;
-	gchar **args;
-	GdaInternalCommandResult *res = NULL;
-
-	g_return_val_if_fail (commands_list, NULL);
-	if (!commands_list->name_ordered) {
-		GSList *list;
-
-		for (list = commands_list->commands; list; list = list->next) {
-			commands_list->name_ordered = 
-				g_slist_insert_sorted (commands_list->name_ordered, list->data,
-						       (GCompareFunc) commands_compare_name);
-			commands_list->group_ordered = 
-				g_slist_insert_sorted (commands_list->group_ordered, list->data,
-						       (GCompareFunc) commands_compare_group);
-		}
-	}
-
-	args = g_strsplit (command_str, " ", 2);
-	command = find_command (commands_list, args[0], &command_complete);
-	g_strfreev (args);
-	args = NULL;
-	if (!command) {
-		g_set_error (error, 0, 0, "%s", 
-			     _("Unknown internal command"));
-		goto cleanup;
-	}
-
-	if (!command->command_func) {
-		g_set_error (error, 0, 0, "%s", 
-			     _("Internal command not correctly defined"));
-		goto cleanup;
-	}
-
-	if (!command_complete) {
-		g_set_error (error, 0, 0, "%s", 
-			     _("Incomplete internal command"));
-		goto cleanup;
-	}
-
-	if (command->arguments_delimiter_func)
-		args = command->arguments_delimiter_func (command_str);
-	else
-		args = default_gda_internal_commandargs_func (command_str);
-	if (command->unquote_args) {
-		gint i;
-		for (i = 1; args[i]; i++) 
-			gda_internal_command_arg_remove_quotes (args[i]);
-	}
-	res = command->command_func (cnc, (const gchar **) &(args[1]), 
-				     error, command->user_data);
-	
- cleanup:
-	if (args)
-		g_strfreev (args);
-
-	return res;
-}
-
 GdaInternalCommandResult *
-gda_internal_command_help (GdaConnection *cnc, const gchar **args, 
+gda_internal_command_help (SqlConsole *console, GdaConnection *cnc, const gchar **args, 
 			   GError **error,
 			   GdaInternalCommandsList *clist)
 {
@@ -408,6 +258,10 @@
 
 	for (list = clist->group_ordered; list; list = list->next) {
 		GdaInternalCommand *command = (GdaInternalCommand*) list->data;
+
+		if (console && command->limit_to_main)
+			continue;
+
 		if (!current_group || strcmp (current_group, command->group)) {
 			current_group = command->group;
 			if (list != clist->group_ordered)
@@ -434,13 +288,19 @@
 }
 
 GdaInternalCommandResult *
-gda_internal_command_history (GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
+gda_internal_command_history (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 
 	res = g_new0 (GdaInternalCommandResult, 1);
 	res->type = GDA_INTERNAL_COMMAND_RESULT_TXT;
 
+	if (console) {
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		TO_IMPLEMENT;
+		return res;
+	}
+
 	GString *string;
 #ifdef HAVE_HISTORY
 	if (args[0]) {
@@ -473,7 +333,7 @@
 }
 
 GdaInternalCommandResult *
-gda_internal_command_dict_sync (GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
+gda_internal_command_dict_sync (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 
@@ -494,7 +354,7 @@
 }
 
 GdaInternalCommandResult *
-gda_internal_command_list_tables (GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
+gda_internal_command_list_tables (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	GdaDataModel *model;
@@ -537,7 +397,7 @@
 }
 
 GdaInternalCommandResult *
-gda_internal_command_list_views (GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
+gda_internal_command_list_views (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	GdaDataModel *model;
@@ -579,7 +439,7 @@
 }
 
 GdaInternalCommandResult *
-gda_internal_command_list_schemas (GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
+gda_internal_command_list_schemas (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	GdaDataModel *model;
@@ -711,7 +571,7 @@
 }
 
 GdaInternalCommandResult *
-gda_internal_command_detail (GdaConnection *cnc, const gchar **args,
+gda_internal_command_detail (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 			     GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;

Modified: trunk/tools/command-exec.h
==============================================================================
--- trunk/tools/command-exec.h	(original)
+++ trunk/tools/command-exec.h	Fri Dec 26 14:37:32 2008
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <glib.h>
 #include <libgda/libgda.h>
+#include "gda-sql.h"
 
 /*
  * Command exec result
@@ -52,7 +53,8 @@
 /*
  * Command definition
  */
-typedef GdaInternalCommandResult *(*GdaInternalCommandFunc) (GdaConnection *, const gchar **, GError **, gpointer);
+typedef GdaInternalCommandResult *(*GdaInternalCommandFunc) (SqlConsole *, GdaConnection *cnc,
+							     const gchar **, GError **, gpointer);
 typedef gchar                   **(*GdaInternalCommandArgsFunc) (const gchar *);
 typedef struct {
 	gchar    *name;
@@ -68,6 +70,7 @@
 	gpointer                   user_data;
 	GdaInternalCommandArgsFunc arguments_delimiter_func;
 	gboolean                   unquote_args;
+	gboolean                   limit_to_main;
 } GdaInternalCommand;
 
 typedef struct {
@@ -81,24 +84,23 @@
 gchar                    *gda_internal_command_arg_remove_quotes (gchar *str);
 
 /* Commands execution */
-GdaInternalCommandResult *gda_internal_command_execute (GdaInternalCommandsList *commands_list,
-							GdaConnection *cnc, const gchar *command_str, GError **error);
+gchar                   **default_gda_internal_commandargs_func (const gchar *string);
 void                      gda_internal_command_exec_result_free (GdaInternalCommandResult *res);
 
 /* Available commands */
-GdaInternalCommandResult *gda_internal_command_help (GdaConnection *cnc, const gchar **args,
+GdaInternalCommandResult *gda_internal_command_help (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 						     GError **error, GdaInternalCommandsList *clist);
-GdaInternalCommandResult *gda_internal_command_history (GdaConnection *cnc, const gchar **args,
+GdaInternalCommandResult *gda_internal_command_history (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 							GError **error, gpointer data);
-GdaInternalCommandResult *gda_internal_command_dict_sync (GdaConnection *cnc, const gchar **args,
+GdaInternalCommandResult *gda_internal_command_dict_sync (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 							  GError **error, gpointer data);
-GdaInternalCommandResult *gda_internal_command_list_tables (GdaConnection *cnc, const gchar **args,
+GdaInternalCommandResult *gda_internal_command_list_tables (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 							    GError **error, gpointer data);
-GdaInternalCommandResult *gda_internal_command_list_views (GdaConnection *cnc, const gchar **args,
+GdaInternalCommandResult *gda_internal_command_list_views (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 							   GError **error, gpointer data);
-GdaInternalCommandResult *gda_internal_command_list_schemas (GdaConnection *cnc, const gchar **args,
+GdaInternalCommandResult *gda_internal_command_list_schemas (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 							     GError **error, gpointer data);
-GdaInternalCommandResult *gda_internal_command_detail (GdaConnection *cnc, const gchar **args,
+GdaInternalCommandResult *gda_internal_command_detail (SqlConsole *console, GdaConnection *cnc, const gchar **args,
 						       GError **error, gpointer data);
 
 /* Misc */

Added: trunk/tools/gda-print.css
==============================================================================
--- (empty file)
+++ trunk/tools/gda-print.css	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,288 @@
+/* General styles */
+body {
+    margin:0px;
+    padding:0px;
+    border:0px;
+    width:100%;
+    background-color: white;
+    font-family: sans-serif;
+    color: black;
+}
+
+a {
+    color:#0000ff;
+    border: 0px;
+}
+
+a:active {
+    color: #ff0000;
+}
+
+a:visited {
+    color: #551a8b;
+}
+
+h1, h2, h3 {
+    margin:.8em 0 .2em 0;
+    padding:0;
+}
+
+p {
+    margin:.4em 0 .8em 0;
+    padding:0;
+}
+
+img {
+    margin:10px 0 5px;
+}
+/* Header styles */
+#header {
+    background: #729FCF;
+    clear:both;
+    float:left;
+    width:100%;
+    font-size: 75%;
+    border-bottom:3px;
+}
+
+#header p,
+#header h1,
+#header h2 {
+    padding-top: 0px;
+    padding-bottom: 10px;
+    color: #eeeeec;
+    margin-left: 85px;
+}
+#header ul {
+    clear:left;
+    float:left;
+    width:100%;
+    list-style:none;
+    margin:10px 0 0 0;
+    padding:0;
+    font-size: 85%;
+}
+#header ul li {
+    display:inline;
+    list-style:none;
+    margin:0;
+    padding:0;
+}
+#header ul li a {
+    font-weight: bold;
+    display:block;
+    float:left;
+    margin:0 0 0 1px;
+    padding:3px 10px;
+    text-align:center;
+    background:#eee;
+    color: #FFFFFF;
+    text-decoration:none;
+    position:relative;
+    left:15px;
+    line-height:1.3em;
+}
+#header ul li a:hover {
+    background:#369;
+    color:#fff;
+}
+#header ul li a.active,
+#header ul li a.active:hover {
+    color:#fff;
+    background:#000;
+    font-weight:bold;
+}
+#header ul li a span {
+    display:block;
+}
+
+/* column container */
+.colmask {
+    position:relative;
+    clear:both;
+    float:left;
+    width:100%;			/* width of whole page */
+    overflow:hidden;
+}
+
+/* common column settings */
+.colright,
+.colmid,
+.colleft {
+    float:left;
+    width:100%;
+    position:relative;
+}
+.col1,
+.col2,
+.col3 {
+    float:left;
+    position:relative;
+    padding:0 0 1em 0;
+    overflow:hidden;
+}
+
+.col1 {
+    background-color: #fff;
+    color: #333;
+}
+
+.leftmenu .colleft {
+    right:98%;			/* right column width */
+    background: #2E3436;
+    color: #FFFFFF;
+}
+.leftmenu .col1 {
+    width:96%;			/* right column content width */
+    left:101%;			/* 100% plus left column left padding */
+}
+.leftmenu .col2 {
+    width:0%;			/* left column content width (column width minus left and right padding) */
+    left:2%;			/* (right column left and right padding) plus (left column left padding) */
+    visibility:hidden;
+}
+
+.col2 ul {
+    font-weight: bold;
+    list-style: none;
+    padding: 0 10px 10px;
+    margin: 0 0 0 0;
+    font-size: 90%;
+}
+
+.col2 li a {
+    font-weight: normal;
+    color: #FFFFFF;
+    margin: 0 0 0 0;
+    padding: 0 10px;
+    text-decoration: none;
+    font-size: 80%;
+}
+
+.col1 h1 {
+    /*margin: 5em 0px .5em 0px;*/
+    font-size: 100%;
+    color: black;
+}
+
+.col1 h2 {
+    padding-left: 5px;
+    font-size: 80%;
+}
+
+.col1 ul {
+    font-weight: bold;
+    list-style: none;
+    padding: 0px 10px 10px;
+    margin: 0px 0px 0px 0px;
+    font-size: 90%;
+}
+
+.col1 li {
+    font-weight: normal;
+    margin: 0px 0px 0px 0px;
+    padding: 0px 10px;
+    text-decoration: none;
+    font-size: 80%;
+}
+
+div.clist {
+    padding: 0px;
+    overflow: hidden;
+}
+
+.clist ul {
+    float: left;
+    width: 100%;
+    margin: 0px;
+    margin-left: 10px;
+    padding: 0px;
+    list-style: none;
+}
+
+.clist li {
+    float: left;
+    width: 33%;
+    margin: 0px;
+    padding: 0px;
+    font-size: 90%;
+}
+
+.clist a {
+    font-weight: normal;
+    color: #050505;
+    margin: 0px 0px 0px 0px;
+    padding: 0px 0px 0px 0px;
+    text-decoration: none;
+}
+
+.clist br {
+    clear: both;
+}
+
+table.ctable
+{
+    font-weight: normal;
+    font-size: 90%;
+    width: 100%;
+    background-color: #fafafa;
+    border: 1px #6699CC solid;
+    border-collapse: collapse;
+    border-spacing: 0px;
+    margin-top: 0px;
+    margin-bottom: 5px;
+}
+
+.ctable th
+{
+    border-bottom: 2px solid #6699CC;
+    background-color: #729FCF;
+    text-align: center;
+    font-weight: bold;
+    color: #eeeeec;
+}
+
+.ctable td
+{
+    padding-left: 2px;
+    border-left: 1px dotted #729FCF;
+}
+
+.graph
+{
+    /*background: lightblue;*/
+    padding: 0px;
+}
+
+.graph img
+{
+    max-width: 100%;
+    height: auto;
+    border: 0px;
+}
+
+.pkey
+{
+    /*background: lightblue;*/
+    color: blue;
+    font-weight: bold;
+}
+.ccode
+{
+    /*background: lightblue;*/
+    padding-left: 5px;
+}
+
+
+/* Footer styles */
+#footer {
+    clear:both;
+    float:left;
+    width:100%;
+    border-top:1px solid #000;
+    font-size:60%;
+}
+#footer p {
+    padding:10px;
+    margin:0;
+}
\ No newline at end of file

Modified: trunk/tools/gda-sql.c
==============================================================================
--- trunk/tools/gda-sql.c	(original)
+++ trunk/tools/gda-sql.c	Fri Dec 26 14:37:32 2008
@@ -35,6 +35,7 @@
 #include <libgda/gda-quark-list.h>
 #include <libgda/gda-meta-struct.h>
 #include <libgda/gda-blob-op.h>
+#include "binreloc/sql-binreloc.h"
 
 #ifndef G_OS_WIN32
 #include <signal.h>
@@ -67,6 +68,7 @@
 
 #ifdef HAVE_LIBSOUP
 gint http_port = -1;
+gchar *auth_token = NULL;
 #endif
 
 static GOptionEntry entries[] = {
@@ -80,6 +82,7 @@
         { "list-providers", 'L', 0, G_OPTION_ARG_NONE, &list_providers, "List installed database providers and exit", NULL },
 #ifdef HAVE_LIBSOUP
 	{ "http-port", 's', 0, G_OPTION_ARG_INT, &http_port, "Run embedded HTTP server on specified port", "port" },
+	{ "http-token", 't', 0, G_OPTION_ARG_STRING, &auth_token, "Authentication token (required to authenticate clients)", "token" },
 #endif
         { NULL }
 };
@@ -96,16 +99,9 @@
 } SigintHandlerCode;
 static SigintHandlerCode sigint_handler_status = SIGINT_HANDLER_DISABLED;
 
-typedef enum {
-	OUTPUT_FORMAT_DEFAULT = 0,
-	OUTPUT_FORMAT_HTML,
-	OUTPUT_FORMAT_XML,
-	OUTPUT_FORMAT_CSV
-} OutputFormat;
-
 /* structure to hold program's data */
 typedef struct {
-	GSList *settings; /* list all the CncSetting */
+	GSList *settings; /* list all the ConnectionSetting */
 	ConnectionSetting *current; /* current connection setting to which commands are sent */
 	GdaInternalCommandsList *internal_commands;
 
@@ -126,27 +122,32 @@
 GString *prompt = NULL;
 GMainLoop *main_loop = NULL;
 
+static ConnectionSetting *get_current_connection_settings (SqlConsole *console);
 static char   **completion_func (const char *text, int start, int end);
-static void     compute_prompt (MainData *data, GString *string, gboolean in_command);
-static gboolean set_output_file (MainData *data, const gchar *file, GError **error);
-static gboolean set_input_file (MainData *data, const gchar *file, GError **error);
-static void     output_data_model (MainData *data, GdaDataModel *model);
-static void     output_string (MainData *data, const gchar *str);
-static ConnectionSetting *open_connection (MainData *data, const gchar *cnc_name, const gchar *cnc_string,
+static void     compute_prompt (SqlConsole *console, GString *string, gboolean in_command);
+static gboolean set_output_file (const gchar *file, GError **error);
+static gboolean set_input_file (const gchar *file, GError **error);
+static gchar   *data_model_to_string (SqlConsole *console, GdaDataModel *model);
+static void     output_data_model (GdaDataModel *model);
+static void     output_string (const gchar *str);
+static ConnectionSetting *open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_string,
 					   GError **error);
 static void connection_settings_free (ConnectionSetting *cs);
-static GdaDataModel *list_all_dsn (MainData *data);
-static GdaDataModel *list_all_providers (MainData *data);
+static GdaDataModel *list_all_dsn (void);
+static GdaDataModel *list_all_providers (void);
 
-static gboolean treat_line_func (const gchar *cmde, MainData *data);
+static gboolean treat_line_func (const gchar *cmde, gpointer data);
 static const char *prompt_func (void);
 
 
 /* commands manipulation */
-static GdaInternalCommandsList  *build_internal_commands_list (MainData *data);
+static GdaInternalCommandsList  *build_internal_commands_list (void);
 static gboolean                  command_is_complete (const gchar *command);
-static GdaInternalCommandResult *command_execute (MainData *data, const gchar *command, GError **error);
-static void                      display_result (MainData *data, GdaInternalCommandResult *res);
+static GdaInternalCommandResult *command_execute (SqlConsole *console,
+						  const gchar *command, GError **error);
+
+static gchar                    *result_to_string (SqlConsole *console, GdaInternalCommandResult *res);
+static void                      display_result (GdaInternalCommandResult *res);
 
 int
 main (int argc, char *argv[])
@@ -164,7 +165,10 @@
 		return EXIT_FAILURE;
         }
         g_option_context_free (context);
+
         gda_init ();
+	sql_gbr_init ();
+
 	has_threads = g_thread_supported ();
 	data = g_new0 (MainData, 1);
 	data->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
@@ -173,7 +177,7 @@
 
 	/* output file */
 	if (outfile) {
-		if (! set_output_file (data, outfile, &error)) {
+		if (! set_output_file (outfile, &error)) {
 			g_print ("Can't set output file as '%s': %s\n", outfile,
 				 error->message);
 			exit_status = EXIT_FAILURE;
@@ -183,21 +187,21 @@
 
 	/* treat here lists of providers and defined DSN */
 	if (list_providers) {
-		GdaDataModel *model = list_all_providers (data);
-		output_data_model (data, model);
+		GdaDataModel *model = list_all_providers ();
+		output_data_model (model);
 		g_object_unref (model);
 		goto cleanup;
 	}
 	if (list_configs) {
-		GdaDataModel *model = list_all_dsn (data);
-		output_data_model (data, model);
+		GdaDataModel *model = list_all_dsn ();
+		output_data_model (model);
 		g_object_unref (model);
 		goto cleanup;
 	}
 
 	/* commands file */
 	if (commandsfile) {
-		if (! set_input_file (data, commandsfile, &error)) {
+		if (! set_input_file (commandsfile, &error)) {
 			g_print ("Can't read file '%s': %s\n", commandsfile,
 				 error->message);
 			exit_status = EXIT_FAILURE;
@@ -245,7 +249,7 @@
 			str = g_strdup_printf ("c%d", i-1);
 		if (!data->output_stream) 
 			g_print (_("Opening connection '%s' for: %s\n"), str, argv[i]);
-		cs = open_connection (data, str, argv[i], &error);
+		cs = open_connection (NULL, str, argv[i], &error);
 		g_free (str);
 		if (!cs) {
 			g_print (_("Can't open connection %d: %s\n"), i,
@@ -262,7 +266,7 @@
 		if (!data->output_stream) 
 			g_print (_("Opening connection '%s' for: %s (GDA_SQL_CNC environment variable)\n"), 
 				   str, envstr);
-		cs = open_connection (data, str, envstr, &error);
+		cs = open_connection (NULL, str, envstr, &error);
 		g_free (str);
 		if (!cs) {
 			g_print (_("Can't open connection defined by GDA_SQL_CNC: %s\n"),
@@ -273,12 +277,12 @@
 	}
 
 	/* build internal command s list */
-	data->internal_commands = build_internal_commands_list (data);
+	data->internal_commands = build_internal_commands_list ();
 
 #ifdef HAVE_LIBSOUP
 	/* start HTTP server if requested */
 	if (http_port > 0) {
-		main_data->server = web_server_new (http_port);
+		main_data->server = web_server_new (http_port, auth_token);
 		if (!main_data->server) {
 			g_print (_("Can't run HTTP server on port %d\n"), http_port);
 			exit_status = EXIT_FAILURE;
@@ -289,7 +293,7 @@
 
 	/* process commands which need to be executed as specified by the command line args */
 	if (single_command) {
-		treat_line_func (single_command, data);
+		treat_line_func (single_command, NULL);
 		if (!data->output_stream)
 			g_print ("\n");
 		goto cleanup;
@@ -300,14 +304,14 @@
 		for (;;) {
 			cmde = input_from_stream (data->input_stream);
 			if (cmde) {
-				treat_line_func (cmde, data);
+				treat_line_func (cmde, NULL);
 				g_free (cmde);
 			}
 			else
 				break;
 		}
 		if (interractive && !cmde && isatty (fileno (stdin)))
-			set_input_file (data, NULL, NULL);
+			set_input_file (NULL, NULL);
 		else {
 			if (!data->output_stream)
 				g_print ("\n");
@@ -317,7 +321,7 @@
 
 	/* set up interractive commands */
 	setup_sigint_handler ();
-	init_input ((TreatLineFunc) treat_line_func, prompt_func, data);
+	init_input ((TreatLineFunc) treat_line_func, prompt_func, NULL);
 	set_completion_func (completion_func);
 	init_history ();
 
@@ -330,8 +334,8 @@
  cleanup:
 	/* cleanups */
 	g_slist_foreach (data->settings, (GFunc) connection_settings_free, NULL);
-	set_input_file (data, NULL, NULL); 
-	set_output_file (data, NULL, NULL); 
+	set_input_file (NULL, NULL); 
+	set_output_file (NULL, NULL); 
 	end_input ();
 
 	g_free (data);
@@ -343,18 +347,18 @@
 prompt_func (void)
 {
 	/* compute a new prompt */
-	compute_prompt (main_data, prompt, main_data->partial_command == NULL ? FALSE : TRUE);
+	compute_prompt (NULL, prompt, main_data->partial_command == NULL ? FALSE : TRUE);
 	return (char*) prompt->str;
 }
 
 /* @cmde is stolen here */
 static gboolean
-treat_line_func (const gchar *cmde, MainData *data)
+treat_line_func (const gchar *cmde, gpointer data)
 {
 	gchar *loc_cmde = NULL;
 	if (!cmde) {
 		save_history (NULL, NULL);
-		if (!data->output_stream)
+		if (!main_data->output_stream)
 			g_print ("\n");
 		goto exit;
 	}
@@ -363,34 +367,34 @@
 	g_strchug (loc_cmde);
 	if (*loc_cmde) {
 		add_to_history (loc_cmde);
-		if (!data->partial_command) {
+		if (!main_data->partial_command) {
 			/* enable SIGINT handling */
 			sigint_handler_status = SIGINT_HANDLER_PARTIAL_COMMAND;
-			data->partial_command = g_string_new (loc_cmde);
+			main_data->partial_command = g_string_new (loc_cmde);
 		}
 		else {
-			g_string_append_c (data->partial_command, ' ');
-			g_string_append (data->partial_command, loc_cmde);
+			g_string_append_c (main_data->partial_command, ' ');
+			g_string_append (main_data->partial_command, loc_cmde);
 		}
-		if (command_is_complete (data->partial_command->str)) {
+		if (command_is_complete (main_data->partial_command->str)) {
 			/* execute command */
 			GdaInternalCommandResult *res;
 			FILE *to_stream;
 			GError *error = NULL;
 			
-			if ((*data->partial_command->str != '\\') && (*data->partial_command->str != '.')) {
-				if (data->current) {
-					if (!data->current->query_buffer)
-						data->current->query_buffer = g_string_new ("");
-					g_string_assign (data->current->query_buffer, data->partial_command->str);
+			if ((*main_data->partial_command->str != '\\') && (*main_data->partial_command->str != '.')) {
+				if (main_data->current) {
+					if (!main_data->current->query_buffer)
+						main_data->current->query_buffer = g_string_new ("");
+					g_string_assign (main_data->current->query_buffer, main_data->partial_command->str);
 				}
 			}
 			
-			if (data && data->output_stream)
-				to_stream = data->output_stream;
+			if (data && main_data->output_stream)
+				to_stream = main_data->output_stream;
 			else
 				to_stream = stdout;
-			res = command_execute (data, data->partial_command->str, &error);
+			res = command_execute (NULL, main_data->partial_command->str, &error);
 			
 			if (!res) {
 				g_fprintf (to_stream,
@@ -402,15 +406,15 @@
 				}
 			}
 			else {
-				display_result (data, res);
+				display_result (res);
 				if (res->type == GDA_INTERNAL_COMMAND_RESULT_EXIT) {
 					gda_internal_command_exec_result_free (res);
 					goto exit;
 				}
 				gda_internal_command_exec_result_free (res);
 			}
-			g_string_free (data->partial_command, TRUE);
-			data->partial_command = NULL;
+			g_string_free (main_data->partial_command, TRUE);
+			main_data->partial_command = NULL;
 			
 			/* disable SIGINT handling */
 			sigint_handler_status = SIGINT_HANDLER_DISABLED;
@@ -426,18 +430,55 @@
 }
 
 static void
-display_result (MainData *data, GdaInternalCommandResult *res)
+display_result (GdaInternalCommandResult *res)
 {
 	switch (res->type) {
-	case GDA_INTERNAL_COMMAND_RESULT_DATA_MODEL:
-		output_data_model (data, res->u.model);
+	case GDA_INTERNAL_COMMAND_RESULT_TXT_STDOUT: 
+		g_print ("%s", res->u.txt->str);
+		if (res->u.txt->str [strlen (res->u.txt->str) - 1] != '\n')
+			g_print ("\n");
+		fflush (NULL);
 		break;
+	case GDA_INTERNAL_COMMAND_RESULT_EMPTY:
+		break;
+	case GDA_INTERNAL_COMMAND_RESULT_MULTIPLE: {
+		GSList *list;
+		for (list = res->u.multiple_results; list; list = list->next)
+			display_result ((GdaInternalCommandResult *) list->data);
+		break;
+	}
+	case GDA_INTERNAL_COMMAND_RESULT_EXIT:
+		break;
+	default: {
+		gchar *str;
+		str = result_to_string (NULL, res);
+		output_string (str);
+		g_free (str);
+	}
+	}
+}
+
+static gchar *
+result_to_string (SqlConsole *console, GdaInternalCommandResult *res)
+{
+	OutputFormat of;
+	if (console)
+		of = console->output_format;
+	else
+		of = main_data->output_format;
+
+	switch (res->type) {
+	case GDA_INTERNAL_COMMAND_RESULT_DATA_MODEL:
+		return data_model_to_string (console, res->u.model);
+
 	case GDA_INTERNAL_COMMAND_RESULT_SET: {
 		GSList *list;
 		GString *string;
 		xmlNodePtr node;
 		xmlBufferPtr buffer;
-		switch (data->output_format) {
+		gchar *str;
+
+		switch (of) {
 		case OUTPUT_FORMAT_DEFAULT:
 			string = g_string_new ("");
 			for (list = res->u.set->holders; list; list = list->next) {
@@ -449,9 +490,10 @@
 							gda_holder_get_id (GDA_HOLDER (list->data)), str);
 				g_free (str);
 			}
-			output_string (data, string->str);
-			g_string_free (string, TRUE);
-			break;
+			str = string->str;
+			g_string_free (string, FALSE);
+			return str;
+
 		case OUTPUT_FORMAT_XML: {
 			buffer = xmlBufferCreate ();
 			node = xmlNewNode (NULL, BAD_CAST "parameters");
@@ -468,10 +510,10 @@
 				xmlAddChild (pnode, vnode);
 			}
 			xmlNodeDump (buffer, NULL, node, 0, 1);
-			output_string (data, (gchar *) xmlBufferContent (buffer));
+			str = g_strdup ((gchar *) xmlBufferContent (buffer));
 			xmlBufferFree (buffer);
 			xmlFreeNode (node);
-			break;
+			return str;
 		}
 		case OUTPUT_FORMAT_HTML: {
 			buffer = xmlBufferCreate ();
@@ -489,10 +531,10 @@
 				xmlAddChild (pnode, vnode);
 			}
 			xmlNodeDump (buffer, NULL, node, 0, 1);
-			output_string (data, (gchar *) xmlBufferContent (buffer));
+			str = g_strdup ((gchar *) xmlBufferContent (buffer));
 			xmlBufferFree (buffer);
 			xmlFreeNode (node);
-			break;
+			return str;
 		}
 		case OUTPUT_FORMAT_CSV: 
 			string = g_string_new ("");
@@ -505,66 +547,83 @@
 							gda_holder_get_id (GDA_HOLDER (list->data)), str);
 				g_free (str);
 			}
-			output_string (data, string->str);
-			g_string_free (string, TRUE);
-			break;
+			str = string->str;
+			g_string_free (string, FALSE);
+			return str;
 		default:
 			TO_IMPLEMENT;
-			break;
+			return NULL;
 		}
 		break;
 	}
 	case GDA_INTERNAL_COMMAND_RESULT_TXT: {
 		xmlNodePtr node;
 		xmlBufferPtr buffer;
-		switch (data->output_format) {
+		gchar *str;
+
+		switch (of) {
 		case OUTPUT_FORMAT_DEFAULT:
 		case OUTPUT_FORMAT_CSV:
-			output_string (data, res->u.txt->str);
-			break;
-		case OUTPUT_FORMAT_XML:
+			return g_strdup (res->u.txt->str);
+
+		case OUTPUT_FORMAT_XML: 
 			buffer = xmlBufferCreate ();
 			node = xmlNewNode (NULL, BAD_CAST "txt");
 			xmlNodeSetContent (node, BAD_CAST res->u.txt->str);
 			xmlNodeDump (buffer, NULL, node, 0, 1);
-			output_string (data, (gchar *) xmlBufferContent (buffer));
+			str = g_strdup ((gchar *) xmlBufferContent (buffer));
 			xmlBufferFree (buffer);
 			xmlFreeNode (node);
-			break;
+			return str;
+
 		case OUTPUT_FORMAT_HTML: 
 			buffer = xmlBufferCreate ();
 			node = xmlNewNode (NULL, BAD_CAST "p");
 			xmlNodeSetContent (node, BAD_CAST res->u.txt->str);
 			xmlNodeDump (buffer, NULL, node, 0, 1);
-			output_string (data, (gchar *) xmlBufferContent (buffer));
+			str = g_strdup ((gchar *) xmlBufferContent (buffer));
 			xmlBufferFree (buffer);
 			xmlFreeNode (node);
-			break;
+			return str;
 		default:
 			TO_IMPLEMENT;
-			break;
+			return NULL;
 		}
 		break;
 	}
-	case GDA_INTERNAL_COMMAND_RESULT_TXT_STDOUT: 
-		g_print ("%s", res->u.txt->str);
-		if (res->u.txt->str [strlen (res->u.txt->str) - 1] != '\n')
-			g_print ("\n");
-		fflush (NULL);
-		break;
+
 	case GDA_INTERNAL_COMMAND_RESULT_EMPTY:
-		break;
+		return g_strdup ("");
+
 	case GDA_INTERNAL_COMMAND_RESULT_MULTIPLE: {
 		GSList *list;
-		for (list = res->u.multiple_results; list; list = list->next)
-			display_result (data, (GdaInternalCommandResult *) list->data);
-		break;
+		GString *string = NULL;
+		gchar *str;
+
+		for (list = res->u.multiple_results; list; list = list->next) {
+			GdaInternalCommandResult *tres = (GdaInternalCommandResult*) list->data;
+			gchar *tmp;
+			
+			tmp = result_to_string (console, tres);
+			if (!string)
+				string = g_string_new (tmp);
+			else {
+				g_string_append_c (string, '\n');
+				g_string_append (string, tmp);
+			}
+			g_free (tmp);
+		}
+		if (string) {
+			str = string->str;
+			g_string_free (string, FALSE);
+		}
+		else
+			str = g_strdup ("");
+		return str;
 	}
-	case GDA_INTERNAL_COMMAND_RESULT_EXIT:
-		break;
+	
 	default:
-		TO_IMPLEMENT;
-		break;
+		return NULL;
 	}
 }
 
@@ -595,7 +654,7 @@
 			main_data->partial_command = NULL;
 		}
 		/* show a new prompt */
-		compute_prompt (main_data, prompt, main_data->partial_command == NULL ? FALSE : TRUE);
+		compute_prompt (NULL, prompt, main_data->partial_command == NULL ? FALSE : TRUE);
 		g_print ("\n%s", prompt->str);
 		fflush (NULL);
 	}
@@ -641,18 +700,22 @@
 /*
  * command_execute
  */
-static GdaInternalCommandResult *execute_external_command (MainData *data, const gchar *command, GError **error);
+static GdaInternalCommandResult *execute_internal_command (SqlConsole *console, GdaConnection *cnc,
+							   const gchar *command_str, GError **error);
+static GdaInternalCommandResult *execute_external_command (SqlConsole *console, const gchar *command, GError **error);
 static GdaInternalCommandResult *
-command_execute (MainData *data, const gchar *command, GError **error)
+command_execute (SqlConsole *console, const gchar *command, GError **error)
 {
+	ConnectionSetting *cs;
+
+	cs = get_current_connection_settings (console);
 	if (!command || !(*command))
                 return NULL;
         if ((*command == '\\') || (*command == '.')) {
-                if (data->current)
-                        return gda_internal_command_execute (data->internal_commands,
-                                                             data->current->cnc, command, error);
+                if (cs)
+                        return execute_internal_command (console, cs->cnc, command, error);
                 else
-                        return gda_internal_command_execute (data->internal_commands, NULL, command, error);
+                        return execute_internal_command (console, NULL, command, error);
         }
 	else if (*command == '#') {
 		/* nothing to do */
@@ -662,28 +725,180 @@
 		return res;
 	}
         else {
-                if (!data->current) {
+                if (!cs) {
                         g_set_error (error, 0, 0, "%s", 
                                      _("No connection specified"));
                         return NULL;
                 }
-                if (!gda_connection_is_opened (data->current->cnc)) {
+                if (!gda_connection_is_opened (cs->cnc)) {
                         g_set_error (error, 0, 0, "%s", 
                                      _("Connection closed"));
                         return NULL;
                 }
 
-                return execute_external_command (data, command, error);
+                return execute_external_command (console, command, error);
         }
 }
 
+static gint
+commands_compare_name (GdaInternalCommand *a, GdaInternalCommand *b)
+{
+	gint cmp, alength, blength;
+	if (!a->name || !b->name) {
+		g_warning (_("Invalid unnamed command"));
+		if (!a->name) {
+			if (b->name)
+				return 1;
+			else
+				return 0;
+		}
+		else
+			return -1;
+	}
+	alength = strlen (a->name);
+	blength = strlen (b->name);
+	cmp = strncmp (a->name, b->name, MIN (alength, blength));
+	if (cmp == 0) 
+		return blength - alength;
+	else
+		return cmp;
+}
+
+
+static gint
+commands_compare_group (GdaInternalCommand *a, GdaInternalCommand *b)
+{
+	if (!a->group) {
+		if (b->group)
+			return 1;
+		else
+			return 0;
+	}
+	else {
+		if (b->group) {
+			gint cmp = strcmp (a->group, b->group);
+			if (cmp)
+				return cmp;
+			else 
+				return commands_compare_name (a, b);
+		}
+		else
+			return -1;
+	}
+}
+
+
+static GdaInternalCommand *
+find_command (GdaInternalCommandsList *commands_list, const gchar *command_str, gboolean *command_complete)
+{
+	GdaInternalCommand *command = NULL;
+	GSList *list;
+	gint length;
+
+	if (!command_str || ((*command_str != '\\') && (*command_str != '.')))
+		return NULL;
+
+	length = strlen (command_str + 1);
+	for (list = commands_list->name_ordered; list; list = list->next) {
+		command = (GdaInternalCommand*) list->data;
+		if (!strncmp (command->name, command_str + 1, MIN (length, strlen (command->name)))) {
+			gint l;
+			gchar *ptr;
+			for (ptr = command->name, l = 0; *ptr && (*ptr != ' '); ptr++, l++);
+				
+			if (length == l)
+				break;
+			else
+				command = NULL;
+		}
+		else
+			command = NULL;
+	}
+
+	/* FIXME */
+	if (command_complete)
+		*command_complete = TRUE;
+
+	return command;
+}
+
+
+/*
+ * execute_internal_command
+ *
+ * Executes an internal command (not SQL)
+ */
+GdaInternalCommandResult *
+execute_internal_command (SqlConsole *console, GdaConnection *cnc, const gchar *command_str, GError **error)
+{
+	GdaInternalCommand *command;
+	gboolean command_complete;
+	gchar **args;
+	GdaInternalCommandResult *res = NULL;
+	GdaInternalCommandsList *commands_list = main_data->internal_commands;
+
+	if (!commands_list->name_ordered) {
+		GSList *list;
+
+		for (list = commands_list->commands; list; list = list->next) {
+			commands_list->name_ordered = 
+				g_slist_insert_sorted (commands_list->name_ordered, list->data,
+						       (GCompareFunc) commands_compare_name);
+			commands_list->group_ordered = 
+				g_slist_insert_sorted (commands_list->group_ordered, list->data,
+						       (GCompareFunc) commands_compare_group);
+		}
+	}
+
+	args = g_strsplit (command_str, " ", 2);
+	command = find_command (commands_list, args[0], &command_complete);
+	g_strfreev (args);
+	args = NULL;
+	if (!command) {
+		g_set_error (error, 0, 0, "%s", 
+			     _("Unknown internal command"));
+		goto cleanup;
+	}
+
+	if (!command->command_func) {
+		g_set_error (error, 0, 0, "%s", 
+			     _("Internal command not correctly defined"));
+		goto cleanup;
+	}
+
+	if (!command_complete) {
+		g_set_error (error, 0, 0, "%s", 
+			     _("Incomplete internal command"));
+		goto cleanup;
+	}
+
+	if (command->arguments_delimiter_func)
+		args = command->arguments_delimiter_func (command_str);
+	else
+		args = default_gda_internal_commandargs_func (command_str);
+	if (command->unquote_args) {
+		gint i;
+		for (i = 1; args[i]; i++) 
+			gda_internal_command_arg_remove_quotes (args[i]);
+	}
+	res = command->command_func (console, cnc, (const gchar **) &(args[1]), 
+				     error, command->user_data);
+	
+ cleanup:
+	if (args)
+		g_strfreev (args);
+
+	return res;
+}
+
+
 /*
  * execute_external_command
  *
  * Executes an SQL statement as understood by the DBMS
  */
 static GdaInternalCommandResult *
-execute_external_command (MainData *data, const gchar *command, GError **error)
+execute_external_command (SqlConsole *console, const gchar *command, GError **error)
 {
 	GdaInternalCommandResult *res = NULL;
 	GdaBatch *batch;
@@ -692,8 +907,16 @@
 	GdaSet *params;
 	GObject *obj;
 	const gchar *remain = NULL;
+	ConnectionSetting *cs;
+
+	cs = get_current_connection_settings (console);
+	if (!cs) {
+		g_set_error (error, 0, 0, "%s", 
+			     _("No connection specified"));
+		return NULL;
+	}
 
-	batch = gda_sql_parser_parse_string_as_batch (data->current->parser, command, &remain, error);
+	batch = gda_sql_parser_parse_string_as_batch (cs->parser, command, &remain, error);
 	if (!batch)
 		return NULL;
 	if (remain) {
@@ -728,7 +951,7 @@
 		GSList *list;
 		for (list = params->holders; list; list = list->next) {
 			GdaHolder *h = GDA_HOLDER (list->data);
-			GdaHolder *h_in_data = g_hash_table_lookup (data->parameters, gda_holder_get_id (h));
+			GdaHolder *h_in_data = g_hash_table_lookup (main_data->parameters, gda_holder_get_id (h));
 			if (h_in_data) {
 				const GValue *cvalue;
 				GValue *value;
@@ -783,7 +1006,7 @@
 	}
 
 	res = g_new0 (GdaInternalCommandResult, 1);	
-	obj = gda_connection_statement_execute (data->current->cnc, stmt, params, 
+	obj = gda_connection_statement_execute (cs->cnc, stmt, params, 
 						GDA_STATEMENT_MODEL_RANDOM_ACCESS, NULL, error);
 	if (!obj) {
 		g_free (res);
@@ -810,15 +1033,34 @@
 	return res;
 }
 
+static ConnectionSetting *
+get_current_connection_settings (SqlConsole *console)
+{
+	if (console) {
+		if (console->current) {
+			if (g_slist_find (main_data->settings, console->current))
+				return console->current;
+			else
+				return NULL;
+		}
+		else
+			return NULL;
+	}
+	else
+		return main_data->current;
+}
+
 static void
-compute_prompt (MainData *data, GString *string, gboolean in_command)
+compute_prompt (SqlConsole *console, GString *string, gboolean in_command)
 {
-	gchar *prefix;
+	gchar *prefix = NULL;
+	ConnectionSetting *cs;
 	g_assert (string);
 
 	g_string_set_size (string, 0);
-	if (data->current)
-		prefix = data->current->name;
+	cs = get_current_connection_settings (console);
+	if (cs)
+		prefix = cs->name;
 	else
 		prefix = "gda";
 
@@ -838,19 +1080,19 @@
  * Change the output file, set to %NULL to be back on stdout
  */
 static gboolean
-set_output_file (MainData *data, const gchar *file, GError **error)
+set_output_file (const gchar *file, GError **error)
 {
-	if (data->output_stream) {
-		if (data->output_is_pipe) {
-			pclose (data->output_stream);
+	if (main_data->output_stream) {
+		if (main_data->output_is_pipe) {
+			pclose (main_data->output_stream);
 #ifndef G_OS_WIN32
 			signal (SIGPIPE, SIG_DFL);
 #endif
 		}
 		else
-			fclose (data->output_stream);
-		data->output_stream = NULL;
-		data->output_is_pipe = FALSE;
+			fclose (main_data->output_stream);
+		main_data->output_stream = NULL;
+		main_data->output_is_pipe = FALSE;
 	}
 
 	if (file) {
@@ -859,8 +1101,8 @@
 
 		if (*copy != '|') {
 			/* output to a file */
-			data->output_stream = g_fopen (copy, "w");
-			if (!data->output_stream) {
+			main_data->output_stream = g_fopen (copy, "w");
+			if (!main_data->output_stream) {
 				g_set_error (error, 0, 0,
 					     _("Can't open file '%s' for writing: %s\n"), 
 					     copy,
@@ -868,12 +1110,12 @@
 				g_free (copy);
 				return FALSE;
 			}
-			data->output_is_pipe = FALSE;
+			main_data->output_is_pipe = FALSE;
 		}
 		else {
 			/* output to a pipe */
-			data->output_stream = popen (copy+1, "w");
-			if (!data->output_stream) {
+			main_data->output_stream = popen (copy+1, "w");
+			if (!main_data->output_stream) {
 				g_set_error (error, 0, 0,
 					     _("Can't open pipe '%s': %s\n"), 
 					     copy,
@@ -884,7 +1126,7 @@
 #ifndef G_OS_WIN32
 			signal (SIGPIPE, SIG_IGN);
 #endif
-			data->output_is_pipe = TRUE;
+			main_data->output_is_pipe = TRUE;
 		}
 		g_free (copy);
 	}
@@ -896,23 +1138,23 @@
  * Change the input file, set to %NULL to be back on stdin
  */
 static gboolean
-set_input_file (MainData *data, const gchar *file, GError **error)
+set_input_file (const gchar *file, GError **error)
 {
-	if (data->input_stream) {
-		fclose (data->input_stream);
-		data->input_stream = NULL;
+	if (main_data->input_stream) {
+		fclose (main_data->input_stream);
+		main_data->input_stream = NULL;
 	}
 
 	if (file) {
 		if (*file == '~') {
 			gchar *tmp;
 			tmp = g_strdup_printf ("%s%s", g_get_home_dir (), file+1);
-			data->input_stream = g_fopen (tmp, "r");
+			main_data->input_stream = g_fopen (tmp, "r");
 			g_free (tmp);
 		}
 		else
-			data->input_stream = g_fopen (file, "r");
-		if (!data->input_stream) {
+			main_data->input_stream = g_fopen (file, "r");
+		if (!main_data->input_stream) {
 			g_set_error (error, 0, 0,
 				     _("Can't open file '%s' for reading: %s\n"), 
 				     file,
@@ -939,11 +1181,11 @@
 }
 
 static ConnectionSetting *
-find_connection_from_name (MainData *data, const gchar *name)
+find_connection_from_name (const gchar *name)
 {
 	ConnectionSetting *cs = NULL;
 	GSList *list;
-	for (list = data->settings; list; list = list->next) {
+	for (list = main_data->settings; list; list = list->next) {
 		if (!strcmp (name, ((ConnectionSetting *) list->data)->name)) {
 			cs = (ConnectionSetting *) list->data;
 			break;
@@ -953,7 +1195,6 @@
 }
 
 typedef struct {
-	MainData *data;
 	ConnectionSetting *cs;
 	gboolean cannot_lock;
 	gboolean result;
@@ -968,7 +1209,7 @@
  * Open a connection
  */
 static ConnectionSetting*
-open_connection (MainData *data, const gchar *cnc_name, const gchar *cnc_string, GError **error)
+open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_string, GError **error)
 {
 	GdaConnection *newcnc = NULL;
 	ConnectionSetting *cs = NULL;
@@ -1096,9 +1337,12 @@
 		cs->threader = NULL;
 		cs->meta_job_id = 0;
 
-		data->settings = g_slist_append (data->settings, cs);
-		data->current = cs;
-
+		main_data->settings = g_slist_append (main_data->settings, cs);
+		if (console)
+			console->current = cs;
+		else
+			main_data->current = cs;
+		
 		GdaMetaStore *store;
 		gboolean update_store = FALSE;
 
@@ -1126,7 +1370,6 @@
 				MetaUpdateData *thdata;
 				cs->threader = (GdaThreader*) gda_threader_new ();
 				thdata = g_new0 (MetaUpdateData, 1);
-				thdata->data = data;
 				thdata->cs = cs;
 				thdata->cannot_lock = FALSE;
 				cs->meta_job_id = gda_threader_start_thread (cs->threader, 
@@ -1136,7 +1379,7 @@
 									     (GdaThreaderFunc) thread_cancelled_cb_update_meta_store, 
 									     &lerror);
 				if (cs->meta_job_id == 0) {
-					if (!data->output_stream) 
+					if (!main_data->output_stream) 
 						g_print (_("Error getting meta data in background: %s\n"), 
 							 lerror && lerror->message ? lerror->message : _("No detail"));
 					if (lerror)
@@ -1144,21 +1387,21 @@
 				}
 			}
 			else {
-				if (!data->output_stream) {
+				if (!main_data->output_stream) {
 					g_print (_("Getting database schema information for connection '%s', this may take some time... "),
 						 cs->name);
 					fflush (stdout);
 				}
 				
 				if (!gda_connection_update_meta_store (cs->cnc, NULL, &lerror)) {
-					if (!data->output_stream) 
+					if (!main_data->output_stream) 
 						g_print (_("error: %s\n"), 
 							 lerror && lerror->message ? lerror->message : _("No detail"));
 					if (lerror)
 						g_error_free (lerror);
 				}
 				else
-					if (!data->output_stream) 
+					if (!main_data->output_stream) 
 						g_print (_("Done.\n"));
 			}
 		}
@@ -1189,21 +1432,21 @@
 	data->cs->meta_job_id = 0;
 	if (data->cannot_lock) {
 		GError *lerror = NULL;
-		if (!data->data->output_stream) {
+		if (!main_data->output_stream) {
 			g_print (_("Getting database schema information for connection '%s', this may take some time... "),
 				 data->cs->name);
 			fflush (stdout);
 		}
 
 		if (!gda_connection_update_meta_store (data->cs->cnc, NULL, &lerror)) {
-			if (!data->data->output_stream) 
+			if (!main_data->output_stream) 
 				g_print (_("error: %s\n"), 
 					 lerror && lerror->message ? lerror->message : _("No detail"));
 			if (lerror)
 				g_error_free (lerror);
 		}
 		else
-			if (!data->data->output_stream) 
+			if (!main_data->output_stream) 
 				g_print (_("Done.\n"));
 	}
 	if (data->error)
@@ -1244,36 +1487,44 @@
  * Dumps the data model contents onto @data->output
  */
 static void
-output_data_model (MainData *data, GdaDataModel *model)
+output_data_model (GdaDataModel *model)
 {
 	gchar *str;
+	str = data_model_to_string (NULL, model);
+	output_string (str);
+	g_free (str);
+}
+
+static gchar *
+data_model_to_string (SqlConsole *console, GdaDataModel *model)
+{
 	static gboolean env_set = FALSE;
+	OutputFormat of;
 
 	if (!env_set) {
 		g_setenv ("GDA_DATA_MODEL_DUMP_TITLE", "Yes", TRUE);
 		g_setenv ("GDA_DATA_MODEL_NULL_AS_EMPTY", "Yes", TRUE);
 		env_set = TRUE;
 	}
+	
+	if (console)
+		of = console->output_format;
+	else
+		of = main_data->output_format;
 
-	switch (data->output_format) {
+	switch (of) {
 	case OUTPUT_FORMAT_DEFAULT:
-		str = gda_data_model_dump_as_string (model);
-		output_string (data, str);
-		g_free (str);
+		return gda_data_model_dump_as_string (model);
 		break;
 	case OUTPUT_FORMAT_XML:
-		str = gda_data_model_export_to_string (model, GDA_DATA_MODEL_IO_DATA_ARRAY_XML,
-						       NULL, 0,
-						       NULL, 0, NULL);
-		output_string (data, str);
-		g_free (str);
+		return gda_data_model_export_to_string (model, GDA_DATA_MODEL_IO_DATA_ARRAY_XML,
+							NULL, 0,
+							NULL, 0, NULL);
 		break;
 	case OUTPUT_FORMAT_CSV:
-		str = gda_data_model_export_to_string (model, GDA_DATA_MODEL_IO_TEXT_SEPARATED,
-						       NULL, 0,
-						       NULL, 0, NULL);
-		output_string (data, str);
-		g_free (str);
+		return gda_data_model_export_to_string (model, GDA_DATA_MODEL_IO_TEXT_SEPARATED,
+							NULL, 0,
+							NULL, 0, NULL);
 		break;
 	case OUTPUT_FORMAT_HTML: {
 		xmlBufferPtr buffer;
@@ -1322,7 +1573,7 @@
 		}
 
 		node = xmlNewChild (html, NULL, BAD_CAST "p", NULL);
-		g_strdup_printf (ngettext ("(%d row)", "(%d rows)", nrows), nrows);
+		str = g_strdup_printf (ngettext ("(%d row)", "(%d rows)", nrows), nrows);
 		xmlNodeSetContent (node, BAD_CAST str);
 		g_free (str);
 
@@ -1330,19 +1581,20 @@
 
 		buffer = xmlBufferCreate ();
 		xmlNodeDump (buffer, NULL, html, 0, 1);
-		output_string (data, (gchar *) xmlBufferContent (buffer));
+		str = g_strdup ((gchar *) xmlBufferContent (buffer));
 		xmlBufferFree (buffer);
 		xmlFreeNode (html);
-
-		break;
+		return str;
 	}
 	default: 
+		TO_IMPLEMENT;
 		break;
 	}
+	return NULL;
 }
 
 static void
-output_string (MainData *data, const gchar *str)
+output_string (const gchar *str)
 {
 	FILE *to_stream;
 	gboolean append_nl = FALSE;
@@ -1364,8 +1616,8 @@
 	if (str[length - 1] != '\n')
 		append_nl = TRUE;
 
-	if (data && data->output_stream)
-		to_stream = data->output_stream;
+	if (main_data->output_stream)
+		to_stream = main_data->output_stream;
 	else
 		to_stream = stdout;
 
@@ -1402,7 +1654,7 @@
  * Lists all the sections in the config files (local to the user and global) in the index page
  */
 static GdaDataModel *
-list_all_dsn (MainData *data)
+list_all_dsn (void)
 {
 	return gda_config_list_dsn ();
 }
@@ -1411,7 +1663,7 @@
  * make a list of all the providers in the index page
  */
 static GdaDataModel *
-list_all_providers (MainData *data)
+list_all_providers (void)
 {
 	GdaDataModel *prov_list, *model;
 	gint i, nrows;
@@ -1456,79 +1708,110 @@
 static gchar **args_as_string_func (const gchar *str);
 static gchar **args_as_string_set (const gchar *str);
 
-static GdaInternalCommandResult *extra_command_copyright (GdaConnection *cnc, const gchar **args,
-							  GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_quit (GdaConnection *cnc, const gchar **args,
-						     GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_cd (GdaConnection *cnc, const gchar **args,
-						   GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_set_output (GdaConnection *cnc, const gchar **args,
-							   GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_set_output_format (GdaConnection *cnc, const gchar **args,
-								  GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_set_input (GdaConnection *cnc, const gchar **args,
-							  GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_echo (GdaConnection *cnc, const gchar **args,
-						     GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_qecho (GdaConnection *cnc, const gchar **args,
-						      GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_list_dsn (GdaConnection *cnc, const gchar **args,
-							 GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_create_dsn (GdaConnection *cnc, const gchar **args,
-							   GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_remove_dsn (GdaConnection *cnc, const gchar **args,
-							   GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_list_providers (GdaConnection *cnc, const gchar **args,
-							       GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_manage_cnc (GdaConnection *cnc, const gchar **args,
-							   GError **error, MainData *data);
-
-static GdaInternalCommandResult *extra_command_close_cnc (GdaConnection *cnc, const gchar **args,
-							  GError **error, MainData *data);
-
-static GdaInternalCommandResult *extra_command_bind_cnc (GdaConnection *cnc, const gchar **args,
-							 GError **error, MainData *data);
-
-static GdaInternalCommandResult *extra_command_edit_buffer (GdaConnection *cnc, const gchar **args,
-							    GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_reset_buffer (GdaConnection *cnc, const gchar **args,
-							     GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_show_buffer (GdaConnection *cnc, const gchar **args,
-							    GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_exec_buffer (GdaConnection *cnc, const gchar **args,
-							    GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_write_buffer (GdaConnection *cnc, const gchar **args,
-							     GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_query_buffer_to_dict (GdaConnection *cnc, const gchar **args,
-								     GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_query_buffer_from_dict (GdaConnection *cnc, const gchar **args,
-								       GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_query_buffer_list_dict (GdaConnection *cnc, const gchar **args,
-								       GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_query_buffer_delete_dict (GdaConnection *cnc, const gchar **args,
-									 GError **error, MainData *data);
-
-static GdaInternalCommandResult *extra_command_set (GdaConnection *cnc, const gchar **args,
-						    GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_unset (GdaConnection *cnc, const gchar **args,
-						      GError **error, MainData *data);
-
-static GdaInternalCommandResult *extra_command_graph (GdaConnection *cnc, const gchar **args,
-						      GError **error, MainData *data);
+static GdaInternalCommandResult *extra_command_copyright (SqlConsole *console, GdaConnection *cnc,
+							  const gchar **args,
+							  GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_quit (SqlConsole *console, GdaConnection *cnc,
+						     const gchar **args,
+						     GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_cd (SqlConsole *console, GdaConnection *cnc,
+						   const gchar **args,
+						   GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_set_output (SqlConsole *console, GdaConnection *cnc,
+							   const gchar **args,
+							   GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_set_output_format (SqlConsole *console, GdaConnection *cnc,
+								  const gchar **args,
+								  GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_set_input (SqlConsole *console, GdaConnection *cnc,
+							  const gchar **args,
+							  GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_echo (SqlConsole *console, GdaConnection *cnc,
+						     const gchar **args,
+						     GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_qecho (SqlConsole *console, GdaConnection *cnc,
+						      const gchar **args,
+						      GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_list_dsn (SqlConsole *console, GdaConnection *cnc,
+							 const gchar **args,
+							 GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_create_dsn (SqlConsole *console, GdaConnection *cnc,
+							   const gchar **args,
+							   GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_remove_dsn (SqlConsole *console, GdaConnection *cnc,
+							   const gchar **args,
+							   GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_list_providers (SqlConsole *console, GdaConnection *cnc,
+							       const gchar **args,
+							       GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_manage_cnc (SqlConsole *console, GdaConnection *cnc,
+							   const gchar **args,
+							   GError **error, gpointer data);
+
+static GdaInternalCommandResult *extra_command_close_cnc (SqlConsole *console, GdaConnection *cnc,
+							  const gchar **args,
+							  GError **error, gpointer data);
+
+static GdaInternalCommandResult *extra_command_bind_cnc (SqlConsole *console, GdaConnection *cnc,
+							 const gchar **args,
+							 GError **error, gpointer data);
+
+static GdaInternalCommandResult *extra_command_edit_buffer (SqlConsole *console, GdaConnection *cnc,
+							    const gchar **args,
+							    GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_reset_buffer (SqlConsole *console, GdaConnection *cnc,
+							     const gchar **args,
+							     GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_show_buffer (SqlConsole *console, GdaConnection *cnc,
+							    const gchar **args,
+							    GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_exec_buffer (SqlConsole *console, GdaConnection *cnc,
+							    const gchar **args,
+							    GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_write_buffer (SqlConsole *console, GdaConnection *cnc,
+							     const gchar **args,
+							     GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_query_buffer_to_dict (SqlConsole *console, GdaConnection *cnc,
+								     const gchar **args,
+								     GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_query_buffer_from_dict (SqlConsole *console, GdaConnection *cnc,
+								       const gchar **args,
+								       GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_query_buffer_list_dict (SqlConsole *console, GdaConnection *cnc,
+								       const gchar **args,
+								       GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_query_buffer_delete_dict (SqlConsole *console, GdaConnection *cnc,
+									 const gchar **args,
+									 GError **error, gpointer data);
+
+static GdaInternalCommandResult *extra_command_set (SqlConsole *console, GdaConnection *cnc,
+						    const gchar **args,
+						    GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_unset (SqlConsole *console, GdaConnection *cnc,
+						      const gchar **args,
+						      GError **error, gpointer data);
+
+static GdaInternalCommandResult *extra_command_graph (SqlConsole *console, GdaConnection *cnc,
+						      const gchar **args,
+						      GError **error, gpointer data);
 #ifdef HAVE_LIBSOUP
-static GdaInternalCommandResult *extra_command_httpd (GdaConnection *cnc, const gchar **args,
-						      GError **error, MainData *data);
+static GdaInternalCommandResult *extra_command_httpd (SqlConsole *console, GdaConnection *cnc,
+						      const gchar **args,
+						      GError **error, gpointer data);
 #endif
 
-static GdaInternalCommandResult *extra_command_lo_update (GdaConnection *cnc, const gchar **args,
-							  GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_export (GdaConnection *cnc, const gchar **args,
-						       GError **error, MainData *data);
-static GdaInternalCommandResult *extra_command_set2 (GdaConnection *cnc, const gchar **args,
-						     GError **error, MainData *data);
+static GdaInternalCommandResult *extra_command_lo_update (SqlConsole *console, GdaConnection *cnc,
+							  const gchar **args,
+							  GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_export (SqlConsole *console, GdaConnection *cnc,
+						       const gchar **args,
+						       GError **error, gpointer data);
+static GdaInternalCommandResult *extra_command_set2 (SqlConsole *console, GdaConnection *cnc,
+						     const gchar **args,
+						     GError **error, gpointer data);
 
 static GdaInternalCommandsList *
-build_internal_commands_list (MainData *data)
+build_internal_commands_list (void)
 {
 	GdaInternalCommandsList *commands = g_new0 (GdaInternalCommandsList, 1);
 	GdaInternalCommand *c;
@@ -1542,6 +1825,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1553,6 +1837,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1564,6 +1849,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = FALSE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1575,6 +1861,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = FALSE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1586,6 +1873,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = FALSE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1597,6 +1885,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = FALSE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1608,18 +1897,20 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = FALSE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 #ifdef HAVE_LIBSOUP
 	c = g_new0 (GdaInternalCommand, 1);
 	c->group = _("Information");
-	c->name = g_strdup_printf (_("%s [port]"), "http");
+	c->name = g_strdup_printf (_("%s [port [authentication token]]"), "http");
 	c->description = _("Start/stop embedded HTTP server (on given port or on 12345 by default)");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_httpd;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
-	c->unquote_args = FALSE;
+	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 #endif
 
@@ -1630,9 +1921,10 @@
 	c->description = _("Connect to another defined data source (DSN, see \\l)");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_manage_cnc;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1641,9 +1933,10 @@
 	c->description = _("Close a connection");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_close_cnc;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1652,9 +1945,10 @@
 	c->description = _("Bind several connections together into the CNC_NAME virtual connection");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc)extra_command_bind_cnc;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1663,9 +1957,10 @@
 	c->description = _("List all DSN (or named DSN's attributes)");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_list_dsn;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1674,9 +1969,10 @@
 	c->description = _("Create (or modify) a DSN");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_create_dsn;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1685,9 +1981,10 @@
 	c->description = _("Remove a DSN");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_remove_dsn;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1696,9 +1993,10 @@
 	c->description = _("List all installed database providers (or named one's attributes)");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_list_providers;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1707,9 +2005,10 @@
 	c->description = _("Execute commands from file");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_set_input;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1718,9 +2017,10 @@
 	c->description = _("Send output to a file or |pipe");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_set_output;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1729,9 +2029,10 @@
 	c->description = _("Send output to stdout");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_echo;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = args_as_string_func;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1740,9 +2041,10 @@
 	c->description = _("Send output to output stream");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_qecho;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = args_as_string_func;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1754,6 +2056,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1765,6 +2068,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1776,6 +2080,7 @@
 	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1784,9 +2089,10 @@
 	c->description = _("Edit the query buffer (or file) with external editor");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_edit_buffer;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1795,9 +2101,10 @@
 	c->description = _("Reset the query buffer (fill buffer with contents of file)");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_reset_buffer;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1806,9 +2113,10 @@
 	c->description = _("Show the contents of the query buffer");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_show_buffer;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1817,9 +2125,10 @@
 	c->description = _("Execute contents of query buffer, or named query buffer");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_exec_buffer;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1828,9 +2137,10 @@
 	c->description = _("Write query buffer to file");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_write_buffer;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1839,9 +2149,10 @@
 	c->description = _("Save query buffer to dictionary");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_query_buffer_to_dict;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1850,9 +2161,10 @@
 	c->description = _("Load query buffer from dictionary");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_query_buffer_from_dict;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1861,9 +2173,10 @@
 	c->description = _("Delete query buffer from dictionary");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_query_buffer_delete_dict;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1872,9 +2185,10 @@
 	c->description = _("List all saved query buffers in dictionary");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_query_buffer_list_dict;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1883,9 +2197,10 @@
 	c->description = _("Set or show internal parameter, or list all if no parameters");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_set;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = args_as_string_set;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1894,9 +2209,10 @@
 	c->description = _("Unset (delete) internal named parameter (or all parameters)");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_unset;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1905,9 +2221,10 @@
 	c->description = _("Set output format");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_set_output_format;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	/*
@@ -1917,7 +2234,7 @@
 	c->description = _("Import a blob into the database");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_lo_update;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
@@ -1929,9 +2246,10 @@
 	c->description = _("Export internal parameter or table's value to the FILE file");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_export;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	c = g_new0 (GdaInternalCommand, 1);
@@ -1940,9 +2258,10 @@
 	c->description = _("Set internal parameter as the contents of the FILE file or from an existing table's value");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_set2;
-	c->user_data = data;
+	c->user_data = NULL;
 	c->arguments_delimiter_func = NULL;
 	c->unquote_args = TRUE;
+	c->limit_to_main = TRUE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	/* comes last */
@@ -1954,16 +2273,25 @@
 	c->command_func = (GdaInternalCommandFunc) gda_internal_command_help;
 	c->user_data = commands;
 	c->arguments_delimiter_func = NULL;
+	c->limit_to_main = FALSE;
 	commands->commands = g_slist_prepend (commands->commands, c);
 
 	return commands;
 }
 
 static GdaInternalCommandResult *
-extra_command_set_output (GdaConnection *cnc, const gchar **args,
-			  GError **error, MainData *data)
+extra_command_set_output (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			  GError **error, gpointer data)
 {
-	if (set_output_file (data, args[0], error)) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+	if (set_output_file (args[0], error)) {
 		GdaInternalCommandResult *res;
 		
 		res = g_new0 (GdaInternalCommandResult, 1);
@@ -1975,25 +2303,34 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_set_output_format (GdaConnection *cnc, const gchar **args,
-				 GError **error, MainData *data)
+extra_command_set_output_format (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+				 GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	const gchar *format = NULL;
 
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
 	if (args[0] && *args[0])
 		format = args[0];
 	
-	data->output_format = OUTPUT_FORMAT_DEFAULT;
+	main_data->output_format = OUTPUT_FORMAT_DEFAULT;
 	if (format) {
 		if ((*format == 'X') || (*format == 'x'))
-			data->output_format = OUTPUT_FORMAT_XML;
+			main_data->output_format = OUTPUT_FORMAT_XML;
 		else if ((*format == 'H') || (*format == 'h'))
-			data->output_format = OUTPUT_FORMAT_HTML;
+			main_data->output_format = OUTPUT_FORMAT_HTML;
 		else if ((*format == 'D') || (*format == 'd'))
-			data->output_format = OUTPUT_FORMAT_DEFAULT;
+			main_data->output_format = OUTPUT_FORMAT_DEFAULT;
 		else if ((*format == 'C') || (*format == 'c'))
-			data->output_format = OUTPUT_FORMAT_CSV;
+			main_data->output_format = OUTPUT_FORMAT_CSV;
 		else {
 			g_set_error (error, 0, 0,
 				     _("Unknown output format: '%s', reset to default"), format);
@@ -2001,11 +2338,11 @@
 		}
 	}
 
-	if (!data->output_stream) {
+	if (!main_data->output_stream) {
 		res = g_new0 (GdaInternalCommandResult, 1);
 		res->type = GDA_INTERNAL_COMMAND_RESULT_TXT_STDOUT;
 		res->u.txt = g_string_new ("");
-		switch (data->output_format) {
+		switch (main_data->output_format) {
 		case OUTPUT_FORMAT_DEFAULT:
 			g_string_assign (res->u.txt, ("Output format is default\n"));
 			break;
@@ -2030,10 +2367,19 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_set_input (GdaConnection *cnc, const gchar **args,
-			 GError **error, MainData *data)
+extra_command_set_input (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			 GError **error, gpointer data)
 {
-	if (set_input_file (data, args[0], error)) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (set_input_file (args[0], error)) {
 		GdaInternalCommandResult *res;
 		
 		res = g_new0 (GdaInternalCommandResult, 1);
@@ -2045,8 +2391,8 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_echo (GdaConnection *cnc, const gchar **args,
-		    GError **error, MainData *data)
+extra_command_echo (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		    GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	
@@ -2059,10 +2405,19 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_qecho (GdaConnection *cnc, const gchar **args,
-		     GError **error, MainData *data)
+extra_command_qecho (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		     GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
+
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
 	
 	res = g_new0 (GdaInternalCommandResult, 1);
 	res->type = GDA_INTERNAL_COMMAND_RESULT_TXT;
@@ -2071,7 +2426,7 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_list_dsn (GdaConnection *cnc, const gchar **args, GError **error, MainData *data)
+extra_command_list_dsn (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	GdaDataModel *dsn_list, *model = NULL;
@@ -2180,7 +2535,7 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_create_dsn (GdaConnection *cnc, const gchar **args, GError **error, MainData *data)
+extra_command_create_dsn (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 	GdaDsnInfo newdsn;
@@ -2225,7 +2580,7 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_remove_dsn (GdaConnection *cnc, const gchar **args, GError **error, MainData *data)
+extra_command_remove_dsn (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	gint i;
@@ -2246,7 +2601,7 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_list_providers (GdaConnection *cnc, const gchar **args, GError **error, MainData *data)
+extra_command_list_providers (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	GdaDataModel *prov_list, *model = NULL;
@@ -2349,7 +2704,7 @@
 }
 
 static void
-vconnection_hub_foreach_cb (GdaConnection *cnc, const gchar *ns, GString *string)
+vconnection_hub_foreach_cb (SqlConsole *console, GdaConnection *cnc, const gchar *ns, GString *string)
 {
 	if (string->len > 0)
 		g_string_append_c (string, '\n');
@@ -2358,7 +2713,7 @@
 
 static 
 GdaInternalCommandResult *
-extra_command_manage_cnc (GdaConnection *cnc, const gchar **args, GError **error, MainData *data)
+extra_command_manage_cnc (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	/* arguments:
 	 * 0 = connection name
@@ -2372,7 +2727,7 @@
 			const gchar *user = NULL, *pass = NULL;
 			ConnectionSetting *cs;
 			
-			cs = find_connection_from_name (data, args[0]);
+			cs = find_connection_from_name (args[0]);
 			if (cs) {
 				g_set_error (error, 0, 0,
 					     _("A connection named '%s' already exists"), args[0]);
@@ -2384,7 +2739,7 @@
 				if (args[3])
 					pass = args[3];
 			}
-			cs = open_connection (data, args[0], args[1], error);
+			cs = open_connection (console, args[0], args[1], error);
 			if (cs) {
 				GdaInternalCommandResult *res;
 				
@@ -2399,52 +2754,64 @@
 			/* switch to another already opened connection */
 			ConnectionSetting *cs;
 
-			cs = find_connection_from_name (data, args[0]);
+			cs = find_connection_from_name (args[0]);
 			if (cs) {
 				GdaInternalCommandResult *res;
 				
 				res = g_new0 (GdaInternalCommandResult, 1);
 				res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
-				data->current = cs;
+				if (console)
+					console->current = cs;
+				else
+					main_data->current = cs;
 				return res;
 			}
 			else {
 				if (*args [0] == '~') {
+					/* find connection for which we want the meta store's connection */
 					if (*(args[0] + 1)) {
-						cs = find_connection_from_name (data, args[0] + 1);
+						cs = find_connection_from_name (args[0] + 1);
 						if (!cs) {
 							g_set_error (error, 0, 0,
 								     _("No connection named '%s' found"), args[0] + 1);
 							return NULL;
 						}
 					}
-					else if (!data->current) {
+					else if (console)
+						cs = console->current;
+					else
+						cs = main_data->current;
+
+					if (!cs) {
 						g_set_error (error, 0, 0, "%s", 
 							     _("No current connection"));
 						return NULL;
 					}
-					else {
-						if (* (data->current->name) == '~') 
-							cs = find_connection_from_name (data, data->current->name + 1);
-						if (!cs) {
-							gchar *tmp;
-							tmp = g_strdup_printf ("~%s", data->current->name);
-							cs = find_connection_from_name (data, tmp);
-							g_free (tmp);
-						}
-						if (cs) {
-							GdaInternalCommandResult *res;
-							
-							res = g_new0 (GdaInternalCommandResult, 1);
-							res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
-							data->current = cs;
-							return res;
-						}
 
-						cs = data->current;
+					/* find if requested connection already exists */
+					ConnectionSetting *ncs = NULL;
+					if (* (cs->name) == '~') 
+						ncs = find_connection_from_name (main_data->current->name + 1);
+					else {
+						gchar *tmp;
+						tmp = g_strdup_printf ("~%s", cs->name);
+						ncs = find_connection_from_name (tmp);
+						g_free (tmp);
+					}
+					if (ncs) {
+						GdaInternalCommandResult *res;
+						
+						res = g_new0 (GdaInternalCommandResult, 1);
+						res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+						if (console)
+							console->current = ncs;
+						else
+							main_data->current = ncs;
+						return res;
 					}
 
-					ConnectionSetting *ncs = g_new0 (ConnectionSetting, 1);
+					/* open a new connection */
+					ncs = g_new0 (ConnectionSetting, 1);
 					GdaMetaStore *store;
 					GdaInternalCommandResult *res;
 					
@@ -2459,24 +2826,27 @@
 					ncs->threader = NULL;
 					ncs->meta_job_id = 0;
 					
-					data->settings = g_slist_append (data->settings, ncs);
-					data->current = ncs;
+					main_data->settings = g_slist_append (main_data->settings, ncs);
+					if (console)
+						console->current = ncs;
+					else
+						main_data->current = ncs;
 
 					GError *lerror = NULL;
-					if (!data->output_stream) {
+					if (!main_data->output_stream) {
 						g_print (_("Getting database schema information, "
 							   "this may take some time... "));
 						fflush (stdout);
 					}
 					if (!gda_connection_update_meta_store (ncs->cnc, NULL, &lerror)) {
-						if (!data->output_stream) 
+						if (!main_data->output_stream) 
 							g_print (_("error: %s\n"), 
 								 lerror && lerror->message ? lerror->message : _("No detail"));
 						if (lerror)
 							g_error_free (lerror);
 					}
 					else
-						if (!data->output_stream) 
+						if (!main_data->output_stream) 
 							g_print (_("Done.\n"));
 					
 					res = g_new0 (GdaInternalCommandResult, 1);
@@ -2496,7 +2866,7 @@
 		GSList *list;
 		GdaInternalCommandResult *res;
 
-		if (! data->settings) {
+		if (! main_data->settings) {
 			res = g_new0 (GdaInternalCommandResult, 1);
 			res->type = GDA_INTERNAL_COMMAND_RESULT_TXT;
 			res->u.txt = g_string_new (_("No opened connection"));
@@ -2514,7 +2884,7 @@
 		gda_data_model_set_column_title (model, 3, _("Username"));
 		g_object_set_data (G_OBJECT (model), "name", _("List of opened connections"));
 
-		for (list = data->settings; list; list = list->next) {
+		for (list = main_data->settings; list; list = list->next) {
 			ConnectionSetting *cs = (ConnectionSetting *) list->data;
 			GValue *value;
 			gint row;
@@ -2572,11 +2942,11 @@
 
 static 
 GdaInternalCommandResult *
-extra_command_close_cnc (GdaConnection *cnc, const gchar **args, GError **error, MainData *data)
+extra_command_close_cnc (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
-	ConnectionSetting *cs;
+	ConnectionSetting *cs = NULL;
 	if (args[0] && *args[0]) {
-		cs = find_connection_from_name (data, args[0]);
+		cs = find_connection_from_name (args[0]);
 		if (!cs) {
 			g_set_error (error, 0, 0,
 				     _("No connection named '%s' found"), args[0]);
@@ -2584,22 +2954,37 @@
 		}
 	}
 	else {
-		/* close current connection */
-		if (! data->current) {
+		/* close @cnc */
+		GSList *list;
+		for (list = main_data->settings; list; list = list->next) {
+			if (((ConnectionSetting *) list->data)->cnc == cnc) {
+				cs = (ConnectionSetting *) list->data;
+				break;
+			}
+		}
+
+		if (! cs) {
 			g_set_error (error, 0, 0, "%s", 
-				     _("No currently opened connection"));
+				     _("No connection currently opened"));
 			return NULL;
 		}
-		cs = data->current;
 	}
 	
-	gint pos;
-	pos = g_slist_index (data->settings, cs);
-	data->current = g_slist_nth_data (data->settings, pos - 1);
-	if (! data->current)
-		data->current = g_slist_nth_data (data->settings, pos + 1);
-
-	data->settings = g_slist_remove (data->settings, cs);
+	if ((console && (console->current == cs)) ||
+	    (!console && (main_data->current == cs))) {
+		gint index;
+		ConnectionSetting *ncs = NULL;
+		index = g_slist_index (main_data->settings, cs);
+		if (index == 0)
+			ncs = g_slist_nth_data (main_data->settings, index + 1);
+		else
+			ncs = g_slist_nth_data (main_data->settings, index - 1);
+		if (console)
+			console->current = ncs;
+		else
+			main_data->current = ncs;
+	}
+	main_data->settings = g_slist_remove (main_data->settings, cs);
 	connection_settings_free (cs);
 
 	GdaInternalCommandResult *res;
@@ -2610,7 +2995,7 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_bind_cnc (GdaConnection *cnc, const gchar **args, GError **error, MainData *data)
+extra_command_bind_cnc (SqlConsole *console, GdaConnection *cnc, const gchar **args, GError **error, gpointer data)
 {
 	ConnectionSetting *cs = NULL;
 	gint i, nargs = g_strv_length ((gchar **) args);
@@ -2625,7 +3010,7 @@
 	}
 
 	/* check for connections existance */
-	cs = find_connection_from_name (data, args[0]);
+	cs = find_connection_from_name (args[0]);
 	if (cs) {
 		g_set_error (error, 0, 0,
 			     _("A connection named '%s' already exists"), args[0]);
@@ -2637,7 +3022,7 @@
 		return NULL;
 	}
 	for (i = 1; i < nargs; i++) {
-		cs = find_connection_from_name (data, args[i]);
+		cs = find_connection_from_name (args[i]);
 		if (!cs) {
 			g_set_error (error, 0, 0,
 				     _("No connection named '%s' found"), args[i]);
@@ -2659,7 +3044,7 @@
 	/* add existing connections to virtual connection */
 	string = g_string_new (_("Bound connections are as:"));
 	for (i = 1; i < nargs; i++) {
-		cs = find_connection_from_name (data, args[i]);
+		cs = find_connection_from_name (args[i]);
 		if (!gda_vconnection_hub_add (GDA_VCONNECTION_HUB (virtual), cs->cnc, args[i], error)) {
 			g_object_unref (virtual);
 			g_string_free (string, TRUE);
@@ -2676,8 +3061,11 @@
 	cs->threader = NULL;
 	cs->meta_job_id = 0;
 
-	data->settings = g_slist_append (data->settings, cs);
-	data->current = cs;
+	main_data->settings = g_slist_append (main_data->settings, cs);
+	if (console)
+		console->current = cs;
+	else
+		main_data->current = cs;
 
 	GdaInternalCommandResult *res;
 	
@@ -2688,8 +3076,8 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_copyright (GdaConnection *cnc, const gchar **args,
-			 GError **error, MainData *data)
+extra_command_copyright (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			 GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 	
@@ -2707,10 +3095,19 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_quit (GdaConnection *cnc, const gchar **args,
-		    GError **error, MainData *data)
+extra_command_quit (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		    GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
+
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
 	
 	res = g_new0 (GdaInternalCommandResult, 1);
 	res->type = GDA_INTERNAL_COMMAND_RESULT_EXIT;
@@ -2718,14 +3115,23 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_cd (GdaConnection *cnc, const gchar **args,
-		  GError **error, MainData *data)
+extra_command_cd (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		  GError **error, gpointer data)
 {
 	const gchar *dir = NULL;
 #define DIR_LENGTH 256
 	static char start_dir[DIR_LENGTH];
 	static gboolean init_done = FALSE;
 
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
 	if (!init_done) {
 		init_done = TRUE;
 		memset (start_dir, 0, DIR_LENGTH);
@@ -2777,8 +3183,8 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_edit_buffer (GdaConnection *cnc, const gchar **args,
-			   GError **error, MainData *data)
+extra_command_edit_buffer (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			   GError **error, gpointer data)
 {
 	gchar *filename = NULL;
 	static gchar *editor_name = NULL;
@@ -2786,13 +3192,22 @@
 	gint systemres;
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		goto end_of_command;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
 
 	if (args[0] && *args[0])
 		filename = (gchar *) args[0];
@@ -2802,8 +3217,8 @@
 		fd = g_file_open_tmp (NULL, &filename, error);
 		if (fd < 0)
 			goto end_of_command;
-		if (write (fd, data->current->query_buffer->str, 
-			   data->current->query_buffer->len) != data->current->query_buffer->len) {
+		if (write (fd, main_data->current->query_buffer->str, 
+			   main_data->current->query_buffer->len) != main_data->current->query_buffer->len) {
 			g_set_error (error, 0, 0,
 				     _("Could not write to temporary file '%s': %s"),
 				     filename, strerror (errno));
@@ -2855,7 +3270,7 @@
 			
 			if (!g_file_get_contents (filename, &str, NULL, error))
 				goto end_of_command;
-			g_string_assign (data->current->query_buffer, str);
+			g_string_assign (main_data->current->query_buffer, str);
 			g_free (str);
 		}
 	}
@@ -2875,20 +3290,29 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_reset_buffer (GdaConnection *cnc, const gchar **args,
-			    GError **error, MainData *data)
+extra_command_reset_buffer (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			    GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
 	else 
-		g_string_assign (data->current->query_buffer, "");
+		g_string_assign (main_data->current->query_buffer, "");
 
 	if (args[0]) {
 		const gchar *filename = NULL;
@@ -2898,7 +3322,7 @@
 		if (!g_file_get_contents (filename, &str, NULL, error))
 			return NULL;
 
-		g_string_assign (data->current->query_buffer, str);
+		g_string_assign (main_data->current->query_buffer, str);
 		g_free (str);
 	}
 	
@@ -2909,49 +3333,67 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_show_buffer (GdaConnection *cnc, const gchar **args,
-			   GError **error, MainData *data)
+extra_command_show_buffer (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			   GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
 	res = g_new0 (GdaInternalCommandResult, 1);
 	res->type = GDA_INTERNAL_COMMAND_RESULT_TXT;
-	res->u.txt = g_string_new (data->current->query_buffer->str);
+	res->u.txt = g_string_new (main_data->current->query_buffer->str);
 
 	return res;
 }
 
 static GdaInternalCommandResult *
-extra_command_exec_buffer (GdaConnection *cnc, const gchar **args,
-			   GError **error, MainData *data)
+extra_command_exec_buffer (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			   GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
 	if (args[0] && *args[0]) {
 		/* load named query buffer first */
-		res = extra_command_query_buffer_from_dict (cnc, args, error, data);
+		res = extra_command_query_buffer_from_dict (console, cnc, args, error, data);
 		if (!res)
 			return NULL;
 		gda_internal_command_exec_result_free (res);
 		res = NULL;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
-	if (*data->current->query_buffer->str != 0)
-		res = command_execute (data, data->current->query_buffer->str, error);
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
+	if (*main_data->current->query_buffer->str != 0)
+		res = command_execute (NULL, main_data->current->query_buffer->str, error);
 	else {
 		res = g_new0 (GdaInternalCommandResult, 1);
 		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
@@ -2961,23 +3403,32 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_write_buffer (GdaConnection *cnc, const gchar **args,
-			    GError **error, MainData *data)
+extra_command_write_buffer (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			    GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
 	if (!args[0]) 
 		g_set_error (error, 0, 0, "%s",  
 			     _("Missing FILE to write to"));
 	else {
-		if (g_file_set_contents (args[0], data->current->query_buffer->str, -1, error)) {
+		if (g_file_set_contents (args[0], main_data->current->query_buffer->str, -1, error)) {
 			res = g_new0 (GdaInternalCommandResult, 1);
 			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
 		}
@@ -3003,19 +3454,28 @@
 	"DELETE FROM " QUERY_BUFFERS_TABLE_NAME " WHERE name = ##name::string"
 
 static GdaInternalCommandResult *
-extra_command_query_buffer_list_dict (GdaConnection *cnc, const gchar **args,
-				      GError **error, MainData *data)
+extra_command_query_buffer_list_dict (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+				      GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
 	/* Meta store's init */
 	GdaMetaStore *mstore;
-	mstore = gda_connection_get_meta_store (data->current->cnc);
+	mstore = gda_connection_get_meta_store (main_data->current->cnc);
 	if (!gda_meta_store_schema_add_custom_object (mstore, QUERY_BUFFERS_TABLE_DESC, NULL)) {
 		g_set_error (error, 0, 0, "%s", 
 			     _("Can't initialize dictionary to store query buffers"));
@@ -3026,7 +3486,7 @@
 	static GdaStatement *sel_stmt = NULL;
 	GdaDataModel *model;
 	if (!sel_stmt) {
-		sel_stmt = gda_sql_parser_parse_string (data->current->parser, 
+		sel_stmt = gda_sql_parser_parse_string (main_data->current->parser, 
 							QUERY_BUFFERS_TABLE_SELECT, NULL, NULL);
 		g_assert (sel_stmt);
 	}
@@ -3047,19 +3507,28 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_query_buffer_to_dict (GdaConnection *cnc, const gchar **args,
-				    GError **error, MainData *data)
+extra_command_query_buffer_to_dict (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+				    GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
-	if (*data->current->query_buffer->str != 0) {
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
+	if (*main_data->current->query_buffer->str != 0) {
 		/* find a suitable name */
 		gchar *qname;
 		if (args[0] && *args[0]) 
@@ -3072,7 +3541,7 @@
 
 		/* Meta store's init */
 		GdaMetaStore *mstore;
-		mstore = gda_connection_get_meta_store (data->current->cnc);
+		mstore = gda_connection_get_meta_store (main_data->current->cnc);
 		if (!gda_meta_store_schema_add_custom_object (mstore, QUERY_BUFFERS_TABLE_DESC, NULL)) {
 			g_set_error (error, 0, 0, "%s", 
 				     _("Can't initialize dictionary to store query buffers"));
@@ -3084,14 +3553,14 @@
 		static GdaStatement *ins_stmt = NULL;
 		static GdaSet *ins_params = NULL;
 		if (!ins_stmt) {
-			ins_stmt = gda_sql_parser_parse_string (data->current->parser, 
+			ins_stmt = gda_sql_parser_parse_string (main_data->current->parser, 
 								QUERY_BUFFERS_TABLE_INSERT, NULL, NULL);
 			g_assert (ins_stmt);
 			g_assert (gda_statement_get_parameters (ins_stmt, &ins_params, NULL));
 		}
 
 		if (! gda_set_set_holder_value (ins_params, error, "name", qname) ||
-		    ! gda_set_set_holder_value (ins_params, error, "sql", data->current->query_buffer->str)) {
+		    ! gda_set_set_holder_value (ins_params, error, "sql", main_data->current->query_buffer->str)) {
 			g_free (qname);
 			return NULL;
 		}
@@ -3113,23 +3582,32 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_query_buffer_from_dict (GdaConnection *cnc, const gchar **args,
-				      GError **error, MainData *data)
+extra_command_query_buffer_from_dict (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+				      GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
 
 	if (args[0] && *args[0]) {
 		/* Meta store's init */
 		GdaMetaStore *mstore;
-		mstore = gda_connection_get_meta_store (data->current->cnc);
+		mstore = gda_connection_get_meta_store (main_data->current->cnc);
 		if (!gda_meta_store_schema_add_custom_object (mstore, QUERY_BUFFERS_TABLE_DESC, NULL)) {
 			g_set_error (error, 0, 0, "%s", 
 				     _("Can't initialize dictionary to store query buffers"));
@@ -3142,7 +3620,7 @@
 		GdaDataModel *model;
 		const GValue *cvalue;
 		if (!sel_stmt) {
-			sel_stmt = gda_sql_parser_parse_string (data->current->parser, 
+			sel_stmt = gda_sql_parser_parse_string (main_data->current->parser, 
 								QUERY_BUFFERS_TABLE_SELECT_ONE, NULL, NULL);
 			g_assert (sel_stmt);
 			g_assert (gda_statement_get_parameters (sel_stmt, &sel_params, NULL));
@@ -3159,7 +3637,7 @@
 		
 		if ((gda_data_model_get_n_rows (model) == 1) &&
 		    (cvalue = gda_data_model_get_value_at (model, 0, 0, NULL))) {
-			g_string_assign (data->current->query_buffer, g_value_get_string (cvalue));
+			g_string_assign (main_data->current->query_buffer, g_value_get_string (cvalue));
 			res = g_new0 (GdaInternalCommandResult, 1);
 			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
 		}
@@ -3176,23 +3654,32 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_query_buffer_delete_dict (GdaConnection *cnc, const gchar **args,
-					GError **error, MainData *data)
+extra_command_query_buffer_delete_dict (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+					GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
-	if (!data->current) {
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (!main_data->current) {
 		g_set_error (error, 0, 0, "%s", _("No connection opened"));
 		return NULL;
 	}
 
-	if (!data->current->query_buffer) 
-		data->current->query_buffer = g_string_new ("");
+	if (!main_data->current->query_buffer) 
+		main_data->current->query_buffer = g_string_new ("");
 
 	if (args[0] && *args[0]) {
 		/* Meta store's init */
 		GdaMetaStore *mstore;
-		mstore = gda_connection_get_meta_store (data->current->cnc);
+		mstore = gda_connection_get_meta_store (main_data->current->cnc);
 		if (!gda_meta_store_schema_add_custom_object (mstore, QUERY_BUFFERS_TABLE_DESC, NULL)) {
 			g_set_error (error, 0, 0, "%s", 
 				     _("Can't initialize dictionary to store query buffers"));
@@ -3203,7 +3690,7 @@
 		static GdaStatement *del_stmt = NULL;
 		static GdaSet *del_params = NULL;
 		if (!del_stmt) {
-			del_stmt = gda_sql_parser_parse_string (data->current->parser, 
+			del_stmt = gda_sql_parser_parse_string (main_data->current->parser, 
 								QUERY_BUFFERS_TABLE_DELETE, NULL, NULL);
 			g_assert (del_stmt);
 			g_assert (gda_statement_get_parameters (del_stmt, &del_params, NULL));
@@ -3229,8 +3716,8 @@
 
 static void foreach_param_set (const gchar *pname, GdaHolder *param, GdaDataModel *model);
 static GdaInternalCommandResult *
-extra_command_set (GdaConnection *cnc, const gchar **args,
-		   GError **error, MainData *data)
+extra_command_set (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		   GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 	const gchar *pname = NULL;
@@ -3243,7 +3730,7 @@
 	}
 
 	if (pname) {
-		GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+		GdaHolder *param = g_hash_table_lookup (main_data->parameters, pname);
 		if (param) {
 			if (value) {
 				/* set param's value */
@@ -3256,8 +3743,8 @@
 					GdaDataHandler *dh;
 					GValue *gvalue;
 
-					prov = gda_connection_get_provider (data->current->cnc);
-					dh = gda_server_provider_get_data_handler_g_type (prov, data->current->cnc,
+					prov = gda_connection_get_provider (cnc);
+					dh = gda_server_provider_get_data_handler_g_type (prov, cnc,
 											 gda_holder_get_g_type (param));
 					gvalue = gda_data_handler_get_value_from_str (dh, value, gda_holder_get_g_type (param));
 					if (! gda_holder_take_value (param, gvalue, error))
@@ -3280,7 +3767,7 @@
 				if (!strcmp (value, "_null_"))
 					value = NULL;
 				param = gda_holder_new_string (pname, value);
-				g_hash_table_insert (data->parameters, g_strdup (pname), param);
+				g_hash_table_insert (main_data->parameters, g_strdup (pname), param);
 				res = g_new0 (GdaInternalCommandResult, 1);
 				res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
 			}
@@ -3298,7 +3785,7 @@
 		gda_data_model_set_column_title (model, 0, _("Name"));
 		gda_data_model_set_column_title (model, 1, _("Value"));
 		g_object_set_data (G_OBJECT (model), "name", _("List of defined parameters"));
-		g_hash_table_foreach (data->parameters, (GHFunc) foreach_param_set, model);
+		g_hash_table_foreach (main_data->parameters, (GHFunc) foreach_param_set, model);
 		res = g_new0 (GdaInternalCommandResult, 1);
 		res->type = GDA_INTERNAL_COMMAND_RESULT_DATA_MODEL;
 		res->u.model = model;
@@ -3352,7 +3839,7 @@
 
 	GdaStatement *stmt;
 	const gchar *remain;
-	stmt = gda_sql_parser_parse_string (data->current->parser, sql, &remain, error);
+	stmt = gda_sql_parser_parse_string (main_data->current->parser, sql, &remain, error);
 	if (!stmt) {
 		g_free (sql);
 		return NULL;
@@ -3367,7 +3854,7 @@
 
 	/* execute statement */
 	GdaInternalCommandResult *tmpres;
-	tmpres = execute_external_command (data, sql, error);
+	tmpres = execute_external_command (NULL, sql, error);
 	g_free (sql);
 	if (!tmpres)
 		return NULL;
@@ -3393,8 +3880,8 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_set2 (GdaConnection *cnc, const gchar **args,
-		    GError **error, MainData *data)
+extra_command_set2 (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		    GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 	const gchar *pname = NULL;
@@ -3404,6 +3891,15 @@
         const gchar *row_cond = NULL;
 	gint whichargs = 0;
 
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
 	if (!cnc) {
                 g_set_error (error, 0, 0, "%s", _("No current connection"));
                 return NULL;
@@ -3438,13 +3934,13 @@
 		GdaDataModel *model = NULL;
 		value = get_table_value_at_cell (cnc, error, data, table, column, row_cond, &model);
 		if (value) {
-			GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+			GdaHolder *param = g_hash_table_lookup (main_data->parameters, pname);
 			if (param) 
-				g_hash_table_remove (data->parameters, pname);
+				g_hash_table_remove (main_data->parameters, pname);
 		
 			param = gda_holder_new (G_VALUE_TYPE (value));
 			g_assert (gda_holder_set_value (param, value, NULL));
-			g_hash_table_insert (data->parameters, g_strdup (pname), param);
+			g_hash_table_insert (main_data->parameters, g_strdup (pname), param);
 			res = g_new0 (GdaInternalCommandResult, 1);
 			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
 		}
@@ -3453,15 +3949,15 @@
 	}
 	else if (whichargs == 2) {
 		/* param from filename */
-		GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+		GdaHolder *param = g_hash_table_lookup (main_data->parameters, pname);
 		GValue *bvalue;
 		if (param) 
-			g_hash_table_remove (data->parameters, pname);
+			g_hash_table_remove (main_data->parameters, pname);
 		
 		param = gda_holder_new (GDA_TYPE_BLOB);
 		bvalue = gda_value_new_blob_from_file (filename);
 		g_assert (gda_holder_take_value (param, bvalue, NULL));
-		g_hash_table_insert (data->parameters, g_strdup (pname), param);
+		g_hash_table_insert (main_data->parameters, g_strdup (pname), param);
 		res = g_new0 (GdaInternalCommandResult, 1);
 		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
 	}
@@ -3473,8 +3969,8 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_export (GdaConnection *cnc, const gchar **args,
-		      GError **error, MainData *data)
+extra_command_export (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		      GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 
@@ -3485,6 +3981,15 @@
         const gchar *row_cond = NULL;
 	gint whichargs = 0;
 
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
 	if (!cnc) {
                 g_set_error (error, 0, 0, "%s", _("No current connection"));
                 return NULL;
@@ -3521,7 +4026,7 @@
 	if (whichargs == 1) 
 		value = get_table_value_at_cell (cnc, error, data, table, column, row_cond, &model);
 	else if (whichargs == 2) {
-		GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+		GdaHolder *param = g_hash_table_lookup (main_data->parameters, pname);
 		if (!pname) 
 			g_set_error (error, 0, 0,
 				     _("No parameter named '%s' defined"), pname);
@@ -3584,8 +4089,8 @@
 
 
 static GdaInternalCommandResult *
-extra_command_unset (GdaConnection *cnc, const gchar **args,
-		     GError **error, MainData *data)
+extra_command_unset (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		     GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
 	const gchar *pname = NULL;
@@ -3594,9 +4099,9 @@
 		pname = args[0];
 
 	if (pname) {
-		GdaHolder *param = g_hash_table_lookup (data->parameters, pname);
+		GdaHolder *param = g_hash_table_lookup (main_data->parameters, pname);
 		if (param) {
-			g_hash_table_remove (data->parameters, pname);
+			g_hash_table_remove (main_data->parameters, pname);
 			res = g_new0 (GdaInternalCommandResult, 1);
 			res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
 		}
@@ -3605,8 +4110,8 @@
 				     _("No parameter named '%s' defined"), pname);
 	}
 	else {
-		g_hash_table_destroy (data->parameters);
-		data->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+		g_hash_table_destroy (main_data->parameters);
+		main_data->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 		res = g_new0 (GdaInternalCommandResult, 1);
 		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
 	}
@@ -3695,12 +4200,21 @@
 }
 
 static GdaInternalCommandResult *
-extra_command_graph (GdaConnection *cnc, const gchar **args,
-		     GError **error, MainData *data)
+extra_command_graph (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		     GError **error, gpointer data)
 {
 	gchar *result;
 	GdaMetaStruct *mstruct;
 
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
 	if (!cnc) {
 		g_set_error (error, 0, 0, "%s", _("No current connection"));
 		return NULL;
@@ -3726,14 +4240,24 @@
 
 #ifdef HAVE_LIBSOUP
 static GdaInternalCommandResult *
-extra_command_httpd (GdaConnection *cnc, const gchar **args,
-		     GError **error, MainData *data)
+extra_command_httpd (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+		     GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res = NULL;
-	if (data->server) {
+
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
+	if (main_data->server) {
 		/* stop server */
-		g_object_unref (data->server);
-		data->server = NULL;
+		g_object_unref (main_data->server);
+		main_data->server = NULL;
 		res = g_new0 (GdaInternalCommandResult, 1);
 		res->type = GDA_INTERNAL_COMMAND_RESULT_TXT_STDOUT;
 		res->u.txt = g_string_new (_("HTTPD server stopped"));
@@ -3741,15 +4265,19 @@
 	else {
 		/* start new server */
 		gint port = 12345;
+		const gchar *auth_token = NULL;
 		if (args[0] && *args[0]) {
 			gchar *ptr;
 			port = (gint) strtol (args[0], &ptr, 10);
 			if (ptr && *ptr)
 				port = -1;
+			if (args[1] && *args[1]) {
+				auth_token = args[1];
+			}
 		}
 		if (port > 0) {
-			data->server = web_server_new (port);
-			if (!data->server) 
+			main_data->server = web_server_new (port, auth_token);
+			if (!main_data->server) 
 				g_set_error (error, 0, 0, "%s", 
 					     _("Could not start HTTPD server"));
 			else {
@@ -3769,8 +4297,8 @@
 
 #ifdef NONE
 static GdaInternalCommandResult *
-extra_command_lo_update (GdaConnection *cnc, const gchar **args,
-			 GError **error, MainData *data)
+extra_command_lo_update (SqlConsole *console, GdaConnection *cnc, const gchar **args,
+			 GError **error, gpointer data)
 {
 	GdaInternalCommandResult *res;
 
@@ -3779,6 +4307,15 @@
         const gchar *filename = NULL;
         const gchar *row_cond = NULL;
 
+	if (console) {
+		GdaInternalCommandResult *res;
+		
+		TO_IMPLEMENT;
+		res = g_new0 (GdaInternalCommandResult, 1);
+		res->type = GDA_INTERNAL_COMMAND_RESULT_EMPTY;
+		return res;
+	}
+
 	if (!cnc) {
                 g_set_error (error, 0, 0, "%s", _("No current connection"));
                 return NULL;
@@ -3831,7 +4368,7 @@
 
 	GdaStatement *stmt;
 	const gchar *remain;
-	stmt = gda_sql_parser_parse_string (data->current->parser, sql, &remain, error);
+	stmt = gda_sql_parser_parse_string (main_data->current->parser, sql, &remain, error);
 	g_free (sql);
 	if (!stmt)
 		return NULL;
@@ -3888,8 +4425,6 @@
 	return g_strsplit (str, " ", 3);
 }
 
-
-
 static char **
 completion_func (const char *text, int start, int end)
 {
@@ -3937,7 +4472,7 @@
 const ConnectionSetting *
 gda_sql_get_connection (const gchar *name)
 {
-	return find_connection_from_name (main_data, name);
+	return find_connection_from_name (name);
 }
 
 const ConnectionSetting *
@@ -3945,3 +4480,67 @@
 {
 	return main_data->current;
 }
+
+gchar *
+gda_sql_console_execute (SqlConsole *console, const gchar *command, GError **error)
+{
+	gchar *loc_cmde = NULL;
+	gchar *retstr = NULL;
+
+	loc_cmde = g_strdup (command);
+	g_strchug (loc_cmde);
+	if (*loc_cmde) {
+		if (command_is_complete (loc_cmde)) {
+			/* execute command */
+			GdaInternalCommandResult *res;
+			
+			res = command_execute (console, loc_cmde, error);
+			
+			if (res) {
+				retstr = result_to_string (console, res);
+				gda_internal_command_exec_result_free (res);
+			}
+		}
+		else {
+			g_set_error (error, 0, 0,
+				     _("Command is incomplete"));
+		}
+	}
+	g_free (loc_cmde);
+
+	return retstr;
+}
+
+SqlConsole *
+gda_sql_console_new (const gchar *id)
+{
+	SqlConsole *c;
+
+	c = g_new0 (SqlConsole, 1);
+	if (id)
+		c->id = g_strdup (id);
+	c->current = main_data->current;
+	return c;
+}
+
+void
+gda_sql_console_free (SqlConsole *console)
+{
+	g_free (console->id);
+	g_free (console);
+}
+
+gchar *
+gda_sql_console_compute_prompt (SqlConsole *console)
+{
+	GString *string;
+	gchar *str;
+
+	string = g_string_new ("");
+	compute_prompt (console, string, FALSE);
+
+	str = string->str;
+	g_string_free (string, FALSE);
+
+	return str;
+}

Modified: trunk/tools/gda-sql.h
==============================================================================
--- trunk/tools/gda-sql.h	(original)
+++ trunk/tools/gda-sql.h	Fri Dec 26 14:37:32 2008
@@ -40,10 +40,30 @@
 	guint          meta_job_id;
 } ConnectionSetting;
 
+typedef enum {
+	OUTPUT_FORMAT_DEFAULT = 0,
+	OUTPUT_FORMAT_HTML,
+	OUTPUT_FORMAT_XML,
+	OUTPUT_FORMAT_CSV
+} OutputFormat;
+
+typedef struct {
+	gchar *id;
+	ConnectionSetting *current;
+	OutputFormat output_format;
+	GTimeVal  last_time_used;
+} SqlConsole;
+
 const GSList            *gda_sql_get_all_connections (void);
 const ConnectionSetting *gda_sql_get_connection (const gchar *name);
 const ConnectionSetting *gda_sql_get_current_connection (void);
 
+SqlConsole              *gda_sql_console_new (const gchar *id);
+void                     gda_sql_console_free (SqlConsole *console);
+gchar                   *gda_sql_console_execute (SqlConsole *console, const gchar *command, GError **error);
+
+gchar                   *gda_sql_console_compute_prompt (SqlConsole *console);
+
 G_END_DECLS
 
 #endif

Added: trunk/tools/gda.css
==============================================================================
--- (empty file)
+++ trunk/tools/gda.css	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,287 @@
+/* General styles */
+body {
+    margin:0px;
+    padding:0px;
+    border:0px;
+    width:100%;
+    background-color: white;
+    font-family: sans-serif;
+    color: black;
+}
+
+a {
+    color:#0000ff;
+    border: 0px;
+}
+
+a:active {
+    color: #ff0000;
+}
+
+a:visited {
+    color: #551a8b;
+}
+
+h1, h2, h3 {
+    margin:.8em 0 .2em 0;
+    padding:0;
+}
+
+p {
+    margin:.4em 0 .8em 0;
+    padding:0;
+}
+
+img {
+    margin:10px 0 5px;
+}
+/* Header styles */
+#header {
+    background: #729FCF;
+    clear:both;
+    float:left;
+    width:100%;
+    font-size: 75%;
+    border-bottom:3px;
+}
+
+#header p,
+#header h1,
+#header h2 {
+    padding-top: 0px;
+    padding-bottom: 10px;
+    color: #eeeeec;
+    margin-left: 85px;
+}
+#header ul {
+    clear:left;
+    float:left;
+    width:100%;
+    list-style:none;
+    margin:10px 0 0 0;
+    padding:0;
+    font-size: 85%;
+}
+#header ul li {
+    display:inline;
+    list-style:none;
+    margin:0;
+    padding:0;
+}
+#header ul li a {
+    font-weight: bold;
+    display:block;
+    float:left;
+    margin:0 0 0 1px;
+    padding:3px 10px;
+    text-align:center;
+    background:#eee;
+    color: #FFFFFF;
+    text-decoration:none;
+    position:relative;
+    left:15px;
+    line-height:1.3em;
+}
+#header ul li a:hover {
+    background:#369;
+    color:#fff;
+}
+#header ul li a.active,
+#header ul li a.active:hover {
+    color:#fff;
+    background:#000;
+    font-weight:bold;
+}
+#header ul li a span {
+    display:block;
+}
+
+/* column container */
+.colmask {
+    position:relative;
+    clear:both;
+    float:left;
+    width:100%;			/* width of whole page */
+    overflow:hidden;
+}
+
+/* common column settings */
+.colright,
+.colmid,
+.colleft {
+    float:left;
+    width:100%;
+    position:relative;
+}
+.col1,
+.col2,
+.col3 {
+    float:left;
+    position:relative;
+    padding:0 0 1em 0;
+    overflow:hidden;
+}
+
+.col1 {
+    background-color: #fff;
+    color: #333;
+}
+
+.leftmenu .colleft {
+    right:86%;			/* right column width */
+    background: #2E3436;
+    color: #FFFFFF;
+}
+.leftmenu .col1 {
+    width:84%;			/* right column content width */
+    left:101%;			/* 100% plus left column left padding */
+}
+.leftmenu .col2 {
+    width:12%;			/* left column content width (column width minus left and right padding) */
+    left:2%;			/* (right column left and right padding) plus (left column left padding) */
+}
+
+.col2 ul {
+    font-weight: bold;
+    list-style: none;
+    padding: 0 10px 10px;
+    margin: 0 0 0 0;
+    font-size: 90%;
+}
+
+.col2 li a {
+    font-weight: normal;
+    color: #FFFFFF;
+    margin: 0 0 0 0;
+    padding: 0 10px;
+    text-decoration: none;
+    font-size: 80%;
+}
+
+.col1 h1 {
+    /*margin: 5em 0px .5em 0px;*/
+    font-size: 100%;
+    color: black;
+}
+
+.col1 h2 {
+    padding-left: 5px;
+    font-size: 80%;
+}
+
+.col1 ul {
+    font-weight: bold;
+    list-style: none;
+    padding: 0px 10px 10px;
+    margin: 0px 0px 0px 0px;
+    font-size: 90%;
+}
+
+.col1 li {
+    font-weight: normal;
+    margin: 0px 0px 0px 0px;
+    padding: 0px 10px;
+    text-decoration: none;
+    font-size: 80%;
+}
+
+div.clist {
+    padding: 0px;
+    overflow: hidden;
+}
+
+.clist ul {
+    float: left;
+    width: 100%;
+    margin: 0px;
+    margin-left: 10px;
+    padding: 0px;
+    list-style: none;
+}
+
+.clist li {
+    float: left;
+    width: 33%;
+    margin: 0px;
+    padding: 0px;
+    font-size: 90%;
+}
+
+.clist a {
+    font-weight: normal;
+    color: #050505;
+    margin: 0px 0px 0px 0px;
+    padding: 0px 0px 0px 0px;
+    text-decoration: none;
+}
+
+.clist br {
+    clear: both;
+}
+
+table.ctable
+{
+    font-weight: normal;
+    font-size: 90%;
+    width: 100%;
+    background-color: #fafafa;
+    border: 1px #6699CC solid;
+    border-collapse: collapse;
+    border-spacing: 0px;
+    margin-top: 0px;
+    margin-bottom: 5px;
+}
+
+.ctable th
+{
+    border-bottom: 2px solid #6699CC;
+    background-color: #729FCF;
+    text-align: center;
+    font-weight: bold;
+    color: #eeeeec;
+}
+
+.ctable td
+{
+    padding-left: 2px;
+    border-left: 1px dotted #729FCF;
+}
+
+.graph
+{
+    /*background: lightblue;*/
+    padding: 0px;
+}
+
+.graph img
+{
+    max-width: 100%;
+    height: auto;
+    border: 0px;
+}
+
+.pkey
+{
+    /*background: lightblue;*/
+    color: blue;
+    font-weight: bold;
+}
+.ccode
+{
+    /*background: lightblue;*/
+    padding-left: 5px;
+}
+
+
+/* Footer styles */
+#footer {
+    clear:both;
+    float:left;
+    width:100%;
+    border-top:1px solid #000;
+    font-size:60%;
+}
+#footer p {
+    padding:10px;
+    margin:0;
+}
\ No newline at end of file

Modified: trunk/tools/html-doc.c
==============================================================================
--- trunk/tools/html-doc.c	(original)
+++ trunk/tools/html-doc.c	Fri Dec 26 14:37:32 2008
@@ -5,59 +5,69 @@
 html_doc_new (const gchar *title)
 {
 	HtmlDoc *hdoc;
-	xmlNodePtr topnode, head, node, div, container;
+	xmlNodePtr topnode, head, node, div1, div2, div3;
 
 	hdoc = g_new0 (HtmlDoc, 1);
-	hdoc->doc = xmlNewDoc ("1.0");
-	topnode = xmlNewDocNode (hdoc->doc, NULL, "html", NULL);
+	hdoc->doc = xmlNewDoc (BAD_CAST "1.0");
+	topnode = xmlNewDocNode (hdoc->doc, NULL, BAD_CAST "html", NULL);
 	xmlDocSetRootElement (hdoc->doc, topnode);
 
 	/* head */
-	head = xmlNewChild (topnode, NULL, "head", NULL);
+	head = xmlNewChild (topnode, NULL, BAD_CAST "head", NULL);
+	hdoc->head = head;
 
-	node = xmlNewChild (head, NULL, "meta", NULL);
-	xmlSetProp(node, "http-equiv", (xmlChar*)"Content-Type");
-	xmlSetProp(node, "content", (xmlChar*)"text/html; charset=UTF-8");
-
-	node = xmlNewChild (head, NULL, "meta", NULL);
-	xmlSetProp(node, "http-equiv", (xmlChar*)"refresh");
-	xmlSetProp(node, "content", (xmlChar*)"30"); /* refresh the page every 30 seconds */
-
-	node = xmlNewChild (head, NULL, "title", title);
-	node = xmlNewChild (head, NULL, "link", NULL);
-	xmlSetProp(node, "href", (xmlChar*)"/gda.css");
-	xmlSetProp(node, "rel", (xmlChar*)"stylesheet");
-	xmlSetProp(node, "type", (xmlChar*)"text/css");
+	node = xmlNewChild (head, NULL, BAD_CAST "meta", BAD_CAST "");
+	xmlSetProp(node, BAD_CAST "http-equiv", BAD_CAST "Content-Type");
+	xmlSetProp(node, BAD_CAST "content", BAD_CAST "text/html; charset=UTF-8");
+
+	/*
+	node = xmlNewChild (head, NULL, BAD_CAST "meta", NULL);
+	xmlSetProp(node, BAD_CAST "http-equiv", BAD_CAST "refresh");
+	xmlSetProp(node, BAD_CAST "content", BAD_CAST "30");*/ /* refresh the page every 30 seconds */
+
+	node = xmlNewChild (head, NULL, BAD_CAST "title", BAD_CAST title);
+
+	node = xmlNewChild (head, NULL, BAD_CAST "link", BAD_CAST "");
+	xmlSetProp(node, BAD_CAST "href", BAD_CAST "/gda.css");
+	xmlSetProp(node, BAD_CAST "rel", BAD_CAST "stylesheet");
+	xmlSetProp(node, BAD_CAST "type", BAD_CAST "text/css");
+	xmlSetProp(node, BAD_CAST "media", BAD_CAST "screen");
+
+	node = xmlNewChild (head, NULL, BAD_CAST "link", BAD_CAST "");
+	xmlSetProp(node, BAD_CAST "href", BAD_CAST "/gda-print.css");
+	xmlSetProp(node, BAD_CAST "rel", BAD_CAST "stylesheet");
+	xmlSetProp(node, BAD_CAST "type", BAD_CAST "text/css");
+	xmlSetProp(node, BAD_CAST "media", BAD_CAST "print");
 
 	/* body */
-	node = xmlNewChild (topnode, NULL, "body", NULL);
+	node = xmlNewChild (topnode, NULL, BAD_CAST "body", NULL);
 	hdoc->body = node;
 
-	/* container */
-	container = xmlNewChild (hdoc->body, NULL, "div", NULL);
-	xmlSetProp(container, "id", (xmlChar*)"container");
-
 	/* top */
-	div = xmlNewChild (container, NULL, "div", NULL);
-	xmlSetProp (div, "id", (xmlChar*)"top");
-
-	xmlNewChild (div, NULL, "h1", title);
-	
-
-	/* leftnav */
-	div = xmlNewChild (container, NULL, "div", NULL);
-	xmlSetProp(div, "id", (xmlChar*)"leftnav");
-	hdoc->sidebar = div;
-
-	/* content */
-	div = xmlNewChild (container, NULL, "div", NULL);
-	xmlSetProp(div, "id", (xmlChar*)"content");
-	hdoc->content = div;
+	div1 = xmlNewChild (hdoc->body, NULL, BAD_CAST "div", NULL);
+	xmlSetProp (div1, BAD_CAST "id", BAD_CAST "header");
+	xmlNewChild (div1, NULL, BAD_CAST "h1", BAD_CAST title);
+
+	/* main part */
+	div1 = xmlNewChild (hdoc->body, NULL, BAD_CAST "div", NULL);
+	xmlSetProp (div1, BAD_CAST "class", BAD_CAST "colmask leftmenu");
+
+	div2 = xmlNewChild (div1, NULL, BAD_CAST "div", NULL);
+	xmlSetProp (div2, BAD_CAST "class", BAD_CAST "colleft");
+
+	div3 = xmlNewChild (div2, NULL, BAD_CAST "div", BAD_CAST "");
+	xmlSetProp (div3, BAD_CAST "class", BAD_CAST "col1");
+	hdoc->content = div3;
+
+	div3 = xmlNewChild (div2, NULL, BAD_CAST "div", BAD_CAST "");
+	xmlSetProp (div3, BAD_CAST "class", BAD_CAST "col2");
+	hdoc->sidebar = div3;
 
 	/* footer */
-	div = xmlNewChild (container, NULL, "div", NULL);
-	xmlSetProp(div, "id", (xmlChar*)"footer");
-	xmlNewChild (div, NULL, "p", NULL);
+	div1 = xmlNewChild (hdoc->body, NULL, BAD_CAST "div", NULL);
+	xmlSetProp (div1, BAD_CAST "id", BAD_CAST "footer");
+	xmlNewChild (div1, NULL, BAD_CAST "p", BAD_CAST _("Generated by the GDA SQL console"));
+	hdoc->footer = div1;
 
 	return hdoc;
 }
@@ -76,10 +86,14 @@
 	int size;
 	xmlNodePtr li, a, node;
 
-	node = xmlNewChild (hdoc->sidebar, NULL, "ul", "Misc");
-	li = xmlNewChild (node, NULL, "li", NULL);
-	a = xmlNewChild (li, NULL, "a", _("Help"));
-	xmlSetProp (a, "href", (xmlChar*)"/___help");
+	node = xmlNewChild (hdoc->sidebar, NULL, BAD_CAST "ul", BAD_CAST "Misc");
+	li = xmlNewChild (node, NULL, BAD_CAST "li", NULL);
+	a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST _("Console"));
+	xmlSetProp (a, BAD_CAST "href", BAD_CAST "/~console");
+
+	li = xmlNewChild (node, NULL, BAD_CAST "li", NULL);
+	a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST _("Help"));
+	xmlSetProp (a, BAD_CAST "href", BAD_CAST "/~help");
 
 	xmlDocDumpFormatMemory (hdoc->doc, &retval, &size, 1);
 	if (out_size)

Modified: trunk/tools/html-doc.h
==============================================================================
--- trunk/tools/html-doc.h	(original)
+++ trunk/tools/html-doc.h	Fri Dec 26 14:37:32 2008
@@ -3,10 +3,12 @@
 
 typedef struct {
 	xmlDocPtr   doc;
+	xmlNodePtr  head;
 	xmlNodePtr  body;
 
 	xmlNodePtr  sidebar;
 	xmlNodePtr  content;
+	xmlNodePtr  footer;
 } HtmlDoc;
 
 HtmlDoc  *html_doc_new       (const gchar *title);

Added: trunk/tools/irb.css
==============================================================================
--- (empty file)
+++ trunk/tools/irb.css	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,65 @@
+input.keyboard-selector-input {
+    position: fixed;
+    top: 0;
+    _position: absolute;
+    _top: expression(eval(document.body.scrollTop));
+    left: -300px;
+}
+
+/* irb terminal */
+#terminal {
+    background-color: #f2f2f0;
+    width: 100%;
+    height: 78%;
+    overflow: auto; 
+}
+#irb {
+    visibility: hidden;
+    padding: 4px; margin: 0;
+    font-family: monospace;
+    font-size: 14px;
+    line-height: 16px;
+    color: #204a87;
+    text-align: left;
+}
+#irb div {
+    margin: 0; padding: 0;
+}
+#irb div b {
+    background-color: #874a20;
+    color: #fedeac;
+}
+
+/* terminal escape colors */
+span.fore_black { color: #2e3436; }
+span.fore_dark_gray { color: #888a85; }
+span.fore_gray { color: #babdb6; }
+span.fore_white { color: #eeeeec; }
+span.fore_blue { color: #204a87; }
+span.fore_lt_blue { color: #729fcf; }
+span.fore_green { color: #788600; font-weight: bold; }
+span.fore_lt_green { color: #cbe134; }
+span.fore_cyan { color: #c4a000; } /* using cyan for yellows */
+span.fore_lt_cyan { color: #fc994f; }
+span.fore_red { color: #a40000; }
+span.fore_lt_red { color: #ef2929; font-weight: bold; }
+span.fore_purple { color: #5c3566; }
+span.fore_lt_purple { color: #ad7fa8; }
+span.fore_brown { color: #8f5972; }
+span.fore_lt_brown { color: #b9b9de; }
+span.back_black { background-color: #2e3436; }
+span.back_dark_gray { background-color: #888a85; }
+span.back_gray { background-color: #babdb6; }
+span.back_white { background-color: #eeeeec; }
+span.back_blue { background-color: #204a87; }
+span.back_lt_blue { background-color: #729fcf; }
+span.back_green { background-color: #788600; }
+span.back_lt_green { background-color: #cbe134; }
+span.back_cyan { background-color: #c4a000; } /* using cyan for yellows */
+span.back_lt_cyan { background-color: #fce94f; }
+span.back_red { background-color: #a40000; }
+span.back_lt_red { background-color: #ef2929; }
+span.back_purple { background-color: #5c3566; }
+span.back_lt_purple { background-color: #ad7fa8; }
+span.back_brown { background-color: #8f5902; }
+span.back_lt_brown { background-color: #b9b96e; }

Added: trunk/tools/irb.js
==============================================================================
--- (empty file)
+++ trunk/tools/irb.js	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,44 @@
+//
+// Copyright (c) 2008 why the lucky stiff
+// 
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+var allStretch;
+var defaultPage;
+
+//the main function, call to the effect object
+window.onload = function() {
+    window.irb = new MouseApp.Irb('#irb', {
+        rows: 25,
+	columns: 115,
+        name: 'IRB',
+        greeting: "%+r Interactive SQL console %-r\n use .? to get help",
+        ps: '\033[1;31mgda>\033[m',
+        user: 'guest',
+        host: 'tryruby',
+        irbUrl: '/~irb',
+	gdaid: 'none',
+        init: function () {
+			    
+        },
+    });
+}

Added: trunk/tools/jquery.js
==============================================================================
--- (empty file)
+++ trunk/tools/jquery.js	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,2028 @@
+/*
+ * jQuery 1.2.3 - New Wave Javascript
+ *
+ * Copyright (c) 2008 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $
+ * $Rev: 4663 $
+ */
+(function() {
+    if (window.jQuery) var _jQuery = window.jQuery;
+    var jQuery = window.jQuery = function(selector, context) {
+        return new jQuery.prototype.init(selector, context);
+    };
+    if (window.$) var _$ = window.$;
+    window.$ = jQuery;
+    var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/;
+    var isSimple = /^.[^:#\[\.]*$/;
+    jQuery.fn = jQuery.prototype = {
+        init: function(selector, context) {
+            selector = selector || document;
+            if (selector.nodeType) {
+                this[0] = selector;
+                this.length = 1;
+                return this;
+            } else if (typeof selector == "string") {
+                var match = quickExpr.exec(selector);
+                if (match && (match[1] || !context)) {
+                    if (match[1]) selector = jQuery.clean([match[1]], context);
+                    else {
+                        var elem = document.getElementById(match[3]);
+                        if (elem) if (elem.id != match[3]) return jQuery().find(selector);
+                        else {
+                            this[0] = elem;
+                            this.length = 1;
+                            return this;
+                        } else selector = [];
+                    }
+                } else return new jQuery(context).find(selector);
+            } else if (jQuery.isFunction(selector)) return new jQuery(document)[jQuery.fn.ready ? "ready": "load"](selector);
+            return this.setArray(selector.constructor == Array && selector || (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray(selector) || [selector]);
+        },
+        jquery: "1.2.3",
+        size: function() {
+            return this.length;
+        },
+        length: 0,
+        get: function(num) {
+            return num == undefined ? jQuery.makeArray(this) : this[num];
+        },
+        pushStack: function(elems) {
+            var ret = jQuery(elems);
+            ret.prevObject = this;
+            return ret;
+        },
+        setArray: function(elems) {
+            this.length = 0;
+            Array.prototype.push.apply(this, elems);
+            return this;
+        },
+        each: function(callback, args) {
+            return jQuery.each(this, callback, args);
+        },
+        index: function(elem) {
+            var ret = -1;
+            this.each(function(i) {
+                if (this == elem) ret = i;
+            });
+            return ret;
+        },
+        attr: function(name, value, type) {
+            var options = name;
+            if (name.constructor == String) if (value == undefined) return this.length && jQuery[type || "attr"](this[0], name) || undefined;
+            else {
+                options = {};
+                options[name] = value;
+            }
+            return this.each(function(i) {
+                for (name in options) jQuery.attr(type ? this.style: this, name, jQuery.prop(this, options[name], type, i, name));
+            });
+        },
+        css: function(key, value) {
+            if ((key == 'width' || key == 'height') && parseFloat(value) < 0) value = undefined;
+            return this.attr(key, value, "curCSS");
+        },
+        text: function(text) {
+            if (typeof text != "object" && text != null) return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(text));
+            var ret = "";
+            jQuery.each(text || this,
+            function() {
+                jQuery.each(this.childNodes,
+                function() {
+                    if (this.nodeType != 8) ret += this.nodeType != 1 ? this.nodeValue: jQuery.fn.text([this]);
+                });
+            });
+            return ret;
+        },
+        wrapAll: function(html) {
+            if (this[0]) jQuery(html, this[0].ownerDocument).clone().insertBefore(this[0]).map(function() {
+                var elem = this;
+                while (elem.firstChild) elem = elem.firstChild;
+                return elem;
+            }).append(this);
+            return this;
+        },
+        wrapInner: function(html) {
+            return this.each(function() {
+                jQuery(this).contents().wrapAll(html);
+            });
+        },
+        wrap: function(html) {
+            return this.each(function() {
+                jQuery(this).wrapAll(html);
+            });
+        },
+        append: function() {
+            return this.domManip(arguments, true, false,
+            function(elem) {
+                if (this.nodeType == 1) this.appendChild(elem);
+            });
+        },
+        prepend: function() {
+            return this.domManip(arguments, true, true,
+            function(elem) {
+                if (this.nodeType == 1) this.insertBefore(elem, this.firstChild);
+            });
+        },
+        before: function() {
+            return this.domManip(arguments, false, false,
+            function(elem) {
+                this.parentNode.insertBefore(elem, this);
+            });
+        },
+        after: function() {
+            return this.domManip(arguments, false, true,
+            function(elem) {
+                this.parentNode.insertBefore(elem, this.nextSibling);
+            });
+        },
+        end: function() {
+            return this.prevObject || jQuery([]);
+        },
+        find: function(selector) {
+            var elems = jQuery.map(this,
+            function(elem) {
+                return jQuery.find(selector, elem);
+            });
+            return this.pushStack(/[^+>] [^+>]/.test(selector) || selector.indexOf("..") > -1 ? jQuery.unique(elems) : elems);
+        },
+        clone: function(events) {
+            var ret = this.map(function() {
+                if (jQuery.browser.msie && !jQuery.isXMLDoc(this)) {
+                    var clone = this.cloneNode(true),
+                    container = document.createElement("div");
+                    container.appendChild(clone);
+                    return jQuery.clean([container.innerHTML])[0];
+                } else return this.cloneNode(true);
+            });
+            var clone = ret.find("*").andSelf().each(function() {
+                if (this[expando] != undefined) this[expando] = null;
+            });
+            if (events === true) this.find("*").andSelf().each(function(i) {
+                if (this.nodeType == 3) return;
+                var events = jQuery.data(this, "events");
+                for (var type in events) for (var handler in events[type]) jQuery.event.add(clone[i], type, events[type][handler], events[type][handler].data);
+            });
+            return ret;
+        },
+        filter: function(selector) {
+            return this.pushStack(jQuery.isFunction(selector) && jQuery.grep(this,
+            function(elem, i) {
+                return selector.call(elem, i);
+            }) || jQuery.multiFilter(selector, this));
+        },
+        not: function(selector) {
+            if (selector.constructor == String) if (isSimple.test(selector)) return this.pushStack(jQuery.multiFilter(selector, this, true));
+            else selector = jQuery.multiFilter(selector, this);
+            var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
+            return this.filter(function() {
+                return isArrayLike ? jQuery.inArray(this, selector) < 0 : this != selector;
+            });
+        },
+        add: function(selector) {
+            return ! selector ? this: this.pushStack(jQuery.merge(this.get(), selector.constructor == String ? jQuery(selector).get() : selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ? selector: [selector]));
+        },
+        is: function(selector) {
+            return selector ? jQuery.multiFilter(selector, this).length > 0 : false;
+        },
+        hasClass: function(selector) {
+            return this.is("." + selector);
+        },
+        val: function(value) {
+            if (value == undefined) {
+                if (this.length) {
+                    var elem = this[0];
+                    if (jQuery.nodeName(elem, "select")) {
+                        var index = elem.selectedIndex,
+                        values = [],
+                        options = elem.options,
+                        one = elem.type == "select-one";
+                        if (index < 0) return null;
+                        for (var i = one ? index: 0, max = one ? index + 1 : options.length; i < max; i++) {
+                            var option = options[i];
+                            if (option.selected) {
+                                value = jQuery.browser.msie && !option.attributes.value.specified ? option.text: option.value;
+                                if (one) return value;
+                                values.push(value);
+                            }
+                        }
+                        return values;
+                    } else return (this[0].value || "").replace(/\r/g, "");
+                }
+                return undefined;
+            }
+            return this.each(function() {
+                if (this.nodeType != 1) return;
+                if (value.constructor == Array && /radio|checkbox/.test(this.type)) this.checked = (jQuery.inArray(this.value, value) >= 0 || jQuery.inArray(this.name, value) >= 0);
+                else if (jQuery.nodeName(this, "select")) {
+                    var values = value.constructor == Array ? value: [value];
+                    jQuery("option", this).each(function() {
+                        this.selected = (jQuery.inArray(this.value, values) >= 0 || jQuery.inArray(this.text, values) >= 0);
+                    });
+                    if (!values.length) this.selectedIndex = -1;
+                } else this.value = value;
+            });
+        },
+        html: function(value) {
+            return value == undefined ? (this.length ? this[0].innerHTML: null) : this.empty().append(value);
+        },
+        replaceWith: function(value) {
+            return this.after(value).remove();
+        },
+        eq: function(i) {
+            return this.slice(i, i + 1);
+        },
+        slice: function() {
+            return this.pushStack(Array.prototype.slice.apply(this, arguments));
+        },
+        map: function(callback) {
+            return this.pushStack(jQuery.map(this,
+            function(elem, i) {
+                return callback.call(elem, i, elem);
+            }));
+        },
+        andSelf: function() {
+            return this.add(this.prevObject);
+        },
+        data: function(key, value) {
+            var parts = key.split(".");
+            parts[1] = parts[1] ? "." + parts[1] : "";
+            if (value == null) {
+                var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+                if (data == undefined && this.length) data = jQuery.data(this[0], key);
+                return data == null && parts[1] ? this.data(parts[0]) : data;
+            } else return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
+                jQuery.data(this, key, value);
+            });
+        },
+        removeData: function(key) {
+            return this.each(function() {
+                jQuery.removeData(this, key);
+            });
+        },
+        domManip: function(args, table, reverse, callback) {
+            var clone = this.length > 1,
+            elems;
+            return this.each(function() {
+                if (!elems) {
+                    elems = jQuery.clean(args, this.ownerDocument);
+                    if (reverse) elems.reverse();
+                }
+                var obj = this;
+                if (table && jQuery.nodeName(this, "table") && jQuery.nodeName(elems[0], "tr")) obj = this.getElementsByTagName("tbody")[0] || this.appendChild(this.ownerDocument.createElement("tbody"));
+                var scripts = jQuery([]);
+                jQuery.each(elems,
+                function() {
+                    var elem = clone ? jQuery(this).clone(true)[0] : this;
+                    if (jQuery.nodeName(elem, "script")) {
+                        scripts = scripts.add(elem);
+                    } else {
+                        if (elem.nodeType == 1) scripts = scripts.add(jQuery("script", elem).remove());
+                        callback.call(obj, elem);
+                    }
+                });
+                scripts.each(evalScript);
+            });
+        }
+    };
+    jQuery.prototype.init.prototype = jQuery.prototype;
+    function evalScript(i, elem) {
+        if (elem.src) jQuery.ajax({
+            url: elem.src,
+            async: false,
+            dataType: "script"
+        });
+        else jQuery.globalEval(elem.text || elem.textContent || elem.innerHTML || "");
+        if (elem.parentNode) elem.parentNode.removeChild(elem);
+    }
+    jQuery.extend = jQuery.fn.extend = function() {
+        var target = arguments[0] || {},
+        i = 1,
+        length = arguments.length,
+        deep = false,
+        options;
+        if (target.constructor == Boolean) {
+            deep = target;
+            target = arguments[1] || {};
+            i = 2;
+        }
+        if (typeof target != "object" && typeof target != "function") target = {};
+        if (length == 1) {
+            target = this;
+            i = 0;
+        }
+        for (; i < length; i++) if ((options = arguments[i]) != null) for (var name in options) {
+            if (target === options[name]) continue;
+            if (deep && options[name] && typeof options[name] == "object" && target[name] && !options[name].nodeType) target[name] = jQuery.extend(target[name], options[name]);
+            else if (options[name] != undefined) target[name] = options[name];
+        }
+        return target;
+    };
+    var expando = "jQuery" + (new Date()).getTime(),
+    uuid = 0,
+    windowData = {};
+    var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i;
+    jQuery.extend({
+        noConflict: function(deep) {
+            window.$ = _$;
+            if (deep) window.jQuery = _jQuery;
+            return jQuery;
+        },
+        isFunction: function(fn) {
+            return !! fn && typeof fn != "string" && !fn.nodeName && fn.constructor != Array && /function/i.test(fn + "");
+        },
+        isXMLDoc: function(elem) {
+            return elem.documentElement && !elem.body || elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+        },
+        globalEval: function(data) {
+            data = jQuery.trim(data);
+            if (data) {
+                var head = document.getElementsByTagName("head")[0] || document.documentElement,
+                script = document.createElement("script");
+                script.type = "text/javascript";
+                if (jQuery.browser.msie) script.text = data;
+                else script.appendChild(document.createTextNode(data));
+                head.appendChild(script);
+                head.removeChild(script);
+            }
+        },
+        nodeName: function(elem, name) {
+            return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase();
+        },
+        cache: {},
+        data: function(elem, name, data) {
+            elem = elem == window ? windowData: elem;
+            var id = elem[expando];
+            if (!id) id = elem[expando] = ++uuid;
+            if (name && !jQuery.cache[id]) jQuery.cache[id] = {};
+            if (data != undefined) jQuery.cache[id][name] = data;
+            return name ? jQuery.cache[id][name] : id;
+        },
+        removeData: function(elem, name) {
+            elem = elem == window ? windowData: elem;
+            var id = elem[expando];
+            if (name) {
+                if (jQuery.cache[id]) {
+                    delete jQuery.cache[id][name];
+                    name = "";
+                    for (name in jQuery.cache[id]) break;
+                    if (!name) jQuery.removeData(elem);
+                }
+            } else {
+                try {
+                    delete elem[expando];
+                } catch(e) {
+                    if (elem.removeAttribute) elem.removeAttribute(expando);
+                }
+                delete jQuery.cache[id];
+            }
+        },
+        each: function(object, callback, args) {
+            if (args) {
+                if (object.length == undefined) {
+                    for (var name in object) if (callback.apply(object[name], args) === false) break;
+                } else for (var i = 0,
+                length = object.length; i < length; i++) if (callback.apply(object[i], args) === false) break;
+            } else {
+                if (object.length == undefined) {
+                    for (var name in object) if (callback.call(object[name], name, object[name]) === false) break;
+                } else for (var i = 0,
+                length = object.length,
+                value = object[0]; i < length && callback.call(value, i, value) !== false; value = object[++i]) {}
+            }
+            return object;
+        },
+        prop: function(elem, value, type, i, name) {
+            if (jQuery.isFunction(value)) value = value.call(elem, i);
+            return value && value.constructor == Number && type == "curCSS" && !exclude.test(name) ? value + "px": value;
+        },
+        className: {
+            add: function(elem, classNames) {
+                jQuery.each((classNames || "").split(/\s+/),
+                function(i, className) {
+                    if (elem.nodeType == 1 && !jQuery.className.has(elem.className, className)) elem.className += (elem.className ? " ": "") + className;
+                });
+            },
+            remove: function(elem, classNames) {
+                if (elem.nodeType == 1) elem.className = classNames != undefined ? jQuery.grep(elem.className.split(/\s+/),
+                function(className) {
+                    return ! jQuery.className.has(classNames, className);
+                }).join(" ") : "";
+            },
+            has: function(elem, className) {
+                return jQuery.inArray(className, (elem.className || elem).toString().split(/\s+/)) > -1;
+            }
+        },
+        swap: function(elem, options, callback) {
+            var old = {};
+            for (var name in options) {
+                old[name] = elem.style[name];
+                elem.style[name] = options[name];
+            }
+            callback.call(elem);
+            for (var name in options) elem.style[name] = old[name];
+        },
+        css: function(elem, name, force) {
+            if (name == "width" || name == "height") {
+                var val, props = {
+                    position: "absolute",
+                    visibility: "hidden",
+                    display: "block"
+                },
+                which = name == "width" ? ["Left", "Right"] : ["Top", "Bottom"];
+                function getWH() {
+                    val = name == "width" ? elem.offsetWidth: elem.offsetHeight;
+                    var padding = 0,
+                    border = 0;
+                    jQuery.each(which,
+                    function() {
+                        padding += parseFloat(jQuery.curCSS(elem, "padding" + this, true)) || 0;
+                        border += parseFloat(jQuery.curCSS(elem, "border" + this + "Width", true)) || 0;
+                    });
+                    val -= Math.round(padding + border);
+                }
+                if (jQuery(elem).is(":visible")) getWH();
+                else jQuery.swap(elem, props, getWH);
+                return Math.max(0, val);
+            }
+            return jQuery.curCSS(elem, name, force);
+        },
+        curCSS: function(elem, name, force) {
+            var ret;
+            function color(elem) {
+                if (!jQuery.browser.safari) return false;
+                var ret = document.defaultView.getComputedStyle(elem, null);
+                return ! ret || ret.getPropertyValue("color") == "";
+            }
+            if (name == "opacity" && jQuery.browser.msie) {
+                ret = jQuery.attr(elem.style, "opacity");
+                return ret == "" ? "1": ret;
+            }
+            if (jQuery.browser.opera && name == "display") {
+                var save = elem.style.outline;
+                elem.style.outline = "0 solid black";
+                elem.style.outline = save;
+            }
+            if (name.match(/float/i)) name = styleFloat;
+            if (!force && elem.style && elem.style[name]) ret = elem.style[name];
+            else if (document.defaultView && document.defaultView.getComputedStyle) {
+                if (name.match(/float/i)) name = "float";
+                name = name.replace(/([A-Z])/g, "-$1").toLowerCase();
+                var getComputedStyle = document.defaultView.getComputedStyle(elem, null);
+                if (getComputedStyle && !color(elem)) ret = getComputedStyle.getPropertyValue(name);
+                else {
+                    var swap = [],
+                    stack = [];
+                    for (var a = elem; a && color(a); a = a.parentNode) stack.unshift(a);
+                    for (var i = 0; i < stack.length; i++) if (color(stack[i])) {
+                        swap[i] = stack[i].style.display;
+                        stack[i].style.display = "block";
+                    }
+                    ret = name == "display" && swap[stack.length - 1] != null ? "none": (getComputedStyle && getComputedStyle.getPropertyValue(name)) || "";
+                    for (var i = 0; i < swap.length; i++) if (swap[i] != null) stack[i].style.display = swap[i];
+                }
+                if (name == "opacity" && ret == "") ret = "1";
+            } else if (elem.currentStyle) {
+                var camelCase = name.replace(/\-(\w)/g,
+                function(all, letter) {
+                    return letter.toUpperCase();
+                });
+                ret = elem.currentStyle[name] || elem.currentStyle[camelCase];
+                if (!/^\d+(px)?$/i.test(ret) && /^\d/.test(ret)) {
+                    var style = elem.style.left,
+                    runtimeStyle = elem.runtimeStyle.left;
+                    elem.runtimeStyle.left = elem.currentStyle.left;
+                    elem.style.left = ret || 0;
+                    ret = elem.style.pixelLeft + "px";
+                    elem.style.left = style;
+                    elem.runtimeStyle.left = runtimeStyle;
+                }
+            }
+            return ret;
+        },
+        clean: function(elems, context) {
+            var ret = [];
+            context = context || document;
+            if (typeof context.createElement == 'undefined') context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
+            jQuery.each(elems,
+            function(i, elem) {
+                if (!elem) return;
+                if (elem.constructor == Number) elem = elem.toString();
+                if (typeof elem == "string") {
+                    elem = elem.replace(/(<(\w+)[^>]*?)\/>/g,
+                    function(all, front, tag) {
+                        return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? all: front + "></" + tag + ">";
+                    });
+                    var tags = jQuery.trim(elem).toLowerCase(),
+                    div = context.createElement("div");
+                    var wrap = !tags.indexOf("<opt") && [1, "<select multiple='multiple'>", "</select>"] || !tags.indexOf("<leg") && [1, "<fieldset>", "</fieldset>"] || tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && [1, "<table>", "</table>"] || !tags.indexOf("<tr") && [2, "<table><tbody>", "</tbody></table>"] || (!tags.indexOf("<td") || !tags.indexOf("<th")) && [3, "<table><tbody><tr>", "</tr></tbody></table>"] || !tags.indexOf("<col") && [2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"] || jQuery.browser.msie && [1, "div<div>", "</div>"] || [0, "", ""];
+                    div.innerHTML = wrap[1] + elem + wrap[2];
+                    while (wrap[0]--) div = div.lastChild;
+                    if (jQuery.browser.msie) {
+                        var tbody = !tags.indexOf("<table") && tags.indexOf("<tbody") < 0 ? div.firstChild && div.firstChild.childNodes: wrap[1] == "<table>" && tags.indexOf("<tbody") < 0 ? div.childNodes: [];
+                        for (var j = tbody.length - 1; j >= 0; --j) if (jQuery.nodeName(tbody[j], "tbody") && !tbody[j].childNodes.length) tbody[j].parentNode.removeChild(tbody[j]);
+                        if (/^\s/.test(elem)) div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]), div.firstChild);
+                    }
+                    elem = jQuery.makeArray(div.childNodes);
+                }
+                if (elem.length === 0 && (!jQuery.nodeName(elem, "form") && !jQuery.nodeName(elem, "select"))) return;
+                if (elem[0] == undefined || jQuery.nodeName(elem, "form") || elem.options) ret.push(elem);
+                else ret = jQuery.merge(ret, elem);
+            });
+            return ret;
+        },
+        attr: function(elem, name, value) {
+            if (!elem || elem.nodeType == 3 || elem.nodeType == 8) return undefined;
+            var fix = jQuery.isXMLDoc(elem) ? {}: jQuery.props;
+            if (name == "selected" && jQuery.browser.safari) elem.parentNode.selectedIndex;
+            if (fix[name]) {
+                if (value != undefined) elem[fix[name]] = value;
+                return elem[fix[name]];
+            } else if (jQuery.browser.msie && name == "style") return jQuery.attr(elem.style, "cssText", value);
+            else if (value == undefined && jQuery.browser.msie && jQuery.nodeName(elem, "form") && (name == "action" || name == "method")) return elem.getAttributeNode(name).nodeValue;
+            else if (elem.tagName) {
+                if (value != undefined) {
+                    if (name == "type" && jQuery.nodeName(elem, "input") && elem.parentNode) throw "type property can't be changed";
+                    elem.setAttribute(name, "" + value);
+                }
+                if (jQuery.browser.msie && /href|src/.test(name) && !jQuery.isXMLDoc(elem)) return elem.getAttribute(name, 2);
+                return elem.getAttribute(name);
+            } else {
+                if (name == "opacity" && jQuery.browser.msie) {
+                    if (value != undefined) {
+                        elem.zoom = 1;
+                        elem.filter = (elem.filter || "").replace(/alpha\([^)]*\)/, "") + (parseFloat(value).toString() == "NaN" ? "": "alpha(opacity=" + value * 100 + ")");
+                    }
+                    return elem.filter && elem.filter.indexOf("opacity=") >= 0 ? (parseFloat(elem.filter.match(/opacity=([^)]*)/)[1]) / 100).toString() : "";
+                }
+                name = name.replace(/-([a-z])/ig,
+                function(all, letter) {
+                    return letter.toUpperCase();
+                });
+                if (value != undefined) elem[name] = value;
+                return elem[name];
+            }
+        },
+        trim: function(text) {
+            return (text || "").replace(/^\s+|\s+$/g, "");
+        },
+        makeArray: function(array) {
+            var ret = [];
+            if (typeof array != "array") for (var i = 0,
+            length = array.length; i < length; i++) ret.push(array[i]);
+            else ret = array.slice(0);
+            return ret;
+        },
+        inArray: function(elem, array) {
+            for (var i = 0,
+            length = array.length; i < length; i++) if (array[i] == elem) return i;
+            return - 1;
+        },
+        merge: function(first, second) {
+            if (jQuery.browser.msie) {
+                for (var i = 0; second[i]; i++) if (second[i].nodeType != 8) first.push(second[i]);
+            } else for (var i = 0; second[i]; i++) first.push(second[i]);
+            return first;
+        },
+        unique: function(array) {
+            var ret = [],
+            done = {};
+            try {
+                for (var i = 0,
+                length = array.length; i < length; i++) {
+                    var id = jQuery.data(array[i]);
+                    if (!done[id]) {
+                        done[id] = true;
+                        ret.push(array[i]);
+                    }
+                }
+            } catch(e) {
+                ret = array;
+            }
+            return ret;
+        },
+        grep: function(elems, callback, inv) {
+            var ret = [];
+            for (var i = 0,
+            length = elems.length; i < length; i++) if (!inv && callback(elems[i], i) || inv && !callback(elems[i], i)) ret.push(elems[i]);
+            return ret;
+        },
+        map: function(elems, callback) {
+            var ret = [];
+            for (var i = 0,
+            length = elems.length; i < length; i++) {
+                var value = callback(elems[i], i);
+                if (value !== null && value != undefined) {
+                    if (value.constructor != Array) value = [value];
+                    ret = ret.concat(value);
+                }
+            }
+            return ret;
+        }
+    });
+    var userAgent = navigator.userAgent.toLowerCase();
+    jQuery.browser = {
+        version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
+        safari: /webkit/.test(userAgent),
+        opera: /opera/.test(userAgent),
+        msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
+        mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent)
+    };
+    var styleFloat = jQuery.browser.msie ? "styleFloat": "cssFloat";
+    jQuery.extend({
+        boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
+        props: {
+            "for": "htmlFor",
+            "class": "className",
+            "float": styleFloat,
+            cssFloat: styleFloat,
+            styleFloat: styleFloat,
+            innerHTML: "innerHTML",
+            className: "className",
+            value: "value",
+            disabled: "disabled",
+            checked: "checked",
+            readonly: "readOnly",
+            selected: "selected",
+            maxlength: "maxLength",
+            selectedIndex: "selectedIndex",
+            defaultValue: "defaultValue",
+            tagName: "tagName",
+            nodeName: "nodeName"
+        }
+    });
+    jQuery.each({
+        parent: function(elem) {
+            return elem.parentNode;
+        },
+        parents: function(elem) {
+            return jQuery.dir(elem, "parentNode");
+        },
+        next: function(elem) {
+            return jQuery.nth(elem, 2, "nextSibling");
+        },
+        prev: function(elem) {
+            return jQuery.nth(elem, 2, "previousSibling");
+        },
+        nextAll: function(elem) {
+            return jQuery.dir(elem, "nextSibling");
+        },
+        prevAll: function(elem) {
+            return jQuery.dir(elem, "previousSibling");
+        },
+        siblings: function(elem) {
+            return jQuery.sibling(elem.parentNode.firstChild, elem);
+        },
+        children: function(elem) {
+            return jQuery.sibling(elem.firstChild);
+        },
+        contents: function(elem) {
+            return jQuery.nodeName(elem, "iframe") ? elem.contentDocument || elem.contentWindow.document: jQuery.makeArray(elem.childNodes);
+        }
+    },
+    function(name, fn) {
+        jQuery.fn[name] = function(selector) {
+            var ret = jQuery.map(this, fn);
+            if (selector && typeof selector == "string") ret = jQuery.multiFilter(selector, ret);
+            return this.pushStack(jQuery.unique(ret));
+        };
+    });
+    jQuery.each({
+        appendTo: "append",
+        prependTo: "prepend",
+        insertBefore: "before",
+        insertAfter: "after",
+        replaceAll: "replaceWith"
+    },
+    function(name, original) {
+        jQuery.fn[name] = function() {
+            var args = arguments;
+            return this.each(function() {
+                for (var i = 0,
+                length = args.length; i < length; i++) jQuery(args[i])[original](this);
+            });
+        };
+    });
+    jQuery.each({
+        removeAttr: function(name) {
+            jQuery.attr(this, name, "");
+            if (this.nodeType == 1) this.removeAttribute(name);
+        },
+        addClass: function(classNames) {
+            jQuery.className.add(this, classNames);
+        },
+        removeClass: function(classNames) {
+            jQuery.className.remove(this, classNames);
+        },
+        toggleClass: function(classNames) {
+            jQuery.className[jQuery.className.has(this, classNames) ? "remove": "add"](this, classNames);
+        },
+        remove: function(selector) {
+            if (!selector || jQuery.filter(selector, [this]).r.length) {
+                jQuery("*", this).add(this).each(function() {
+                    jQuery.event.remove(this);
+                    jQuery.removeData(this);
+                });
+                if (this.parentNode) this.parentNode.removeChild(this);
+            }
+        },
+        empty: function() {
+            jQuery(">*", this).remove();
+            while (this.firstChild) this.removeChild(this.firstChild);
+        }
+    },
+    function(name, fn) {
+        jQuery.fn[name] = function() {
+            return this.each(fn, arguments);
+        };
+    });
+    jQuery.each(["Height", "Width"],
+    function(i, name) {
+        var type = name.toLowerCase();
+        jQuery.fn[type] = function(size) {
+            return this[0] == window ? jQuery.browser.opera && document.body["client" + name] || jQuery.browser.safari && window["inner" + name] || document.compatMode == "CSS1Compat" && document.documentElement["client" + name] || document.body["client" + name] : this[0] == document ? Math.max(Math.max(document.body["scroll" + name], document.documentElement["scroll" + name]), Math.max(document.body["offset" + name], document.documentElement["offset" + name])) : size == undefined ? (this.length ? jQuery.css(this[0], type) : null) : this.css(type, size.constructor == String ? size: size + "px");
+        };
+    });
+    var chars = jQuery.browser.safari && parseInt(jQuery.browser.version) < 417 ? "(?:[\\w*_-]|\\\\.)": "(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",
+    quickChild = new RegExp("^>\\s*(" + chars + "+)"),
+    quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)"),
+    quickClass = new RegExp("^([#.]?)(" + chars + "*)");
+    jQuery.extend({
+        expr: {
+            "": function(a, i, m) {
+                return m[2] == "*" || jQuery.nodeName(a, m[2]);
+            },
+            "#": function(a, i, m) {
+                return a.getAttribute("id") == m[2];
+            },
+            ":": {
+                lt: function(a, i, m) {
+                    return i < m[3] - 0;
+                },
+                gt: function(a, i, m) {
+                    return i > m[3] - 0;
+                },
+                nth: function(a, i, m) {
+                    return m[3] - 0 == i;
+                },
+                eq: function(a, i, m) {
+                    return m[3] - 0 == i;
+                },
+                first: function(a, i) {
+                    return i == 0;
+                },
+                last: function(a, i, m, r) {
+                    return i == r.length - 1;
+                },
+                even: function(a, i) {
+                    return i % 2 == 0;
+                },
+                odd: function(a, i) {
+                    return i % 2;
+                },
+                "first-child": function(a) {
+                    return a.parentNode.getElementsByTagName("*")[0] == a;
+                },
+                "last-child": function(a) {
+                    return jQuery.nth(a.parentNode.lastChild, 1, "previousSibling") == a;
+                },
+                "only-child": function(a) {
+                    return ! jQuery.nth(a.parentNode.lastChild, 2, "previousSibling");
+                },
+                parent: function(a) {
+                    return a.firstChild;
+                },
+                empty: function(a) {
+                    return ! a.firstChild;
+                },
+                contains: function(a, i, m) {
+                    return (a.textContent || a.innerText || jQuery(a).text() || "").indexOf(m[3]) >= 0;
+                },
+                visible: function(a) {
+                    return "hidden" != a.type && jQuery.css(a, "display") != "none" && jQuery.css(a, "visibility") != "hidden";
+                },
+                hidden: function(a) {
+                    return "hidden" == a.type || jQuery.css(a, "display") == "none" || jQuery.css(a, "visibility") == "hidden";
+                },
+                enabled: function(a) {
+                    return ! a.disabled;
+                },
+                disabled: function(a) {
+                    return a.disabled;
+                },
+                checked: function(a) {
+                    return a.checked;
+                },
+                selected: function(a) {
+                    return a.selected || jQuery.attr(a, "selected");
+                },
+                text: function(a) {
+                    return "text" == a.type;
+                },
+                radio: function(a) {
+                    return "radio" == a.type;
+                },
+                checkbox: function(a) {
+                    return "checkbox" == a.type;
+                },
+                file: function(a) {
+                    return "file" == a.type;
+                },
+                password: function(a) {
+                    return "password" == a.type;
+                },
+                submit: function(a) {
+                    return "submit" == a.type;
+                },
+                image: function(a) {
+                    return "image" == a.type;
+                },
+                reset: function(a) {
+                    return "reset" == a.type;
+                },
+                button: function(a) {
+                    return "button" == a.type || jQuery.nodeName(a, "button");
+                },
+                input: function(a) {
+                    return /input|select|textarea|button/i.test(a.nodeName);
+                },
+                has: function(a, i, m) {
+                    return jQuery.find(m[3], a).length;
+                },
+                header: function(a) {
+                    return /h\d/i.test(a.nodeName);
+                },
+                animated: function(a) {
+                    return jQuery.grep(jQuery.timers,
+                    function(fn) {
+                        return a == fn.elem;
+                    }).length;
+                }
+            }
+        },
+        parse: [/^(\[) * ?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/, /^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/, new RegExp("^([:.#]*)(" + chars + "+)")],
+        multiFilter: function(expr, elems, not) {
+            var old, cur = [];
+            while (expr && expr != old) {
+                old = expr;
+                var f = jQuery.filter(expr, elems, not);
+                expr = f.t.replace(/^\s*,\s*/, "");
+                cur = not ? elems = f.r: jQuery.merge(cur, f.r);
+            }
+            return cur;
+        },
+        find: function(t, context) {
+            if (typeof t != "string") return [t];
+            if (context && context.nodeType != 1 && context.nodeType != 9) return [];
+            context = context || document;
+            var ret = [context],
+            done = [],
+            last,
+            nodeName;
+            while (t && last != t) {
+                var r = [];
+                last = t;
+                t = jQuery.trim(t);
+                var foundToken = false;
+                var re = quickChild;
+                var m = re.exec(t);
+                if (m) {
+                    nodeName = m[1].toUpperCase();
+                    for (var i = 0; ret[i]; i++) for (var c = ret[i].firstChild; c; c = c.nextSibling) if (c.nodeType == 1 && (nodeName == "*" || c.nodeName.toUpperCase() == nodeName)) r.push(c);
+                    ret = r;
+                    t = t.replace(re, "");
+                    if (t.indexOf(" ") == 0) continue;
+                    foundToken = true;
+                } else {
+                    re = /^([>+~])\s*(\w*)/i;
+                    if ((m = re.exec(t)) != null) {
+                        r = [];
+                        var merge = {};
+                        nodeName = m[2].toUpperCase();
+                        m = m[1];
+                        for (var j = 0,
+                        rl = ret.length; j < rl; j++) {
+                            var n = m == "~" || m == "+" ? ret[j].nextSibling: ret[j].firstChild;
+                            for (; n; n = n.nextSibling) if (n.nodeType == 1) {
+                                var id = jQuery.data(n);
+                                if (m == "~" && merge[id]) break;
+                                if (!nodeName || n.nodeName.toUpperCase() == nodeName) {
+                                    if (m == "~") merge[id] = true;
+                                    r.push(n);
+                                }
+                                if (m == "+") break;
+                            }
+                        }
+                        ret = r;
+                        t = jQuery.trim(t.replace(re, ""));
+                        foundToken = true;
+                    }
+                }
+                if (t && !foundToken) {
+                    if (!t.indexOf(",")) {
+                        if (context == ret[0]) ret.shift();
+                        done = jQuery.merge(done, ret);
+                        r = ret = [context];
+                        t = " " + t.substr(1, t.length);
+                    } else {
+                        var re2 = quickID;
+                        var m = re2.exec(t);
+                        if (m) {
+                            m = [0, m[2], m[3], m[1]];
+                        } else {
+                            re2 = quickClass;
+                            m = re2.exec(t);
+                        }
+                        m[2] = m[2].replace(/\\/g, "");
+                        var elem = ret[ret.length - 1];
+                        if (m[1] == "#" && elem && elem.getElementById && !jQuery.isXMLDoc(elem)) {
+                            var oid = elem.getElementById(m[2]);
+                            if ((jQuery.browser.msie || jQuery.browser.opera) && oid && typeof oid.id == "string" && oid.id != m[2]) oid = jQuery('[ id="' + m[2] + '"]', elem)[0];
+                            ret = r = oid && (!m[3] || jQuery.nodeName(oid, m[3])) ? [oid] : [];
+                        } else {
+                            for (var i = 0; ret[i]; i++) {
+                                var tag = m[1] == "#" && m[3] ? m[3] : m[1] != "" || m[0] == "" ? "*": m[2];
+                                if (tag == "*" && ret[i].nodeName.toLowerCase() == "object") tag = "param";
+                                r = jQuery.merge(r, ret[i].getElementsByTagName(tag));
+                            }
+                            if (m[1] == ".") r = jQuery.classFilter(r, m[2]);
+                            if (m[1] == "#") {
+                                var tmp = [];
+                                for (var i = 0; r[i]; i++) if (r[i].getAttribute("id") == m[2]) {
+                                    tmp = [r[i]];
+                                    break;
+                                }
+                                r = tmp;
+                            }
+                            ret = r;
+                        }
+                        t = t.replace(re2, "");
+                    }
+                }
+                if (t) {
+                    var val = jQuery.filter(t, r);
+                    ret = r = val.r;
+                    t = jQuery.trim(val.t);
+                }
+            }
+            if (t) ret = [];
+            if (ret && context == ret[0]) ret.shift();
+            done = jQuery.merge(done, ret);
+            return done;
+        },
+        classFilter: function(r, m, not) {
+            m = " " + m + " ";
+            var tmp = [];
+            for (var i = 0; r[i]; i++) {
+                var pass = (" " + r[i].className + " ").indexOf(m) >= 0;
+                if (!not && pass || not && !pass) tmp.push(r[i]);
+            }
+            return tmp;
+        },
+        filter: function(t, r, not) {
+            var last;
+            while (t && t != last) {
+                last = t;
+                var p = jQuery.parse,
+                m;
+                for (var i = 0; p[i]; i++) {
+                    m = p[i].exec(t);
+                    if (m) {
+                        t = t.substring(m[0].length);
+                        m[2] = m[2].replace(/\\/g, "");
+                        break;
+                    }
+                }
+                if (!m) break;
+                if (m[1] == ":" && m[2] == "not") r = isSimple.test(m[3]) ? jQuery.filter(m[3], r, true).r: jQuery(r).not(m[3]);
+                else if (m[1] == ".") r = jQuery.classFilter(r, m[2], not);
+                else if (m[1] == "[") {
+                    var tmp = [],
+                    type = m[3];
+                    for (var i = 0,
+                    rl = r.length; i < rl; i++) {
+                        var a = r[i],
+                        z = a[jQuery.props[m[2]] || m[2]];
+                        if (z == null || /href|src|selected/.test(m[2])) z = jQuery.attr(a, m[2]) || '';
+                        if ((type == "" && !!z || type == "=" && z == m[5] || type == "!=" && z != m[5] || type == "^=" && z && !z.indexOf(m[5]) || type == "$=" && z.substr(z.length - m[5].length) == m[5] || (type == "*=" || type == "~=") && z.indexOf(m[5]) >= 0) ^ not) tmp.push(a);
+                    }
+                    r = tmp;
+                } else if (m[1] == ":" && m[2] == "nth-child") {
+                    var merge = {},
+                    tmp = [],
+                    test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" || !/\D/.test(m[3]) && "0n+" + m[3] || m[3]),
+                    first = (test[1] + (test[2] || 1)) - 0,
+                    last = test[3] - 0;
+                    for (var i = 0,
+                    rl = r.length; i < rl; i++) {
+                        var node = r[i],
+                        parentNode = node.parentNode,
+                        id = jQuery.data(parentNode);
+                        if (!merge[id]) {
+                            var c = 1;
+                            for (var n = parentNode.firstChild; n; n = n.nextSibling) if (n.nodeType == 1) n.nodeIndex = c++;
+                            merge[id] = true;
+                        }
+                        var add = false;
+                        if (first == 0) {
+                            if (node.nodeIndex == last) add = true;
+                        } else if ((node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0) add = true;
+                        if (add ^ not) tmp.push(node);
+                    }
+                    r = tmp;
+                } else {
+                    var fn = jQuery.expr[m[1]];
+                    if (typeof fn == "object") fn = fn[m[2]];
+                    if (typeof fn == "string") fn = eval("false||function(a,i){return " + fn + ";}");
+                    r = jQuery.grep(r,
+                    function(elem, i) {
+                        return fn(elem, i, m, r);
+                    },
+                    not);
+                }
+            }
+            return {
+                r: r,
+                t: t
+            };
+        },
+        dir: function(elem, dir) {
+            var matched = [];
+            var cur = elem[dir];
+            while (cur && cur != document) {
+                if (cur.nodeType == 1) matched.push(cur);
+                cur = cur[dir];
+            }
+            return matched;
+        },
+        nth: function(cur, result, dir, elem) {
+            result = result || 1;
+            var num = 0;
+            for (; cur; cur = cur[dir]) if (cur.nodeType == 1 && ++num == result) break;
+            return cur;
+        },
+        sibling: function(n, elem) {
+            var r = [];
+            for (; n; n = n.nextSibling) {
+                if (n.nodeType == 1 && (!elem || n != elem)) r.push(n);
+            }
+            return r;
+        }
+    });
+    jQuery.event = {
+        add: function(elem, types, handler, data) {
+            if (elem.nodeType == 3 || elem.nodeType == 8) return;
+            if (jQuery.browser.msie && elem.setInterval != undefined) elem = window;
+            if (!handler.guid) handler.guid = this.guid++;
+            if (data != undefined) {
+                var fn = handler;
+                handler = function() {
+                    return fn.apply(this, arguments);
+                };
+                handler.data = data;
+                handler.guid = fn.guid;
+            }
+            var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}),
+            handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle",
+            function() {
+                var val;
+                if (typeof jQuery == "undefined" || jQuery.event.triggered) return val;
+                val = jQuery.event.handle.apply(arguments.callee.elem, arguments);
+                return val;
+            });
+            handle.elem = elem;
+            jQuery.each(types.split(/\s+/),
+            function(index, type) {
+                var parts = type.split(".");
+                type = parts[0];
+                handler.type = parts[1];
+                var handlers = events[type];
+                if (!handlers) {
+                    handlers = events[type] = {};
+                    if (!jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem) === false) {
+                        if (elem.addEventListener) elem.addEventListener(type, handle, false);
+                        else if (elem.attachEvent) elem.attachEvent("on" + type, handle);
+                    }
+                }
+                handlers[handler.guid] = handler;
+                jQuery.event.global[type] = true;
+            });
+            elem = null;
+        },
+        guid: 1,
+        global: {},
+        remove: function(elem, types, handler) {
+            if (elem.nodeType == 3 || elem.nodeType == 8) return;
+            var events = jQuery.data(elem, "events"),
+            ret,
+            index;
+            if (events) {
+                if (types == undefined || (typeof types == "string" && types.charAt(0) == ".")) for (var type in events) this.remove(elem, type + (types || ""));
+                else {
+                    if (types.type) {
+                        handler = types.handler;
+                        types = types.type;
+                    }
+                    jQuery.each(types.split(/\s+/),
+                    function(index, type) {
+                        var parts = type.split(".");
+                        type = parts[0];
+                        if (events[type]) {
+                            if (handler) delete events[type][handler.guid];
+                            else for (handler in events[type]) if (!parts[1] || events[type][handler].type == parts[1]) delete events[type][handler];
+                            for (ret in events[type]) break;
+                            if (!ret) {
+                                if (!jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem) === false) {
+                                    if (elem.removeEventListener) elem.removeEventListener(type, jQuery.data(elem, "handle"), false);
+                                    else if (elem.detachEvent) elem.detachEvent("on" + type, jQuery.data(elem, "handle"));
+                                }
+                                ret = null;
+                                delete events[type];
+                            }
+                        }
+                    });
+                }
+                for (ret in events) break;
+                if (!ret) {
+                    var handle = jQuery.data(elem, "handle");
+                    if (handle) handle.elem = null;
+                    jQuery.removeData(elem, "events");
+                    jQuery.removeData(elem, "handle");
+                }
+            }
+        },
+        trigger: function(type, data, elem, donative, extra) {
+            data = jQuery.makeArray(data || []);
+            if (type.indexOf("!") >= 0) {
+                type = type.slice(0, -1);
+                var exclusive = true;
+            }
+            if (!elem) {
+                if (this.global[type]) jQuery("*").add([window, document]).trigger(type, data);
+            } else {
+                if (elem.nodeType == 3 || elem.nodeType == 8) return undefined;
+                var val, ret, fn = jQuery.isFunction(elem[type] || null),
+                event = !data[0] || !data[0].preventDefault;
+                if (event) data.unshift(this.fix({
+                    type: type,
+                    target: elem
+                }));
+                data[0].type = type;
+                if (exclusive) data[0].exclusive = true;
+                if (jQuery.isFunction(jQuery.data(elem, "handle"))) val = jQuery.data(elem, "handle").apply(elem, data);
+                if (!fn && elem["on" + type] && elem["on" + type].apply(elem, data) === false) val = false;
+                if (event) data.shift();
+                if (extra && jQuery.isFunction(extra)) {
+                    ret = extra.apply(elem, val == null ? data: data.concat(val));
+                    if (ret !== undefined) val = ret;
+                }
+                if (fn && donative !== false && val !== false && !(jQuery.nodeName(elem, 'a') && type == "click")) {
+                    this.triggered = true;
+                    try {
+                        elem[type]();
+                    } catch(e) {}
+                }
+                this.triggered = false;
+            }
+            return val;
+        },
+        handle: function(event) {
+            var val;
+            event = jQuery.event.fix(event || window.event || {});
+            var parts = event.type.split(".");
+            event.type = parts[0];
+            var handlers = jQuery.data(this, "events") && jQuery.data(this, "events")[event.type],
+            args = Array.prototype.slice.call(arguments, 1);
+            args.unshift(event);
+            for (var j in handlers) {
+                var handler = handlers[j];
+                args[0].handler = handler;
+                args[0].data = handler.data;
+                if (!parts[1] && !event.exclusive || handler.type == parts[1]) {
+                    var ret = handler.apply(this, args);
+                    if (val !== false) val = ret;
+                    if (ret === false) {
+                        event.preventDefault();
+                        event.stopPropagation();
+                    }
+                }
+            }
+            if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null;
+            return val;
+        },
+        fix: function(event) {
+            var originalEvent = event;
+            event = jQuery.extend({},
+            originalEvent);
+            event.preventDefault = function() {
+                if (originalEvent.preventDefault) originalEvent.preventDefault();
+                originalEvent.returnValue = false;
+            };
+            event.stopPropagation = function() {
+                if (originalEvent.stopPropagation) originalEvent.stopPropagation();
+                originalEvent.cancelBubble = true;
+            };
+            if (!event.target) event.target = event.srcElement || document;
+            if (event.target.nodeType == 3) event.target = originalEvent.target.parentNode;
+            if (!event.relatedTarget && event.fromElement) event.relatedTarget = event.fromElement == event.target ? event.toElement: event.fromElement;
+            if (event.pageX == null && event.clientX != null) {
+                var doc = document.documentElement,
+                body = document.body;
+                event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0);
+                event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0);
+            }
+            if (!event.which && ((event.charCode || event.charCode === 0) ? event.charCode: event.keyCode)) event.which = event.charCode || event.keyCode;
+            if (!event.metaKey && event.ctrlKey) event.metaKey = event.ctrlKey;
+            if (!event.which && event.button) event.which = (event.button & 1 ? 1 : (event.button & 2 ? 3 : (event.button & 4 ? 2 : 0)));
+            return event;
+        },
+        special: {
+            ready: {
+                setup: function() {
+                    bindReady();
+                    return;
+                },
+                teardown: function() {
+                    return;
+                }
+            },
+            mouseenter: {
+                setup: function() {
+                    if (jQuery.browser.msie) return false;
+                    jQuery(this).bind("mouseover", jQuery.event.special.mouseenter.handler);
+                    return true;
+                },
+                teardown: function() {
+                    if (jQuery.browser.msie) return false;
+                    jQuery(this).unbind("mouseover", jQuery.event.special.mouseenter.handler);
+                    return true;
+                },
+                handler: function(event) {
+                    if (withinElement(event, this)) return true;
+                    arguments[0].type = "mouseenter";
+                    return jQuery.event.handle.apply(this, arguments);
+                }
+            },
+            mouseleave: {
+                setup: function() {
+                    if (jQuery.browser.msie) return false;
+                    jQuery(this).bind("mouseout", jQuery.event.special.mouseleave.handler);
+                    return true;
+                },
+                teardown: function() {
+                    if (jQuery.browser.msie) return false;
+                    jQuery(this).unbind("mouseout", jQuery.event.special.mouseleave.handler);
+                    return true;
+                },
+                handler: function(event) {
+                    if (withinElement(event, this)) return true;
+                    arguments[0].type = "mouseleave";
+                    return jQuery.event.handle.apply(this, arguments);
+                }
+            }
+        }
+    };
+    jQuery.fn.extend({
+        bind: function(type, data, fn) {
+            return type == "unload" ? this.one(type, data, fn) : this.each(function() {
+                jQuery.event.add(this, type, fn || data, fn && data);
+            });
+        },
+        one: function(type, data, fn) {
+            return this.each(function() {
+                jQuery.event.add(this, type,
+                function(event) {
+                    jQuery(this).unbind(event);
+                    return (fn || data).apply(this, arguments);
+                },
+                fn && data);
+            });
+        },
+        unbind: function(type, fn) {
+            return this.each(function() {
+                jQuery.event.remove(this, type, fn);
+            });
+        },
+        trigger: function(type, data, fn) {
+            return this.each(function() {
+                jQuery.event.trigger(type, data, this, true, fn);
+            });
+        },
+        triggerHandler: function(type, data, fn) {
+            if (this[0]) return jQuery.event.trigger(type, data, this[0], false, fn);
+            return undefined;
+        },
+        toggle: function() {
+            var args = arguments;
+            return this.click(function(event) {
+                this.lastToggle = 0 == this.lastToggle ? 1 : 0;
+                event.preventDefault();
+                return args[this.lastToggle].apply(this, arguments) || false;
+            });
+        },
+        hover: function(fnOver, fnOut) {
+            return this.bind('mouseenter', fnOver).bind('mouseleave', fnOut);
+        },
+        ready: function(fn) {
+            bindReady();
+            if (jQuery.isReady) fn.call(document, jQuery);
+            else jQuery.readyList.push(function() {
+                return fn.call(this, jQuery);
+            });
+            return this;
+        }
+    });
+    jQuery.extend({
+        isReady: false,
+        readyList: [],
+        ready: function() {
+            if (!jQuery.isReady) {
+                jQuery.isReady = true;
+                if (jQuery.readyList) {
+                    jQuery.each(jQuery.readyList,
+                    function() {
+                        this.apply(document);
+                    });
+                    jQuery.readyList = null;
+                }
+                jQuery(document).triggerHandler("ready");
+            }
+        }
+    });
+    var readyBound = false;
+    function bindReady() {
+        if (readyBound) return;
+        readyBound = true;
+        if (document.addEventListener && !jQuery.browser.opera) document.addEventListener("DOMContentLoaded", jQuery.ready, false);
+        if (jQuery.browser.msie && window == top)(function() {
+            if (jQuery.isReady) return;
+            try {
+                document.documentElement.doScroll("left");
+            } catch(error) {
+                setTimeout(arguments.callee, 0);
+                return;
+            }
+            jQuery.ready();
+        })();
+        if (jQuery.browser.opera) document.addEventListener("DOMContentLoaded",
+        function() {
+            if (jQuery.isReady) return;
+            for (var i = 0; i < document.styleSheets.length; i++) if (document.styleSheets[i].disabled) {
+                setTimeout(arguments.callee, 0);
+                return;
+            }
+            jQuery.ready();
+        },
+        false);
+        if (jQuery.browser.safari) {
+            var numStyles; (function() {
+                if (jQuery.isReady) return;
+                if (document.readyState != "loaded" && document.readyState != "complete") {
+                    setTimeout(arguments.callee, 0);
+                    return;
+                }
+                if (numStyles === undefined) numStyles = jQuery("style, link[rel=stylesheet]").length;
+                if (document.styleSheets.length != numStyles) {
+                    setTimeout(arguments.callee, 0);
+                    return;
+                }
+                jQuery.ready();
+            })();
+        }
+        jQuery.event.add(window, "load", jQuery.ready);
+    }
+    jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick," + "mousedown,mouseup,mousemove,mouseover,mouseout,change,select," + "submit,keydown,keypress,keyup,error").split(","),
+    function(i, name) {
+        jQuery.fn[name] = function(fn) {
+            return fn ? this.bind(name, fn) : this.trigger(name);
+        };
+    });
+    var withinElement = function(event, elem) {
+        var parent = event.relatedTarget;
+        while (parent && parent != elem) try {
+            parent = parent.parentNode;
+        } catch(error) {
+            parent = elem;
+        }
+        return parent == elem;
+    };
+    jQuery(window).bind("unload",
+    function() {
+        jQuery("*").add(document).unbind();
+    });
+    jQuery.fn.extend({
+        load: function(url, params, callback) {
+            if (jQuery.isFunction(url)) return this.bind("load", url);
+            var off = url.indexOf(" ");
+            if (off >= 0) {
+                var selector = url.slice(off, url.length);
+                url = url.slice(0, off);
+            }
+            callback = callback ||
+            function() {};
+            var type = "GET";
+            if (params) if (jQuery.isFunction(params)) {
+                callback = params;
+                params = null;
+            } else {
+                params = jQuery.param(params);
+                type = "POST";
+            }
+            var self = this;
+            jQuery.ajax({
+                url: url,
+                type: type,
+                dataType: "html",
+                data: params,
+                complete: function(res, status) {
+                    if (status == "success" || status == "notmodified") self.html(selector ? jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g, "")).find(selector) : res.responseText);
+                    self.each(callback, [res.responseText, status, res]);
+                }
+            });
+            return this;
+        },
+        serialize: function() {
+            return jQuery.param(this.serializeArray());
+        },
+        serializeArray: function() {
+            return this.map(function() {
+                return jQuery.nodeName(this, "form") ? jQuery.makeArray(this.elements) : this;
+            }).filter(function() {
+                return this.name && !this.disabled && (this.checked || /select|textarea/i.test(this.nodeName) || /text|hidden|password/i.test(this.type));
+            }).map(function(i, elem) {
+                var val = jQuery(this).val();
+                return val == null ? null: val.constructor == Array ? jQuery.map(val,
+                function(val, i) {
+                    return {
+                        name: elem.name,
+                        value: val
+                    };
+                }) : {
+                    name: elem.name,
+                    value: val
+                };
+            }).get();
+        }
+    });
+    jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),
+    function(i, o) {
+        jQuery.fn[o] = function(f) {
+            return this.bind(o, f);
+        };
+    });
+    var jsc = (new Date).getTime();
+    jQuery.extend({
+        get: function(url, data, callback, type) {
+            if (jQuery.isFunction(data)) {
+                callback = data;
+                data = null;
+            }
+            return jQuery.ajax({
+                type: "GET",
+                url: url,
+                data: data,
+                success: callback,
+                dataType: type
+            });
+        },
+        getScript: function(url, callback) {
+            return jQuery.get(url, null, callback, "script");
+        },
+        getJSON: function(url, data, callback) {
+            return jQuery.get(url, data, callback, "json");
+        },
+        post: function(url, data, callback, type) {
+            if (jQuery.isFunction(data)) {
+                callback = data;
+                data = {};
+            }
+            return jQuery.ajax({
+                type: "POST",
+                url: url,
+                data: data,
+                success: callback,
+                dataType: type
+            });
+        },
+        ajaxSetup: function(settings) {
+            jQuery.extend(jQuery.ajaxSettings, settings);
+        },
+        ajaxSettings: {
+            global: true,
+            type: "GET",
+            timeout: 0,
+            contentType: "application/x-www-form-urlencoded",
+            processData: true,
+            async: true,
+            data: null,
+            username: null,
+            password: null,
+            accepts: {
+                xml: "application/xml, text/xml",
+                html: "text/html",
+                script: "text/javascript, application/javascript",
+                json: "application/json, text/javascript",
+                text: "text/plain",
+                _default: "*/*"
+            }
+        },
+        lastModified: {},
+        ajax: function(s) {
+            var jsonp, jsre = /=\?(&|$)/g,
+            status, data;
+            s = jQuery.extend(true, s, jQuery.extend(true, {},
+            jQuery.ajaxSettings, s));
+            if (s.data && s.processData && typeof s.data != "string") s.data = jQuery.param(s.data);
+            if (s.dataType == "jsonp") {
+                if (s.type.toLowerCase() == "get") {
+                    if (!s.url.match(jsre)) s.url += (s.url.match(/\?/) ? "&": "?") + (s.jsonp || "callback") + "=?";
+                } else if (!s.data || !s.data.match(jsre)) s.data = (s.data ? s.data + "&": "") + (s.jsonp || "callback") + "=?";
+                s.dataType = "json";
+            }
+            if (s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre))) {
+                jsonp = "jsonp" + jsc++;
+                if (s.data) s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
+                s.url = s.url.replace(jsre, "=" + jsonp + "$1");
+                s.dataType = "script";
+                window[jsonp] = function(tmp) {
+                    data = tmp;
+                    success();
+                    complete();
+                    window[jsonp] = undefined;
+                    try {
+                        delete window[jsonp];
+                    } catch(e) {}
+                    if (head) head.removeChild(script);
+                };
+            }
+            if (s.dataType == "script" && s.cache == null) s.cache = false;
+            if (s.cache === false && s.type.toLowerCase() == "get") {
+                var ts = (new Date()).getTime();
+                var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2");
+                s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&": "?") + "_=" + ts: "");
+            }
+            if (s.data && s.type.toLowerCase() == "get") {
+                s.url += (s.url.match(/\?/) ? "&": "?") + s.data;
+                s.data = null;
+            }
+            if (s.global && !jQuery.active++) jQuery.event.trigger("ajaxStart");
+            if ((!s.url.indexOf("http") || !s.url.indexOf("//")) && s.dataType == "script" && s.type.toLowerCase() == "get") {
+                var head = document.getElementsByTagName("head")[0];
+                var script = document.createElement("script");
+                script.src = s.url;
+                if (s.scriptCharset) script.charset = s.scriptCharset;
+                if (!jsonp) {
+                    var done = false;
+                    script.onload = script.onreadystatechange = function() {
+                        if (!done && (!this.readyState || this.readyState == "loaded" || this.readyState == "complete")) {
+                            done = true;
+                            success();
+                            complete();
+                            head.removeChild(script);
+                        }
+                    };
+                }
+                head.appendChild(script);
+                return undefined;
+            }
+            var requestDone = false;
+            var xml = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
+            xml.open(s.type, s.url, s.async, s.username, s.password);
+            try {
+                if (s.data) xml.setRequestHeader("Content-Type", s.contentType);
+                if (s.ifModified) xml.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT");
+                xml.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+                xml.setRequestHeader("Accept", s.dataType && s.accepts[s.dataType] ? s.accepts[s.dataType] + ", */*": s.accepts._default);
+            } catch(e) {}
+            if (s.beforeSend) s.beforeSend(xml);
+            if (s.global) jQuery.event.trigger("ajaxSend", [xml, s]);
+            var onreadystatechange = function(isTimeout) {
+                if (!requestDone && xml && (xml.readyState == 4 || isTimeout == "timeout")) {
+                    requestDone = true;
+                    if (ival) {
+                        clearInterval(ival);
+                        ival = null;
+                    }
+                    status = isTimeout == "timeout" && "timeout" || !jQuery.httpSuccess(xml) && "error" || s.ifModified && jQuery.httpNotModified(xml, s.url) && "notmodified" || "success";
+                    if (status == "success") {
+                        try {
+                            data = jQuery.httpData(xml, s.dataType);
+                        } catch(e) {
+                            status = "parsererror";
+                        }
+                    }
+                    if (status == "success") {
+                        var modRes;
+                        try {
+                            modRes = xml.getResponseHeader("Last-Modified");
+                        } catch(e) {}
+                        if (s.ifModified && modRes) jQuery.lastModified[s.url] = modRes;
+                        if (!jsonp) success();
+                    } else jQuery.handleError(s, xml, status);
+                    complete();
+                    if (s.async) xml = null;
+                }
+            };
+            if (s.async) {
+                var ival = setInterval(onreadystatechange, 13);
+                if (s.timeout > 0) setTimeout(function() {
+                    if (xml) {
+                        xml.abort();
+                        if (!requestDone) onreadystatechange("timeout");
+                    }
+                },
+                s.timeout);
+            }
+            try {
+                xml.send(s.data);
+            } catch(e) {
+                jQuery.handleError(s, xml, null, e);
+            }
+            if (!s.async) onreadystatechange();
+            function success() {
+                if (s.success) s.success(data, status);
+                if (s.global) jQuery.event.trigger("ajaxSuccess", [xml, s]);
+            }
+            function complete() {
+                if (s.complete) s.complete(xml, status);
+                if (s.global) jQuery.event.trigger("ajaxComplete", [xml, s]);
+                if (s.global && !--jQuery.active) jQuery.event.trigger("ajaxStop");
+            }
+            return xml;
+        },
+        handleError: function(s, xml, status, e) {
+            if (s.error) s.error(xml, status, e);
+            if (s.global) jQuery.event.trigger("ajaxError", [xml, s, e]);
+        },
+        active: 0,
+        httpSuccess: function(r) {
+            try {
+                return ! r.status && location.protocol == "file:" || (r.status >= 200 && r.status < 300) || r.status == 304 || r.status == 1223 || jQuery.browser.safari && r.status == undefined;
+            } catch(e) {}
+            return false;
+        },
+        httpNotModified: function(xml, url) {
+            try {
+                var xmlRes = xml.getResponseHeader("Last-Modified");
+                return xml.status == 304 || xmlRes == jQuery.lastModified[url] || jQuery.browser.safari && xml.status == undefined;
+            } catch(e) {}
+            return false;
+        },
+        httpData: function(r, type) {
+            var ct = r.getResponseHeader("content-type");
+            var xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0;
+            var data = xml ? r.responseXML: r.responseText;
+            if (xml && data.documentElement.tagName == "parsererror") throw "parsererror";
+            if (type == "script") jQuery.globalEval(data);
+            if (type == "json") data = eval("(" + data + ")");
+            return data;
+        },
+        param: function(a) {
+            var s = [];
+            if (a.constructor == Array || a.jquery) jQuery.each(a,
+            function() {
+                s.push(encodeURIComponent(this.name) + "=" + encodeURIComponent(this.value));
+            });
+            else for (var j in a) if (a[j] && a[j].constructor == Array) jQuery.each(a[j],
+            function() {
+                s.push(encodeURIComponent(j) + "=" + encodeURIComponent(this));
+            });
+            else s.push(encodeURIComponent(j) + "=" + encodeURIComponent(a[j]));
+            return s.join("&").replace(/%20/g, "+");
+        }
+    });
+    jQuery.fn.extend({
+        show: function(speed, callback) {
+            return speed ? this.animate({
+                height: "show",
+                width: "show",
+                opacity: "show"
+            },
+            speed, callback) : this.filter(":hidden").each(function() {
+                this.style.display = this.oldblock || "";
+                if (jQuery.css(this, "display") == "none") {
+                    var elem = jQuery("<" + this.tagName + " />").appendTo("body");
+                    this.style.display = elem.css("display");
+                    if (this.style.display == "none") this.style.display = "block";
+                    elem.remove();
+                }
+            }).end();
+        },
+        hide: function(speed, callback) {
+            return speed ? this.animate({
+                height: "hide",
+                width: "hide",
+                opacity: "hide"
+            },
+            speed, callback) : this.filter(":visible").each(function() {
+                this.oldblock = this.oldblock || jQuery.css(this, "display");
+                this.style.display = "none";
+            }).end();
+        },
+        _toggle: jQuery.fn.toggle,
+        toggle: function(fn, fn2) {
+            return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ? this._toggle(fn, fn2) : fn ? this.animate({
+                height: "toggle",
+                width: "toggle",
+                opacity: "toggle"
+            },
+            fn, fn2) : this.each(function() {
+                jQuery(this)[jQuery(this).is(":hidden") ? "show": "hide"]();
+            });
+        },
+        slideDown: function(speed, callback) {
+            return this.animate({
+                height: "show"
+            },
+            speed, callback);
+        },
+        slideUp: function(speed, callback) {
+            return this.animate({
+                height: "hide"
+            },
+            speed, callback);
+        },
+        slideToggle: function(speed, callback) {
+            return this.animate({
+                height: "toggle"
+            },
+            speed, callback);
+        },
+        fadeIn: function(speed, callback) {
+            return this.animate({
+                opacity: "show"
+            },
+            speed, callback);
+        },
+        fadeOut: function(speed, callback) {
+            return this.animate({
+                opacity: "hide"
+            },
+            speed, callback);
+        },
+        fadeTo: function(speed, to, callback) {
+            return this.animate({
+                opacity: to
+            },
+            speed, callback);
+        },
+        animate: function(prop, speed, easing, callback) {
+            var optall = jQuery.speed(speed, easing, callback);
+            return this[optall.queue === false ? "each": "queue"](function() {
+                if (this.nodeType != 1) return false;
+                var opt = jQuery.extend({},
+                optall);
+                var hidden = jQuery(this).is(":hidden"),
+                self = this;
+                for (var p in prop) {
+                    if (prop[p] == "hide" && hidden || prop[p] == "show" && !hidden) return jQuery.isFunction(opt.complete) && opt.complete.apply(this);
+                    if (p == "height" || p == "width") {
+                        opt.display = jQuery.css(this, "display");
+                        opt.overflow = this.style.overflow;
+                    }
+                }
+                if (opt.overflow != null) this.style.overflow = "hidden";
+                opt.curAnim = jQuery.extend({},
+                prop);
+                jQuery.each(prop,
+                function(name, val) {
+                    var e = new jQuery.fx(self, opt, name);
+                    if (/toggle|show|hide/.test(val)) e[val == "toggle" ? hidden ? "show": "hide": val](prop);
+                    else {
+                        var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),
+                        start = e.cur(true) || 0;
+                        if (parts) {
+                            var end = parseFloat(parts[2]),
+                            unit = parts[3] || "px";
+                            if (unit != "px") {
+                                self.style[name] = (end || 1) + unit;
+                                start = ((end || 1) / e.cur(true)) * start;
+                                self.style[name] = start + unit;
+                            }
+                            if (parts[1]) end = ((parts[1] == "-=" ? -1 : 1) * end) + start;
+                            e.custom(start, end, unit);
+                        } else e.custom(start, val, "");
+                    }
+                });
+                return true;
+            });
+        },
+        queue: function(type, fn) {
+            if (jQuery.isFunction(type) || (type && type.constructor == Array)) {
+                fn = type;
+                type = "fx";
+            }
+            if (!type || (typeof type == "string" && !fn)) return queue(this[0], type);
+            return this.each(function() {
+                if (fn.constructor == Array) queue(this, type, fn);
+                else {
+                    queue(this, type).push(fn);
+                    if (queue(this, type).length == 1) fn.apply(this);
+                }
+            });
+        },
+        stop: function(clearQueue, gotoEnd) {
+            var timers = jQuery.timers;
+            if (clearQueue) this.queue([]);
+            this.each(function() {
+                for (var i = timers.length - 1; i >= 0; i--) if (timers[i].elem == this) {
+                    if (gotoEnd) timers[i](true);
+                    timers.splice(i, 1);
+                }
+            });
+            if (!gotoEnd) this.dequeue();
+            return this;
+        }
+    });
+    var queue = function(elem, type, array) {
+        if (!elem) return undefined;
+        type = type || "fx";
+        var q = jQuery.data(elem, type + "queue");
+        if (!q || array) q = jQuery.data(elem, type + "queue", array ? jQuery.makeArray(array) : []);
+        return q;
+    };
+    jQuery.fn.dequeue = function(type) {
+        type = type || "fx";
+        return this.each(function() {
+            var q = queue(this, type);
+            q.shift();
+            if (q.length) q[0].apply(this);
+        });
+    };
+    jQuery.extend({
+        speed: function(speed, easing, fn) {
+            var opt = speed && speed.constructor == Object ? speed: {
+                complete: fn || !fn && easing || jQuery.isFunction(speed) && speed,
+                duration: speed,
+                easing: fn && easing || easing && easing.constructor != Function && easing
+            };
+            opt.duration = (opt.duration && opt.duration.constructor == Number ? opt.duration: {
+                slow: 600,
+                fast: 200
+            } [opt.duration]) || 400;
+            opt.old = opt.complete;
+            opt.complete = function() {
+                if (opt.queue !== false) jQuery(this).dequeue();
+                if (jQuery.isFunction(opt.old)) opt.old.apply(this);
+            };
+            return opt;
+        },
+        easing: {
+            linear: function(p, n, firstNum, diff) {
+                return firstNum + diff * p;
+            },
+            swing: function(p, n, firstNum, diff) {
+                return (( - Math.cos(p * Math.PI) / 2) + 0.5) * diff + firstNum;
+            }
+        },
+        timers: [],
+        timerId: null,
+        fx: function(elem, options, prop) {
+            this.options = options;
+            this.elem = elem;
+            this.prop = prop;
+            if (!options.orig) options.orig = {};
+        }
+    });
+    jQuery.fx.prototype = {
+        update: function() {
+            if (this.options.step) this.options.step.apply(this.elem, [this.now, this]); (jQuery.fx.step[this.prop] || jQuery.fx.step._default)(this);
+            if (this.prop == "height" || this.prop == "width") this.elem.style.display = "block";
+        },
+        cur: function(force) {
+            if (this.elem[this.prop] != null && this.elem.style[this.prop] == null) return this.elem[this.prop];
+            var r = parseFloat(jQuery.css(this.elem, this.prop, force));
+            return r && r > -10000 ? r: parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
+        },
+        custom: function(from, to, unit) {
+            this.startTime = (new Date()).getTime();
+            this.start = from;
+            this.end = to;
+            this.unit = unit || this.unit || "px";
+            this.now = this.start;
+            this.pos = this.state = 0;
+            this.update();
+            var self = this;
+            function t(gotoEnd) {
+                return self.step(gotoEnd);
+            }
+            t.elem = this.elem;
+            jQuery.timers.push(t);
+            if (jQuery.timerId == null) {
+                jQuery.timerId = setInterval(function() {
+                    var timers = jQuery.timers;
+                    for (var i = 0; i < timers.length; i++) if (!timers[i]()) timers.splice(i--, 1);
+                    if (!timers.length) {
+                        clearInterval(jQuery.timerId);
+                        jQuery.timerId = null;
+                    }
+                },
+                13);
+            }
+        },
+        show: function() {
+            this.options.orig[this.prop] = jQuery.attr(this.elem.style, this.prop);
+            this.options.show = true;
+            this.custom(0, this.cur());
+            if (this.prop == "width" || this.prop == "height") this.elem.style[this.prop] = "1px";
+            jQuery(this.elem).show();
+        },
+        hide: function() {
+            this.options.orig[this.prop] = jQuery.attr(this.elem.style, this.prop);
+            this.options.hide = true;
+            this.custom(this.cur(), 0);
+        },
+        step: function(gotoEnd) {
+            var t = (new Date()).getTime();
+            if (gotoEnd || t > this.options.duration + this.startTime) {
+                this.now = this.end;
+                this.pos = this.state = 1;
+                this.update();
+                this.options.curAnim[this.prop] = true;
+                var done = true;
+                for (var i in this.options.curAnim) if (this.options.curAnim[i] !== true) done = false;
+                if (done) {
+                    if (this.options.display != null) {
+                        this.elem.style.overflow = this.options.overflow;
+                        this.elem.style.display = this.options.display;
+                        if (jQuery.css(this.elem, "display") == "none") this.elem.style.display = "block";
+                    }
+                    if (this.options.hide) this.elem.style.display = "none";
+                    if (this.options.hide || this.options.show) for (var p in this.options.curAnim) jQuery.attr(this.elem.style, p, this.options.orig[p]);
+                }
+                if (done && jQuery.isFunction(this.options.complete)) this.options.complete.apply(this.elem);
+                return false;
+            } else {
+                var n = t - this.startTime;
+                this.state = n / this.options.duration;
+                this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing": "linear")](this.state, n, 0, 1, this.options.duration);
+                this.now = this.start + ((this.end - this.start) * this.pos);
+                this.update();
+            }
+            return true;
+        }
+    };
+    jQuery.fx.step = {
+        scrollLeft: function(fx) {
+            fx.elem.scrollLeft = fx.now;
+        },
+        scrollTop: function(fx) {
+            fx.elem.scrollTop = fx.now;
+        },
+        opacity: function(fx) {
+            jQuery.attr(fx.elem.style, "opacity", fx.now);
+        },
+        _default: function(fx) {
+            fx.elem.style[fx.prop] = fx.now + fx.unit;
+        }
+    };
+    jQuery.fn.offset = function() {
+        var left = 0,
+        top = 0,
+        elem = this[0],
+        results;
+        if (elem) with(jQuery.browser) {
+            var parent = elem.parentNode,
+            offsetChild = elem,
+            offsetParent = elem.offsetParent,
+            doc = elem.ownerDocument,
+            safari2 = safari && parseInt(version) < 522 && !/adobeair/i.test(userAgent),
+            fixed = jQuery.css(elem, "position") == "fixed";
+            if (elem.getBoundingClientRect) {
+                var box = elem.getBoundingClientRect();
+                add(box.left + Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft), box.top + Math.max(doc.documentElement.scrollTop, doc.body.scrollTop));
+                add( - doc.documentElement.clientLeft, -doc.documentElement.clientTop);
+            } else {
+                add(elem.offsetLeft, elem.offsetTop);
+                while (offsetParent) {
+                    add(offsetParent.offsetLeft, offsetParent.offsetTop);
+                    if (mozilla && !/^t(able|d|h)$/i.test(offsetParent.tagName) || safari && !safari2) border(offsetParent);
+                    if (!fixed && jQuery.css(offsetParent, "position") == "fixed") fixed = true;
+                    offsetChild = /^body$/i.test(offsetParent.tagName) ? offsetChild: offsetParent;
+                    offsetParent = offsetParent.offsetParent;
+                }
+                while (parent && parent.tagName && !/^body|html$/i.test(parent.tagName)) {
+                    if (!/^inline|table.*$/i.test(jQuery.css(parent, "display"))) add( - parent.scrollLeft, -parent.scrollTop);
+                    if (mozilla && jQuery.css(parent, "overflow") != "visible") border(parent);
+                    parent = parent.parentNode;
+                }
+                if ((safari2 && (fixed || jQuery.css(offsetChild, "position") == "absolute")) || (mozilla && jQuery.css(offsetChild, "position") != "absolute")) add( - doc.body.offsetLeft, -doc.body.offsetTop);
+                if (fixed) add(Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft), Math.max(doc.documentElement.scrollTop, doc.body.scrollTop));
+            }
+            results = {
+                top: top,
+                left: left
+            };
+        }
+        function border(elem) {
+            add(jQuery.curCSS(elem, "borderLeftWidth", true), jQuery.curCSS(elem, "borderTopWidth", true));
+        }
+        function add(l, t) {
+            left += parseInt(l) || 0;
+            top += parseInt(t) || 0;
+        }
+        return results;
+    };
+})();
\ No newline at end of file

Added: trunk/tools/md5.js
==============================================================================
--- (empty file)
+++ trunk/tools/md5.js	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,256 @@
+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}

Added: trunk/tools/mouseapp_2.js
==============================================================================
--- (empty file)
+++ trunk/tools/mouseapp_2.js	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,888 @@
+//
+// Copyright (c) 2008 why the lucky stiff
+// 
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+var MouseApp = {
+    Version: '0.10',
+    CharCodes: {
+        0: '&nbsp;', 1: '&nbsp;', 9: '&nbsp;',
+        32: '&nbsp;', 34: '&quot;', 38: '&amp;',
+        60: '&lt;', 62: '&gt;', 127: '&loz;',
+        0x20AC: '&euro;'
+    },
+    KeyCodes: {
+        Backspace: 8, Tab: 9, Enter: 13, Esc: 27, PageUp: 33, PageDown: 34,
+        End: 35, Home: 36, Left: 37, Up: 38, Right: 39, Down: 40, Insert: 45,
+        Delete: 46, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117,
+        F7: 118, F8: 119, F10: 121
+    },
+    CodeKeys: {},
+    Modes: { 1: 'b', 2: 'u', 4: 'i', 8: 'strike' },
+    ModeIds: { r: 1, u: 2, i: 4, s: 8 },
+    Colors: ['black', 'blue', 'green',
+        'cyan', 'red', 'purple', 'brown', 
+        'gray', 'dark_gray', 'lt_blue',
+        'lt_green', 'lt_cyan', 'lt_red',
+        'lt_purple', 'yellow', 'white']
+}
+if ( navigator.appVersion.indexOf('AppleWebKit') > 0 ) {
+    MouseApp.KeyCodes = {
+        Backspace: 8, Tab: 9, Enter: 13, Esc: 27, PageUp: 63276, PageDown: 63277,
+        End: 63275, Home: 63273, Left: 63234, Up: 63232, Right: 63235, Down: 63233, Insert: 632325,
+        Delete: 63272, F1: 63236, F2: 63237, F3: 63238, F4: 63239, F5: 63240, F6: 63241,
+        F7: 63242, F8: 63243, F10: 63244
+    };
+}
+for ( var k in MouseApp.KeyCodes ) {
+    MouseApp.CodeKeys[MouseApp.KeyCodes[k]] = k;
+}
+
+MouseApp.isPrintable = function(ch) {
+    return (ch >= 32);
+};
+
+MouseApp.Base = function(){};
+MouseApp.Base.prototype = {
+    setOptions: function(options) {
+        this.options = {
+            columns: 72, rows: 24, indent: 2,
+            title: 'MouseApp',
+            blinkRate: 500,
+            ps: '>',
+            greeting:'%+r Terminal ready. %-r'
+        }
+        $.extend(this.options, options || {});
+    }
+}
+
+MouseApp.Manager = new Object();
+$.extend(MouseApp.Manager, {
+    observeTerm: function(term) {
+        this.activeTerm = term;
+        if ( this.observingKeyboard ) return;
+        var mgr = this;
+        if ( term.input ) {
+          term.input.keypress(function(e) { mgr.onKeyPress(e) });
+          if (!window.opera) term.input.keydown(function(e) { mgr.onKeyDown(e) });
+          else window.setInterval(function(){term.input.focus()},1);
+        } else {
+          if (!window.opera) $(document).keydown(function(e) { mgr.onKeyDown(e) });
+          $(document).keypress(function(e) { mgr.onKeyPress(e) });
+        }
+        this.observingKeyboard = true;
+    },
+
+    onKeyDown: function(e) { 
+        e = (e) ? e : ((event) ? event : null);
+        if ( e && MouseApp.CodeKeys[e.keyCode] ) {
+            if ( window.event ) { 
+                this.sendKeyPress(e);
+            }
+            this.blockEvent(e);
+            return false;
+        }
+        return true;
+    },
+
+    onKeyPress: function(e) {
+        if ( !window.opera && window.event && e.keyCode != 13 && e.keyCode != 8 ) { 
+            e.charCode = e.keyCode; e.keyCode = null; 
+        }
+        if ( e.keyCode == 191 ) { /* FF 1.0.x sends this upsy quizy -- ignore */
+            return;
+        }
+        return this.sendKeyPress(e);
+    },
+
+    sendKeyPress: function(e) {
+        var term = MouseApp.Manager.activeTerm;
+        term.cursorOff();
+        b = term.onKeyPress(e);
+        term.cursorOn();
+        return b;
+    },
+
+    blockEvent: function (e) {
+        e.cancelBubble=true;
+        if (window.event && !window.opera) e.keyCode=0;
+        if (e.stopPropagation) e.stopPropagation();
+        if (e.preventDefault)  e.preventDefault();
+    }
+});
+
+/* Basic text window functionality */
+MouseApp.Window = function(element, options) {
+  this.element = $(element);
+  this.setOptions(options);
+  this.initWindow();
+};
+
+$.extend(MouseApp.Window.prototype, (new MouseApp.Base()), {
+    initWindow: function() {
+        var html = '';
+        for ( var i = 0; i < this.options.rows; i++ ) {
+            html += "<div id='" + this.element.attr('id') + "_" + i + "'>&nbsp;</div>\n";
+        }
+        this.element.html(html);
+        this.typingOn();
+        if (this.options.input) {
+          this.input = $(this.options.input);
+          this.input.focus();
+        }
+        MouseApp.Manager.observeTerm(this);
+        this.clear();
+        this.cursorOn();
+        this.painting = true;
+        this.element.css({visibility: 'visible'});
+    },
+
+    text: function() {
+      var str = "";
+      for (var i = 0; i < this.screen.length; i++ ) {
+        for (var j = 0; j < this.options.columns; j++ ) {
+          var ch = this.screen[i][j]; 
+          if ( ch[0] != 0 ) {
+            str += String.fromCharCode(ch[0]);
+          }
+        }
+      }
+      return str;
+    },
+
+    clear: function() {
+        this.rpos = 0;
+        this.cpos = 0;
+        this.screen = [];
+        this.element.html('');
+        this.screen[0] = this.fillRow(this.options.columns, 0);
+        this.paint(0);
+    },
+
+    typingOn: function() { this.typing = true; },
+    typingOff: function() { this.typing = false; },
+
+    cursorOn: function() {
+        if ( this.blinker ) {
+            clearInterval( this.blinker );
+        }
+        this.underblink = this.screen[this.rpos][this.cpos][1];
+        MouseApp.Manager.activeTerm.blink();
+        this.blinker = setInterval(function(){MouseApp.Manager.activeTerm.blink();}, this.options.blinkRate);
+        this.cursor = true;
+    },
+
+    cursorOff: function() {
+        if ( this.blinker ) {
+            clearInterval( this.blinker );
+        }
+        if ( this.cursor ) {
+            this.screen[this.rpos][this.cpos][1] = this.underblink;
+            this.paint(this.rpos);
+            this.cursor = false;
+        }
+    },
+
+    blink: function() {
+        if ( this == MouseApp.Manager.activeTerm ) {
+            var mode = this.screen[this.rpos][this.cpos][1];
+            this.screen[this.rpos][this.cpos][1] = ( mode & 1 ) ? mode & 4094 : mode | 1;
+            this.paint(this.rpos);
+        }
+    },
+
+    fillRow: function(len, ch, mode) {
+        ary = []
+        for (var i = 0; i < len; i++) {
+            ary[i] = [ch, mode];
+        }
+        return ary;
+    },
+
+    paint: function(start, end) {
+        if (!this.painting) return;
+
+        if (!end) end = start;
+        for (var row = start; row <= end && row < this.screen.length; row++) {
+            var html = '';
+            var mode = 0;
+            var fcolor = 0;
+            var bcolor = 0;
+            var spans = 0;
+            for (var i = 0; i < this.options.columns; i++ ) {
+                var c = this.screen[row][i][0];
+                var m = this.screen[row][i][1] & 15;  // 4 mode bits
+                var f = (this.screen[row][i][1] & (15 << 4)) >> 4; // 4 foreground bits
+                var b = (this.screen[row][i][1] & (15 << 8)) >> 8; // 4 background bits
+                if ( m != mode ) {
+                    if ( MouseApp.Modes[mode] ) html += "</" + MouseApp.Modes[mode] + ">";
+                    if ( MouseApp.Modes[m] ) html += "<" + MouseApp.Modes[m] + ">";
+                    mode = m;
+                }
+                if ( ( f != fcolor && f == 0 ) || ( b != bcolor && b == 0 ) ) {
+                    for ( var s = 0; s < spans; s++ ) html += "</span>";
+                    fcolor = 0; bcolor = 0;
+                }
+                if ( f != fcolor ) {
+                    if ( MouseApp.Colors[f] ) {
+                        html += "<span class='fore_" + MouseApp.Colors[f] + "'>";
+                        spans++;
+                    }
+                    fcolor = f;
+                }
+                if ( b != bcolor ) {
+                    if ( MouseApp.Colors[b] ) html += "<span class='back_" + MouseApp.Colors[b] + "'>";
+                    spans++; bcolor = b;
+                }
+                html += MouseApp.CharCodes[c] ? MouseApp.CharCodes[c] : String.fromCharCode(c);
+            }
+            if ( MouseApp.Modes[mode] ) html += "</" + MouseApp.Modes[mode] + ">";
+            for ( var s = 0; s < spans; s++ ) html += "</span>";
+            var new_id = this.element.attr('id') + '_' + row;
+            if (!$('#' + new_id).get(0)) {
+                this.element.append("<div id='" + new_id + "'>&nbsp;</div>");
+                this.scrollAllTheWayDown();
+            }
+            $('#' + new_id).html(html);
+        }
+    },
+
+    onAfterKey: function() {
+        this.scrollAllTheWayDown();
+    },
+
+    highlightLine: function(i) {
+        if (i >= 0 && i < this.screen.length)
+        {
+            $("#" + this.element.attr('id') + "_" + i);
+        }
+    },
+
+    scrollToLine: function(i) {
+        var p = this.element[0].parentNode;
+        if ( p.scrollHeight > p.clientHeight ) {
+            p.scrollTop = (p.scrollHeight - p.clientHeight);
+        }
+    },
+
+    scrollAllTheWayDown: function() {
+        var p = this.element[0].parentNode;
+        if ( p.scrollHeight > p.clientHeight ) {
+            p.scrollTop = (p.scrollHeight - p.clientHeight);
+        }
+    },
+
+    putc: function(ch, mode) {
+        if ( ch == 13 ) {
+            return;
+        } else if ( ch == 10 ) {
+            this.screen[this.rpos][this.cpos] = [ch, mode];
+            this.advanceLine();
+        } else {
+            this.screen[this.rpos][this.cpos] = [ch, mode];
+            this.paint(this.rpos);
+            this.advance();
+        }
+    },
+
+    zpad: function(n) {
+        if (n < 10) n = "0" + n;
+        return n;
+    },
+
+    puts: function(str, mode) {
+        if ( !str ) return;
+        var p = this.painting;
+        var r = this.rpos;
+        this.painting = false;
+        for ( var i = 0; i < str.length; i++ ) {
+            this.insertc(str.charCodeAt(i), mode);
+        }
+        this.painting = p;
+        this.paint(r, this.rpos);
+    },
+
+    advance: function() {
+        this.cpos++;
+        if ( this.cpos >= this.options.columns ) {
+            this.advanceLine();
+        }
+    },
+
+    advanceLine: function() {
+        this.cpos = 0;
+        this.rpos++;
+        this.ensureRow(this.rpos);
+        this.paint(this.rpos, this.screen.length - 1);
+    },
+
+    fwdc: function() {
+        var r = this.rpos;
+        var c = this.cpos;
+        if ( c < this.options.columns - 1 ) {
+            c++;
+        } else if ( r < this.screen.length - 1 ) {
+            r++;
+            c = 0;
+        }
+        var ch = (c == 0 ? this.screen[r-1][this.options.columns-1] : this.screen[r][c-1]);
+        if ( MouseApp.isPrintable(ch[0]) ) {
+            this.rpos = r;
+            this.cpos = c;
+        }
+    },
+
+    fwdLine: function() {
+        if ( this.rpos >= this.screen.length - 1 ) return;
+        this.rpos++;
+        while ( this.cpos > 0 && !MouseApp.isPrintable(this.screen[this.rpos][this.cpos - 1][0]) ) {
+            this.cpos--;
+        }
+    },
+
+    backc: function() {
+        var r = this.rpos;
+        var c = this.cpos;
+        if ( c > 0 ) {
+            c--;
+        } else if ( r > 0 ) {
+            c = this.options.columns - 1;
+            r--;
+        }
+        if ( MouseApp.isPrintable(this.screen[r][c][0]) ) {
+            this.rpos = r;
+            this.cpos = c;
+            return true;
+        }
+        return false;
+    },
+
+    getTypingStart: function() {
+        var c = this.cpos;
+        if ( !MouseApp.isPrintable(this.screen[this.rpos][c][0]) ) {
+            c--;
+        }
+        var pos = null;
+        for ( var r = this.rpos; r >= 0; r-- ) {
+            while ( c >= 0 ) {
+                if ( !MouseApp.isPrintable(this.screen[r][c][0]) ) {
+                    return pos;
+                }
+                pos = [r, c];
+                c--;
+            }
+            c = this.options.columns - 1;
+        }
+    },
+
+    getTypingEnd: function(mod) {
+        var c = this.cpos;
+        if ( !MouseApp.isPrintable(this.screen[this.rpos][c][0]) ) {
+            c--;
+        }
+        var pos = null;
+        for ( var r = this.rpos; r < this.screen.length; r++ ) {
+            while ( c < this.options.columns ) {
+                if ( !this.screen[r] || !this.screen[r][c] || !MouseApp.isPrintable(this.screen[r][c][0]) ) {
+                    if (!mod) return pos;
+                    mod--;
+                }
+                pos = [r, c];
+                c++;
+            }
+            c = 0;
+        }
+    },
+
+    getTypingAt: function(start, end) {
+        var r = start[0];
+        var c = start[1];
+        var str = '';
+        while ( r < end[0] || c <= end[1] ) {
+            if ( c < this.options.columns ) {
+                str += String.fromCharCode(this.screen[r][c][0]);
+                c++;
+            } else {
+                c = 0;
+                r++;
+            }
+        }
+        return str;
+    },
+
+    ensureRow: function(r) {
+        if (!this.screen[r]) {
+            this.screen[r] = this.fillRow(this.options.columns, 0);
+        }
+    },
+
+    insertc: function(ch, mode) {
+        var r = this.rpos; var c = this.cpos;
+        var end = this.getTypingEnd(+1);
+        if (end) {
+            var thisc = null;
+            var lastc = this.screen[this.rpos][this.cpos];
+            while ( r < end[0] || c <= end[1] ) {
+                if ( c < this.options.columns ) {
+                    thisc = this.screen[r][c];
+                    this.screen[r][c] = lastc;
+                    lastc = thisc;
+                    c++;
+                } else {
+                    c = 0;
+                    r++;
+                    this.ensureRow(r);
+                }
+            }
+            this.paint(this.rpos, end[0]);
+        }
+        this.putc(ch, mode);
+    },
+
+    delc: function() {
+        /* end of line */
+        if ( MouseApp.isPrintable(this.screen[this.rpos][this.cpos][0]) ) {
+            var end = this.getTypingEnd();
+            var thisc = null;
+            var lastc = [0, 0];
+            while ( this.rpos < end[0] || this.cpos <= end[1] ) {
+                if ( end[1] >= 0 ) {
+                    thisc = this.screen[end[0]][end[1]];
+                    this.screen[end[0]][end[1]] = lastc;
+                    lastc = thisc;
+                    end[1]--;
+                } else {
+                    end[1] = this.options.columns - 1;
+                    this.paint(end[0]);
+                    end[0]--;
+                }
+            }
+        }
+    },
+
+    backspace: function() {
+        /* end of line */
+        if ( !MouseApp.isPrintable(this.screen[this.rpos][this.cpos][0]) ) {
+            this.backc();
+            this.screen[this.rpos][this.cpos] = [0, 0];
+        } else {
+            if ( this.backc() ) this.delc();
+        }
+    },
+
+    backLine: function() {
+        if ( this.rpos < 1 ) return;
+        this.rpos--;
+        while ( this.cpos > 0 && !MouseApp.isPrintable(this.screen[this.rpos][this.cpos - 1][0]) ) {
+            this.cpos--;
+        }
+    },
+
+    onKeyPress: function(e) {
+        var ch = e.keyCode;
+        var key_name = MouseApp.CodeKeys[ch];
+        if (window.opera && !e.altKey && e.keyCode != 13 && e.keyCode != 8) key_name = null;
+        ch = (e.which || e.charCode || e.keyCode);
+        if (e.which) ch = e.which;
+        if (!key_name) { key_name = String.fromCharCode(ch); }
+        if (e.ctrlKey) { key_name = 'Ctrl' + key_name;  }
+
+        // alert([e.keyCode, e.which, key_name, this['onKey' + key_name]]);
+        if (this.typing && this.onAnyKey) this.onAnyKey(key_name);
+        if (key_name && this['onKey' + key_name]) {
+            if (this.typing) this['onKey' + key_name]();
+            MouseApp.Manager.blockEvent(e);
+            if (this.typing && this.onAfterKey) this.onAfterKey(key_name, true);
+            return false;
+        }
+        if (!e.ctrlKey) {
+            if (MouseApp.isPrintable(ch)) {
+                if (this.typing) this.insertc(ch, 0);
+                MouseApp.Manager.blockEvent(e);
+                if (this.typing && this.onAfterKey) this.onAfterKey(key_name, true);
+                return false;
+            }
+        }
+        if (this.typing && this.onAfterKey) this.onAfterKey(key_name, false);
+        return true;
+    },
+    onKeyHome: function() {
+        var s = this.getTypingStart();
+        this.rpos = s[0]; this.cpos = s[1];
+    },
+    onKeyEnd: function() {
+        var e = this.getTypingEnd(+1);
+        this.rpos = e[0]; this.cpos = e[1];
+    },
+    onKeyInsert: function() { },
+    onKeyDelete: function() { this.delc(); },
+    onKeyUp: function() { this.backLine(); },
+    onKeyLeft: function() { this.backc(); },
+    onKeyRight: function() { this.fwdc(); },
+    onKeyDown: function() { this.fwdLine(); },
+    onKeyBackspace: function() { this.backspace(); },
+    onKeyEnter: function() { this.insertc(10, 0); },
+    onKeyTab: function() { 
+      this.insertc(32, 0);
+      while (this.cpos % this.options.indent != 0) this.insertc(32, 0);
+    }
+});
+
+/* Terminal running moush */
+MouseApp.Terminal = function(element, options) {
+  this.element = $(element);
+  this.setOptions(options);
+  this.initWindow();
+  this.setup();
+};
+
+$.extend(MouseApp.Terminal.prototype, MouseApp.Window.prototype, {
+    setup: function() {
+        this.history = [];
+        this.backupNum = this.historyNum = this.commandNum = 0;
+        if (this.onStart) {
+            this.onStart();
+        } else {
+            this.write(this.options.greeting + "\n", true);
+            this.prompt();
+        }
+    },
+
+    prompt: function(ps, pt) {
+        if (!ps) {
+            ps = this.options.ps; pt = true;
+        }
+        this.write(ps, pt);
+        this.putc(1, 0);
+        this.typingOn();
+    },
+
+    getCommand: function() {
+        var s = this.getTypingStart();
+        var e = this.getTypingEnd();
+        if (!s || !e) return;
+        return this.getTypingAt(s, e);
+    },
+
+    clearCommand: function() {
+        var s = this.getTypingStart();
+        var e = this.getTypingEnd();
+        if (!s || !e) return;
+        var r = s[0];
+        var c = s[1];
+        this.rpos = r; this.cpos = c;
+        while ( r < e[0] || c <= e[1] ) {
+            if ( c < this.options.columns ) {
+                this.screen[r][c] = [0, 0];
+                c++;
+            } else {
+                c = 0;
+                this.paint(r);
+                r++;
+            }
+        }
+        this.paint(r);
+    },
+
+    write: function(str, pcodes) {
+        var p = this.painting;
+        var r = this.rpos;
+        this.painting = false;
+        var mode = 0;
+        var today = new Date();
+        for ( var i = 0; i < str.length; i++ ) {
+            if ( str.substr(i,1) == "\n" ) {
+                this.advanceLine();
+                continue;
+            } else if ( str.substr(i,1) == "\033" ) {
+                if ( str.substr(i+1,2) == "[m" ) {
+                    mode = 0;
+                    i += 2;
+                    continue;
+                }
+                if ( str.substr(i+1,5) == "[0;0m" ) {
+                    mode = 0;
+                    i += 5;
+                    continue;
+                }
+                var colors = str.substr(i+1,7).match(/^\[(\d);(\d+)m/);
+                if ( colors ) {
+                    var colCode = parseInt( colors[2] );
+                    var color = colCode % 10;
+                    if ( colors[1] == '1' ) {
+                        color += 8;
+                    }
+                    if ( colCode / 10 == 4 ) {
+                        color = color << 4;
+                    }
+                    mode = (mode & 15) + color << 4;
+                    i += colors[0].length;
+                    continue;
+                }
+            } else if ( str.substr(i,1) == '%' && pcodes ) {
+                var s2 = str.substr(i,2);
+                switch ( s2 ) {
+                    case '%h':
+                        this.puts(this.options.host, mode);
+                        i++;
+                    continue;
+                    case '%l':
+                        this.puts(this.options.name, mode);
+                        i++;
+                    continue;
+                    case '%n':
+                        this.advanceLine();
+                        i++;
+                    continue;
+                    case '%s':
+                        this.puts("moush", mode);
+                        i++;
+                    continue;
+                    case '%t':
+                        this.puts(this.zpad(today.getHours()) + ":" + this.zpad(today.getMinutes()) + ":" +
+                            this.zpad(today.getSeconds()), mode);
+                        i++;
+                    continue;
+                    case '%u':
+                        this.puts(this.options.user, mode);
+                        i++;
+                    continue;
+                    case '%v':
+                        this.puts(MouseApp.Version, mode);
+                        i++;
+                    continue;
+                    case '%!':
+                        this.puts(this.historyNum.toString(), mode);
+                        i++;
+                    continue;
+                    case '%#':
+                        this.puts(this.commandNum.toString(), mode);
+                        i++;
+                    continue;
+                    case '%+':
+                        var kind = str.substr(i+2, 1);
+                        if ( MouseApp.ModeIds[kind] ) {
+                            mode = mode | MouseApp.ModeIds[kind];
+                            i += 2;
+                            continue;
+                        }
+                    break;
+                    case '%-':
+                        var kind = str.substr(i+2, 1);
+                        if ( MouseApp.ModeIds[kind] ) {
+                            mode = mode & ( 4095 - MouseApp.ModeIds[kind] );
+                            i += 2;
+                            continue;
+                        }
+                    break;
+                }
+            }
+            this.putc(str.charCodeAt(i), mode);
+        }
+        this.painting = p;
+        this.paint(r, this.rpos);
+    },
+
+    onKeyUp: function() {
+        if ( this.backupNum == 0 ) return;
+        if ( this.backupNum == this.historyNum ) {
+            this.history[this.historyNum] = this.getCommand();
+        }
+        this.clearCommand();
+        this.backupNum--;
+        this.puts(this.history[this.backupNum]);
+    },
+    onKeyDown: function() {
+        if ( this.backupNum >= this.historyNum ) return;
+        this.clearCommand();
+        this.backupNum++;
+        this.puts(this.history[this.backupNum]);
+    },
+    onKeyEnter: function() {
+        var cmd = this.getCommand();
+        if (cmd) {
+            this.history[this.historyNum] = cmd;
+            this.backupNum = ++this.historyNum;
+        }
+        this.commandNum++;
+        this.advanceLine();
+        if (cmd) {
+            var str = this.onCommand(cmd);
+            if (str) {
+                if ( str.substr(str.length - 1, 1) != "\n" ) {
+                    str += "\n";
+                }
+                this.write(str);
+            }
+        }
+        this.prompt();
+    },
+    onCommand: function(line) {
+        // this.puts("Echoing: " + line + "\n");
+        if ( line == "clear" ) {
+            this.clear();
+        } else {
+            return "\033[1;37m\033[0;44mYou typed:\033[m " + line;
+        }
+    }
+});
+
+/* Notepad sort of editor */
+MouseApp.Notepad = function(element, options) {
+  this.element = $(element);
+  this.setOptions(options);
+  this.initWindow();
+  this.history = [];
+  this.lineno = 0;
+};
+
+$.extend(MouseApp.Notepad.prototype, MouseApp.Window.prototype, {
+    csave: function() {
+        if ( this.cpos_save ) {
+            this.cpos = this.cpos_save;
+        } else {
+            this.cpos_save = this.cpos;
+        }
+    },
+    onKeyUp: function() { if ( this.rpos < 1 ) { return; } this.csave(); this.backLine(); },
+    onKeyDown: function() { if ( this.rpos < this.screen.length - 1 ) { this.csave(); this.fwdLine(); } },
+    onAfterKey: function(key, st) {
+        if ( st && !(key == 'Up' || key == 'Down') ) {
+            this.cpos_save = null;
+        }
+    },
+    insertc: function(ch, mode) {
+        if (ch == 10) {
+            this.element.append("<div id='" + this.element.attr('id') + '_' + this.screen.length + "'>&nbsp;</div>");
+            this.screen.splice(this.rpos + 1, 0, this.fillRow(this.options.columns, 0));
+            var c = this.cpos; var c2 = 0;
+            while (c < this.options.columns)
+            {
+                if (this.screen[this.rpos][c] == 0) break;
+                this.screen[this.rpos + 1][c2] = this.screen[this.rpos][c];
+                this.screen[this.rpos][c] = [0, 0];
+                c++; c2++;
+            }
+            this.paint(this.rpos);
+            if (MouseApp.isPrintable(this.screen[this.rpos][c]))
+            {
+                var r = this.rpos; var c = this.cpos;
+                this.rpos += 1; this.cpos = c2;
+                this.delc();
+                this.rpos = r; this.cpos = c;
+            }
+            this.putc(ch, mode);
+            if (this.rpos == this.screen.length - 1)
+              this.scrollAllTheWayDown();
+        } else {
+            var c = this.cpos + 1;
+            var lastc = this.screen[this.rpos][this.cpos];
+            this.putc(ch, mode);
+            for ( var r = this.rpos; r < this.screen.length; r++ ) {
+                while (c < this.options.columns)
+                {
+                    var tmpc = this.screen[r][c];
+                    if (lastc[0] == 0)
+                        break;
+                    this.screen[r][c] = lastc;
+                    lastc = tmpc;
+                    c++;
+                }
+                if (c < this.options.columns) {
+                    break;
+                }
+                c = 0;
+            }
+        }
+    },
+
+    backc: function() {
+        var r = this.rpos;
+        var c = this.cpos - 1;
+        for ( var r = this.rpos; r >= 0; r-- ) {
+            while ( c >= 0 ) {
+                this.rpos = r;
+                this.cpos = c;
+                if ( this.screen[r][c][0] != 0 ) {
+                    this.paint(r);
+                    return true;
+                }
+                this.screen[r][c] = [0, 0];
+                c--;
+            }
+            c = this.options.columns - 1;
+        }
+        return false;
+    },
+    delc: function() {
+        var c = this.cpos + 1;
+        for ( var r = this.rpos; r < this.screen.length; r++ ) {
+            while ( c < this.options.columns ) {
+                if ( this.screen[r][c][0] != 0 ) {
+                    break;
+                }
+                c++;
+            }
+            if ( c < this.options.columns ) break;
+            c = 0;
+        }
+
+        if (r >= this.screen.length) return;
+
+        var r2 = this.rpos;
+        var c2 = this.cpos;
+        for ( var r2 = this.rpos; r2 < this.screen.length; r2++ ) {
+            while (c2 < this.options.columns)
+            {
+                if (this.screen[r][c][0] == 0)
+                    break;
+                this.screen[r2][c2] = this.screen[r][c];
+                c2++;
+                c++;
+                if (c >= this.options.columns) {
+                  r++;
+                  if (r >= this.options.rows) break;
+                  c = 0;
+                }
+            }
+            if (c2 < this.options.columns) {
+                while (c2 < this.options.columns) {
+                  this.screen[r2][c2] = [0, 0];
+                  c2++;
+                }
+                break;
+            }
+            c2 = 0;
+        }
+
+        if (r != r2 && r < this.screen.length)
+        {
+            this.screen.splice(r, 1);
+            $("#" + this.element.attr('id') + "_" + this.screen.length).remove();
+        }
+        this.paint(this.rpos, this.screen.length);
+    },
+    onKeyBackspace: function() {
+        if (this.backc()) this.delc();
+    }
+
+});

Added: trunk/tools/mouseirb_2.js
==============================================================================
--- (empty file)
+++ trunk/tools/mouseirb_2.js	Fri Dec 26 14:37:32 2008
@@ -0,0 +1,143 @@
+//
+// Copyright (c) 2008 why the lucky stiff
+// 
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software,
+// and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+// SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+// OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+
+/* Irb running moush */
+MouseApp.Irb = function(element, options) {
+  this.element = $(element);
+  this.setOptions(options);
+  if ( this.options.init ) {
+      this.init = this.options.init;
+  }
+  this.initWindow();
+  this.setup();
+  this.irbInit = false;
+  this.fireOffCmd(".c", (function(r) {
+                                        /*alert (r.responseText);*/
+                                        var xmlDoc=r.responseXML.documentElement;
+                                        var txt;
+                                        if (xmlDoc.getElementsByTagName("cmde")[0].childNodes[0])
+                                            txt = xmlDoc.getElementsByTagName("cmde")[0].childNodes[0].nodeValue;
+                                        var pt = xmlDoc.getElementsByTagName("prompt")[0].childNodes[0].nodeValue;
+                                }));
+};
+
+$.extend(MouseApp.Irb.prototype, MouseApp.Terminal.prototype, {
+    cmdToQuery: function(cmd) {
+        return "cmd=" + escape(cmd.replace(/&lt;/g, '<').replace(/&gt;/g, '>').
+            replace(/&amp;/g, '&').replace(/\r?\n/g, "\n")).replace(/\+/g, "%2B") +
+	    "&cid=" + this.options.gdaid;
+    },
+
+    fireOffCmd: function(cmd, func) {
+      var irb = this;
+        if (!this.irbInit)
+        {
+          $.ajax({url: this.options.irbUrl + "?" + this.cmdToQuery("!INIT!IRB!"), type: "GET",
+				  complete: (function(r) { 
+						  irb.irbInit = true; 
+						  var xmlDoc=r.responseXML.documentElement;
+						  var cid = xmlDoc.getElementsByTagName("cid")[0].childNodes[0].nodeValue;
+						  irb.options.gdaid = cid;
+						  irb.fireOffCmd(cmd, func); 
+					  }), 
+				  type:"xml"});
+        }
+        else
+        {
+          $.ajax({url: this.options.irbUrl + "?" + this.cmdToQuery(cmd), type: "GET", 
+            complete: func});
+        }
+    },
+
+    reply: function(str,prompt) {
+        var raw = str.replace(/\033\[(\d);(\d+)m/g, '');
+        if (str != "..") {
+            if ( str[str.length - 1] != "\n" ) {
+                str += "\n";
+            }
+            js_payload = /\033\[1;JSm(.*)\033\[m/;
+            js_in = str.match(js_payload);
+            if (js_in) {
+                try {
+                    js_in = eval(js_in[1]);
+                } catch (e) {}
+                str = str.replace(js_payload, '');
+            }
+            var pr_re = new RegExp("(^|\\n)=>");
+            if ( str.match( pr_re ) ) {
+              str = str.replace(new RegExp("(^|\\n)=>"), "$1\033[1;34m=>\033[m");
+            } else {
+              str = str.replace(new RegExp("(^|\\n)= (.+?) ="), "$1\033[1;33m$2\033[m");
+            }
+            this.write(str);
+	    if (prompt) {
+		    var trimmed = prompt.replace(/^\s+|\s+$/g, '') ;
+		    this.options.ps = "\033[1;31m" + trimmed + "\033[m";
+	    }
+            this.prompt();
+        } else {
+            this.prompt("\033[1;32m..\033[m", true);
+        }
+    },
+
+    onKeyCtrll: function() {
+	this.clear();
+        this.prompt();
+        //this.clearCommand();
+        //this.puts("reset");
+        //this.onKeyEnter();
+    },
+
+    onKeyEnter: function() {
+        this.typingOff();
+        var cmd = this.getCommand();
+        if (cmd) {
+            this.history[this.historyNum] = cmd;
+            this.backupNum = ++this.historyNum;
+        }
+        this.commandNum++;
+        this.advanceLine();
+        if (cmd) {
+            if ( cmd == ".clear" ) {
+                this.clear();
+                this.prompt();
+            } else {
+                var term = this;
+                this.fireOffCmd(cmd, (function(r) {
+					/*alert (r.responseText);*/
+					var xmlDoc=r.responseXML.documentElement;
+					var txt;
+					if (xmlDoc.getElementsByTagName("cmde")[0].childNodes[0])
+					    txt = xmlDoc.getElementsByTagName("cmde")[0].childNodes[0].nodeValue;
+					var pt = xmlDoc.getElementsByTagName("prompt")[0].childNodes[0].nodeValue;
+					term.reply(txt ? txt: '', pt ? pt : null);
+				}));
+            }
+        } else {
+            this.prompt();
+        }
+    }
+});
+

Modified: trunk/tools/web-server.c
==============================================================================
--- trunk/tools/web-server.c	(original)
+++ trunk/tools/web-server.c	Fri Dec 26 14:37:32 2008
@@ -23,9 +23,19 @@
 #include <glib/gstdio.h>
 #include <stdarg.h>
 #include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
 #include "web-server.h"
 #include <libsoup/soup.h>
 #include "html-doc.h"
+#include "binreloc/sql-binreloc.h"
+
+/* Use the RSA reference implementation included in the RFC-1321, http://www.freesoft.org/CIE/RFC/1321/ */
+#include "global.h"
+#include "md5.h"
+
+#define MAX_CHALLENGES 10
+#define MAX_AUTH_COOKIES 10
 
 /* 
  * Main static functions 
@@ -38,19 +48,43 @@
 /* get a pointer to the parents to be able to call their destructor */
 static GObjectClass  *parent_class = NULL;
 
-
 struct _WebServerPrivate
 {
 	SoupServer  *server;
-	GHashTable  *tmpdata_hash; /* key = a path without the starting '/', value = a TmpData pointer */
-	GSList      *tmpdata_list; /* list of the TmpData pointers in @tmpdata_hash, memory not managed here */
-	guint        timer;
+	GHashTable  *ressources_hash; /* key = a path without the starting '/', value = a TmpRessource pointer */
+	GSList      *ressources_list; /* list of the TmpRessource pointers in @ressources_hash, memory not managed here */
+	guint        ressources_timer;
+
+	/* authentication */
+	gchar       *token; /* FIXME: protect it! */
+	GArray      *challenges; /* array of TimedString representing the currently valid challenges */
+	GArray      *cookies; /* array of TimedString representing the currently valid authentication cookie values */
+	guint        auth_timer;
+
+	GSList      *terminals_list; /* list of SqlConsole */
+	guint        term_timer;
 };
 
+
+typedef struct {
+	gchar    *string;
+	GTimeVal  validity;
+} TimedString;
+
+static TimedString *timed_string_new (guint duration);
+static void         timed_string_free (TimedString *ts);
+
+static TimedString *challenge_add (WebServer *server);
+static void         challenges_manage (WebServer *server);
+
+static TimedString *auth_cookie_add (WebServer *server);
+static void         auth_cookies_manage (WebServer *server);
+
+
 /*
  * Temporary available data
  *
- * Each TmpData structure represents a ressource which will be available for some time (until it has
+ * Each TmpRessource structure represents a ressource which will be available for some time (until it has
  * expired).
  *
  * If the expiration_date attribute is set to 0, then there is no expiration at all.
@@ -60,336 +94,12 @@
 	gchar *data;
 	gsize  size;
 	int    expiration_date; /* 0 to avoid expiration */
-} TmpData;
-
-static gboolean
-delete_tmp_data (WebServer *server)
-{
-	GSList *list;
-	GTimeVal tv;
-	gint n_timed = 0;
-
-	g_get_current_time (&tv);
-	for (list = server->priv->tmpdata_list; list; ) {
-		TmpData *td = (TmpData *) list->data;
-		if ((td->expiration_date > 0) && (td->expiration_date < tv.tv_sec)) {
-			GSList *n = list->next;
-			g_hash_table_remove (server->priv->tmpdata_hash, td->path);
-			server->priv->tmpdata_list = g_slist_delete_link (server->priv->tmpdata_list, list);
-			list = n;
-		}
-		else {
-			if (td->expiration_date > 0)
-				n_timed ++;
-			list = list->next;
-		}
-	}
-	if (n_timed == 0) {
-		server->priv->timer = 0;
-		return FALSE;
-	}
-	else
-		return TRUE;
-}
-
-/*
- * @data is stolen!
- */
-static TmpData *
-tmp_data_add (WebServer *server, const gchar *path, gchar *data, gsize data_length)
-{
-	TmpData *td;
-	GTimeVal tv;
-
-	g_get_current_time (&tv);
-	td = g_new0 (TmpData, 1);
-	td->path = g_strdup (path);
-	td->data = data;
-	td->size = data_length;
-	td->expiration_date = tv.tv_sec + 30;
-	g_hash_table_insert (server->priv->tmpdata_hash, g_strdup (path), td);
-	server->priv->tmpdata_list = g_slist_prepend (server->priv->tmpdata_list, td);
-	if (!server->priv->timer)
-		server->priv->timer = g_timeout_add_seconds (5, (GSourceFunc) delete_tmp_data, server);
-	return td;
-}
-
-/*
- * @data is static
- */
-static TmpData *
-tmp_static_data_add (WebServer *server, const gchar *path, gchar *data, gsize data_length)
-{
-	TmpData *td;
-
-	td = g_new0 (TmpData, 1);
-	td->path = g_strdup (path);
-	td->data = data;
-	td->size = data_length;
-	td->expiration_date = 0;
-	g_hash_table_insert (server->priv->tmpdata_hash, g_strdup (path), td);
-	server->priv->tmpdata_list = g_slist_prepend (server->priv->tmpdata_list, td);
-	return td;
-}
-
-static void
-tmp_data_free (TmpData *data)
-{
-	g_free (data->data);
-	g_free (data);
-}
-
-#define GDA_CSS \
-"body {" \
-"        margin: 0px;" \
-"        background-color: white;" \
-"        font-family: sans-serif;" \
-"        color: black;" \
-"}" \
-"" \
-"a {" \
-"    color: #0000ff;" \
-"    border: 0px;" \
-"}" \
-"" \
-"a:active {" \
-"        color: #ff0000;" \
-"}" \
-"" \
-"a:visited {" \
-"        color: #551a8b;" \
-"}" \
-"" \
-"" \
-"#container" \
-"{" \
-"    width: 97%;" \
-"    margin: 1%;" \
-"    background-color: #fff;" \
-"    color: #333;" \
-"}" \
-"" \
-"" \
-"" \
-"#top" \
-"{" \
-"    background: #729FCF;" \
-"    float: left;" \
-"    width: 100%;" \
-"    font-size: 75%;" \
-"}" \
-"" \
-"#top h1" \
-"{" \
-"    margin: 0;" \
-"    margin-left: 85px;" \
-"    padding-top: 20px;" \
-"    padding-bottom: 20px;" \
-"    color: #eeeeec;" \
-"}" \
-"" \
-"#top ul {" \
-"    list-style: none;" \
-"    text-align: right;" \
-"    padding: 0 1ex;" \
-"    margin: 0;" \
-"    font-size: 85%;" \
-"}" \
-"" \
-"#top li a {" \
-"    font-weight: bold;" \
-"    color: #FFFFFF;" \
-"    margin: 0 2ex;" \
-"    text-decoration: none;" \
-"    line-height: 30px;" \
-"" \
-"}" \
-"" \
-"" \
-"/*" \
-" * Left naivgation pane" \
-" */" \
-"#leftnav" \
-"{" \
-"    float: left;" \
-"    width: 140px;" \
-"    margin: 0;" \
-"    padding-top: 5;" \
-"" \
-"    background: #2E3436;" \
-"    color: #FFFFFF;" \
-"}" \
-"" \
-"#leftnav ul {" \
-"    font-weight: bold;" \
-"    list-style: none;" \
-"    padding: 0 10px 10px;;" \
-"    margin: 0 0 0 0;" \
-"    font-size: 90%;" \
-"}" \
-"" \
-"#leftnav li a {" \
-"    font-weight: normal;" \
-"    color: #FFFFFF;" \
-"    margin: 0 0 0 0;" \
-"    padding: 0 10px;" \
-"    text-decoration: none;" \
-"    font-size: 80%;" \
-"}" \
-"" \
-"#leftnav p { margin: 0 0 1em 0; }" \
-"" \
-"/* " \
-" * Content" \
-" */" \
-"#content" \
-"{" \
-"    /*background: red;*/" \
-"    margin-left: 140px;" \
-"    padding: 5em 1em;" \
-"}" \
-"" \
-"#content h1" \
-"{ " \
-"    /*background: green;*/" \
-"    margin: 5em 0 .5em 0 0;" \
-"    font-size: 100%;" \
-"}" \
-"" \
-"#content h2" \
-"{ " \
- "    /*background: green;*/"			\
-"    padding-left: 5;" \
-"    font-size: 80%;" \
-"}" \
-"" \
-"#content ul {" \
-"    font-weight: bold;" \
-"    list-style: none;" \
-"    padding: 0 10px 10px;;" \
-"    margin: 0 0 0 0;" \
-"    font-size: 90%;" \
-"}" \
-"" \
-"#content li {" \
-"    font-weight: normal;" \
-"    margin: 0 0 0 0;" \
-"    padding: 0 10px;" \
-"    text-decoration: none;" \
-"    font-size: 80%;" \
-"}" \
-"" \
-"div.clist" \
-"{" \
-"    /*background: blue;*/" \
-"    padding: 0;" \
-"    overflow: hidden;" \
-"}" \
-"" \
-".clist ul" \
-"{" \
-"    /*background: lightgray;*/" \
-"    margin-bottom: 0;" \
-"" \
-"    float: left;" \
-"    width: 100%;" \
-"    margin: 0;" \
-"    margin-left: 10px;" \
-"    padding: 0;" \
-"    list-style: none;" \
-"}" \
-"" \
-".clist li" \
-"{" \
-"    /*background: lightblue;*/" \
-"    float: left;" \
-"    width: 33%;" \
-"    margin: 0;" \
-"    padding: 0;" \
-"    font-size: 90%;" \
-"    /*word-wrap: break-word;*/" \
-"}" \
-"" \
-".clist a" \
-"{" \
-"    font-weight: normal;" \
-"    color: #050505;" \
-"    margin: 0 0 0 0;" \
-"    padding: 0 0 0 0;" \
-"    text-decoration: none;" \
-"}" \
-"" \
-".clist br" \
-"{" \
-"    clear: both;" \
-"}" \
-"" \
-"table.ctable" \
-"{" \
-"    font-weight: normal;" \
-"    font-size: 90%;" \
-"    width: 100%;" \
-"    background-color: #fafafa;" \
-"    border: 1px #6699CC solid;" \
-"    border-collapse: collapse;" \
-"    border-spacing: 0px;" \
-"    margin-top: 0px;" \
-"    margin-bottom: 5px;" \
-"}" \
-"" \
-".ctable th" \
-"{" \
-"    border-bottom: 2px solid #6699CC;" \
-"    background-color: #729FCF;" \
-"    text-align: center;" \
-"    font-weight: bold;" \
-"    color: #eeeeec;" \
-"}" \
-"" \
-".ctable td" \
-"{" \
-"    padding-left: 2px;" \
-"    border-left: 1px dotted #729FCF;" \
-"}" \
-"" \
-".graph" \
-"{" \
-"    /*background: lightblue;*/" \
-"    padding: 0;" \
-"}" \
-"" \
-".graph img" \
-"{" \
-"    max-width: 100%;" \
-"    height: auto;" \
-"    border: 0;" \
-"}" \
-"" \
-".pkey" \
-"{" \
-"    /*background: lightblue;*/" \
-"    color: blue;" \
-"    font-weight: bold;" \
-"}" \
-"" \
-".ccode" \
-"{" \
-"    /*background: lightblue;*/" \
-"    padding-left: 5px;" \
-"}" \
-"" \
-"/*" \
-" * Footer" \
-" */" \
-"#footer" \
-"{" \
-"    clear: both;" \
-"    margin: 0;" \
-"    padding: 2;" \
-"    color: #eeeeec;" \
-"    background: #729FCF;" \
-"}"
+} TmpRessource;
 
+static TmpRessource  *tmp_ressource_add (WebServer *server, const gchar *path, gchar *data, gsize data_length);
+static TmpRessource  *tmp_static_data_add (WebServer *server, const gchar *path, gchar *data, gsize data_length);
+static gboolean       delete_tmp_ressource (WebServer *server);
+static void           tmp_ressource_free (TmpRessource *data);
 
 /* module error */
 GQuark web_server_error_quark (void)
@@ -445,83 +155,167 @@
 web_server_init (WebServer *server)
 {
 	server->priv = g_new0 (WebServerPrivate, 1);
-	server->priv->timer = 0;
-	server->priv->tmpdata_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
-							    g_free, (GDestroyNotify) tmp_data_free);
-	server->priv->tmpdata_list = NULL;
-
-	tmp_static_data_add (server, "gda.css", GDA_CSS, strlen (GDA_CSS));
-}
-
-
-static gboolean get_file (SoupServer *server, SoupMessage *msg, const char *path, GError **error);
-static void     get_root (SoupServer *server, SoupMessage *msg);
+	server->priv->ressources_timer = 0;
+	server->priv->ressources_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+							    g_free, (GDestroyNotify) tmp_ressource_free);
+	server->priv->ressources_list = NULL;
+
+	server->priv->token = g_strdup ("");
+	server->priv->challenges = g_array_new (FALSE, FALSE, sizeof (TimedString*));
+	server->priv->cookies = g_array_new (FALSE, FALSE, sizeof (TimedString*));
+	server->priv->auth_timer = 0;
+
+	server->priv->terminals_list = NULL;
+	server->priv->term_timer = 0;
+}
+
+static void     get_cookies (SoupMessage *msg, ...);
+static gboolean get_file (WebServer *server, SoupMessage *msg, const char *path, GError **error);
+static void     get_root (WebServer *server, SoupMessage *msg);
+static void     get_for_console (WebServer *server, SoupMessage *msg);
 static gboolean get_for_cnc (WebServer *webserver, SoupMessage *msg, 
 			     const ConnectionSetting *cs, gchar **extra, GError **error);
+static gboolean get_auth (WebServer *server, SoupMessage *msg, GHashTable *query);
+static gboolean get_post_for_irb (WebServer *webserver, SoupMessage *msg, 
+				  const ConnectionSetting *cs, GHashTable *query, GError **error);
+static void     get_for_cnclist (WebServer *webserver, SoupMessage *msg);
 
 
+/*#define DEBUG_SERVER*/
+#ifdef DEBUG_SERVER
+static void
+debug_display_query (gchar *key, gchar *value, gpointer data)
+{
+	g_print ("\t%s => %s\n", key, value);
+}
+#endif
+
 static void
 server_callback (SoupServer *server, SoupMessage *msg,
                  const char *path, GHashTable *query,
                  SoupClientContext *context, WebServer *webserver)
 {
-	/*#define DEBUG_SERVER*/
 #ifdef DEBUG_SERVER
         printf ("%s %s HTTP/1.%d\n", msg->method, path, soup_message_get_http_version (msg));
+	/*
         SoupMessageHeadersIter iter;
         const char *name, *value;
         soup_message_headers_iter_init (&iter, msg->request_headers);
         while (soup_message_headers_iter_next (&iter, &name, &value))
                 printf ("%s: %s\n", name, value);
+	*/
         if (msg->request_body->length)
                 printf ("Request body: %s\n", msg->request_body->data);
+	if (query) {
+		printf ("Query parts:\n");
+		g_hash_table_foreach (query, (GHFunc) debug_display_query, NULL);
+	}
+#endif
+	
+	GError *error = NULL;
+	gboolean ok = TRUE;
+	gboolean done = FALSE;
+	TmpRessource *tmpdata;
+	if ((*path != '/') || (*path && (path[1] == '/'))) {
+		soup_message_set_status_full (msg, SOUP_STATUS_UNAUTHORIZED, "Wrong path name");
+		return;
+	}
+	path++;
+
+	/* check for authentication */
+	gboolean auth_needed = TRUE;
+	if (g_str_has_suffix (path, ".js") ||
+	    g_str_has_suffix (path, ".css"))
+		auth_needed = FALSE;
+	if (auth_needed) {
+		/* check cookie named "coa" */
+		gchar *cookie;
+		get_cookies (msg, "coa", &cookie, NULL);
+
+		if (cookie) {
+			gint n;
+			for (n = 0; n < webserver->priv->cookies->len; n++) {
+				TimedString *ts = g_array_index (webserver->priv->cookies, TimedString *, n);
+#ifdef DEBUG_SERVER
+				g_print ("CMP Cookie %s with msg's cookie %s\n",
+					 ts->string, cookie);
 #endif
+				if (!strcmp (ts->string, cookie)) {
+					/* cookie exists => we are authenticated */
+					auth_needed = FALSE;
+					break;
+				}
+			}
+			g_free (cookie);
+		}
+
+		if (auth_needed) {
+			if (!get_auth (webserver, msg, query))
+				return;
+		}
+	}
 	
-        if (msg->method == SOUP_METHOD_GET) {
-		GError *error = NULL;
-		gboolean ok = TRUE;
-		TmpData *tmpdata;
-		if (*path != '/') {
-			soup_message_set_status_full (msg, SOUP_STATUS_UNAUTHORIZED, "Wrong path name");
-			return;
-		}
-		path++;
-
-		if (*path == 0)
-			get_root (server, msg);
-		else if ((tmpdata = g_hash_table_lookup (webserver->priv->tmpdata_hash, path))) {
+	if (*path == 0) {
+		if (msg->method == SOUP_METHOD_GET)  {
+			get_root (webserver, msg);
+			done = TRUE;
+		}
+	}
+	else if ((tmpdata = g_hash_table_lookup (webserver->priv->ressources_hash, path))) {
+		if (msg->method == SOUP_METHOD_GET) {
 			soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC,
 						  tmpdata->data, tmpdata->size);
 			soup_message_set_status (msg, SOUP_STATUS_OK);
+			done = TRUE;
 		}
-		else {
-			gchar **array = NULL;
-			array = g_strsplit (path, "/", 0);
-
-			const ConnectionSetting *cs;
-			cs = gda_sql_get_connection (array[0]);
-
-			if (cs) 
+	}
+	else {
+		gchar **array = NULL;
+		array = g_strsplit (path, "/", 0);
+		
+		const ConnectionSetting *cs;
+		cs = gda_sql_get_connection (array[0]);
+		
+		if (cs) {
+			if (msg->method == SOUP_METHOD_GET) {
 				ok = get_for_cnc (webserver, msg, cs, array[1] ? &(array[1]) : NULL, &error);
-			else {
-				/*ok = get_file (webserver, msg, path, &error);*/
-				ok = FALSE;
+				done = TRUE;
 			}
-			if (array)
-				g_strfreev (array);
 		}
-
-		if (!ok) {
-			if (error) {
-				soup_message_set_status_full (msg, error->code, error->message);
-				g_error_free (error);
+		else if (!strcmp (path, "~console")) {
+			get_for_console (webserver, msg);
+			done = TRUE;
+		}
+		else if (!strcmp (path, "~irb")) {
+			ok = get_post_for_irb (webserver, msg, cs, query, &error);
+			done = TRUE;
+		}
+		else if (!strcmp (path, "~cnclist")) {
+			get_for_cnclist (webserver, msg);
+			done = TRUE;
+		}
+		else {
+			if (msg->method == SOUP_METHOD_GET) {
+				ok = get_file (webserver, msg, path, &error);
+				done = TRUE;
 			}
-			else
-				soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
 		}
+		if (array)
+			g_strfreev (array);
+	}
+	
+	if (!ok) {
+		if (error) {
+			soup_message_set_status_full (msg, error->code, error->message);
+			g_error_free (error);
+		}
+		else
+			soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
 	}
-        else
+        
+	if (!done)
                 soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+
 #ifdef DEBUG_SERVER
         printf ("  -> %d %s\n\n", msg->status_code, msg->reason_phrase);
 #endif
@@ -530,13 +324,14 @@
 /**
  * web_server_new
  * @type: the #GType requested
+ * @auth_token: the authentication token, or %NULL
  *
  * Creates a new server of type @type
  *
  * Returns: a new #WebServer object
  */
 WebServer *
-web_server_new (gint port)
+web_server_new (gint port, const gchar *auth_token)
 {
 	WebServer *server;
 
@@ -546,7 +341,12 @@
 						NULL);
 	soup_server_add_handler (server->priv->server, NULL,
                                  (SoupServerCallback) server_callback, server, NULL);
-	
+
+	if (auth_token) {
+		g_free (server->priv->token);
+		server->priv->token = g_strdup (auth_token);
+	}
+
 	soup_server_run_async (server->priv->server);
 
 	return server;
@@ -560,22 +360,30 @@
 
 	server = WEB_SERVER (object);
 	if (server->priv) {
-		if (server->priv->tmpdata_hash) {
-			g_hash_table_destroy (server->priv->tmpdata_hash);
-			server->priv->tmpdata_hash = NULL;
-		}
-		if (server->priv->tmpdata_list) {
-			g_slist_free (server->priv->tmpdata_list);
-			server->priv->tmpdata_list = NULL;
+		if (server->priv->ressources_hash) {
+			g_hash_table_destroy (server->priv->ressources_hash);
+			server->priv->ressources_hash = NULL;
+		}
+		if (server->priv->ressources_list) {
+			g_slist_free (server->priv->ressources_list);
+			server->priv->ressources_list = NULL;
 		}
 		if (server->priv->server) {
 			g_object_unref (server->priv->server);
 			server->priv->server = NULL;
 		}
-		if (server->priv->timer) {
-			g_source_remove (server->priv->timer);
-			server->priv->timer = 0;
-		}		
+		if (server->priv->ressources_timer) {
+			g_source_remove (server->priv->ressources_timer);
+			server->priv->ressources_timer = 0;
+		}
+		if (server->priv->auth_timer) {
+			g_source_remove (server->priv->auth_timer);
+			server->priv->auth_timer = 0;
+		}
+		if (server->priv->term_timer) {
+			g_source_remove (server->priv->term_timer);
+			server->priv->term_timer = 0;
+		}
 	}
 
 	/* parent class */
@@ -592,6 +400,23 @@
 
 	server = WEB_SERVER (object);
 	if (server->priv) {
+		gint i;
+		for (i = 0; i < server->priv->challenges->len; i++) {
+			TimedString *ts = g_array_index (server->priv->challenges, TimedString *, i);
+			timed_string_free (ts);
+		}
+		g_array_free (server->priv->challenges, TRUE);
+
+		for (i = 0; i < server->priv->cookies->len; i++) {
+			TimedString *ts = g_array_index (server->priv->cookies, TimedString *, i);
+			timed_string_free (ts);
+		}
+		g_array_free (server->priv->cookies, TRUE);
+
+		if (server->priv->terminals_list) {
+			g_slist_foreach (server->priv->terminals_list, (GFunc) gda_sql_console_free, NULL);
+			g_slist_free (server->priv->terminals_list);
+		}
 		g_free (server->priv);
 	}
 
@@ -600,13 +425,44 @@
 }
 
 /*
+ *
+ * Server GET/POST methods
+ *
+ */
+static HtmlDoc *create_new_htmldoc (WebServer *webserver, const ConnectionSetting *cs);
+static void get_variables (SoupMessage *msg, GHashTable *query, ...);
+
+/*
  * GET for a file
  */
 static gboolean
-get_file (SoupServer *server, SoupMessage *msg, const char *path, GError **error)
+get_file (WebServer *server, SoupMessage *msg, const char *path, GError **error)
 {
 	GMappedFile *mfile;
-        mfile = g_mapped_file_new (path, FALSE, error);
+	gchar *real_path;
+
+	real_path = sql_gbr_get_file_path (SQL_DATA_DIR, "libgda-4.0", "web", path, NULL);
+	if (!real_path)
+		return FALSE;
+
+	/*g_print ("get_file () => %s\n", real_path);*/
+	if (!g_file_test (real_path, G_FILE_TEST_EXISTS)) {
+		/* test if we are in the compilation directory */
+		gchar *cwd, *tmp;
+		cwd = g_get_current_dir ();
+		tmp = g_build_filename (cwd, "gda-sql.c", NULL);
+		if (g_file_test (tmp, G_FILE_TEST_EXISTS)) {
+			g_free (real_path);
+			real_path = g_build_filename (cwd, path, NULL);
+		}
+		else {
+			g_free (cwd);
+			return FALSE;
+		}
+		g_free (cwd);
+	}
+        mfile = g_mapped_file_new (real_path, FALSE, error);
+	g_free (real_path);
 	if (!mfile)
 		return FALSE;
 
@@ -620,11 +476,180 @@
 	return TRUE;
 }
 
+#define PAD_LEN 64  /* PAD length */
+#define SIG_LEN 16  /* MD5 digest length */
+/*
+ * From RFC 2104
+ */
+static void
+hmac_md5 (uint8_t*  text,            /* pointer to data stream */
+	  int   text_len,            /* length of data stream */
+	  uint8_t*  key,             /* pointer to authentication key */
+	  int   key_len,             /* length of authentication key */
+	  uint8_t  *hmac)            /* returned hmac-md5 */
+{
+	MD5_CTX md5c;
+	uint8_t k_ipad[PAD_LEN];    /* inner padding - key XORd with ipad */
+	uint8_t k_opad[PAD_LEN];    /* outer padding - key XORd with opad */
+	uint8_t keysig[SIG_LEN];
+	int i;
+
+	/* if key is longer than PAD length, reset it to key=MD5(key) */
+	if (key_len > PAD_LEN) {
+		MD5_CTX md5key;
+
+		MD5Init (&md5key);
+		MD5Update (&md5key, key, key_len);
+		MD5Final (keysig, &md5key);
+
+		key = keysig;
+		key_len = SIG_LEN;
+	}
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(Key XOR opad, MD5(Key XOR ipad, text))
+	 *
+	 * where Key is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* Zero pads and store key */
+	memset (k_ipad, 0, PAD_LEN);
+	memcpy (k_ipad, key, key_len);
+	memcpy (k_opad, k_ipad, PAD_LEN);
+
+	/* XOR key with ipad and opad values */
+	for (i=0; i<PAD_LEN; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/* perform inner MD5 */
+	MD5Init (&md5c);                    /* start inner hash */
+	MD5Update (&md5c, k_ipad, PAD_LEN); /* hash inner pad */
+	MD5Update (&md5c, text, text_len);  /* hash text */
+	MD5Final (hmac, &md5c);             /* store inner hash */
+
+	/* perform outer MD5 */
+	MD5Init (&md5c);                    /* start outer hash */
+	MD5Update (&md5c, k_opad, PAD_LEN); /* hash outer pad */
+	MD5Update (&md5c, hmac, SIG_LEN);   /* hash inner hash */
+	MD5Final (hmac, &md5c);             /* store results */
+}
+
+
+
+/*
+ * Creates a login form
+ *
+ * Returns: TRUE if the user is now authenticated, or FALSE if the user needs to authenticate
+ */
+static gboolean
+get_auth (WebServer *server, SoupMessage *msg, GHashTable *query)
+{
+	HtmlDoc *hdoc;
+	xmlChar *xstr;
+	SoupBuffer *buffer;
+	gsize size;
+
+	hdoc = html_doc_new (_("Authentication required"));
+	xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST _("Authentication required"));
+	soup_message_headers_replace (msg->response_headers,
+				      "Content-Type", "text/html");
+
+	/* check to see if this page is called as an answer to authentication */
+	gchar *token = NULL;
+	get_variables (msg, query, "etoken", &token, NULL);
+
+	if (token) {
+		gint n;
+		for (n = 0; n < server->priv->challenges->len; n++) {
+			TimedString *ts = g_array_index (server->priv->challenges, TimedString *, n);
+			uint8_t hmac[16];
+			GString *md5str;
+			gint i;
+			
+			hmac_md5 ((uint8_t *) ts->string, strlen (ts->string),
+				  (uint8_t *) server->priv->token, strlen (server->priv->token), hmac);
+			md5str = g_string_new ("");
+			for (i = 0; i < 16; i++)
+				g_string_append_printf (md5str, "%02x", hmac[i]);
+			
+			if (!strcmp (md5str->str, token)) {
+				/* forge a new location change message */
+				GString *cook;
+				gchar *tmp;
+				TimedString *new_cookie;
+				
+				new_cookie = auth_cookie_add (server);
+				cook = g_string_new ("");
+				tmp = gda_rfc1738_encode (new_cookie->string);
+				g_string_append_printf (cook, "coa=%s; path=/", tmp);
+				g_free (tmp);
+				soup_message_headers_append (msg->response_headers, "Set-Cookie", cook->str);
+				g_string_free (cook, TRUE);
+				
+				/*soup_message_set_status (msg, SOUP_STATUS_TEMPORARY_REDIRECT);
+				  soup_message_headers_append (msg->response_headers, "Location", "/");*/
+				
+				g_string_free (md5str, TRUE);
+				g_free (token);
+				
+				return TRUE;
+			}
+			g_string_free (md5str, TRUE);
+		}
+		
+		g_free (token);
+	}
+	
+
+	/* Add javascript */
+	xmlNodePtr form, node;
+	node = xmlNewChild (hdoc->head, NULL, BAD_CAST "script", BAD_CAST "");
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "text/javascript");
+	xmlSetProp (node, BAD_CAST "src", BAD_CAST "/md5.js");
+
+	/* login form */
+	TimedString *new_challenge = challenge_add (server);
+	form = xmlNewChild (hdoc->content, NULL, BAD_CAST "form", NULL);
+	gchar *str = g_strdup_printf ("javascript:etoken.value=hex_hmac_md5(token.value, '%s'); javascript:token.value=''", new_challenge->string);
+	xmlSetProp (form, BAD_CAST "onsubmit", BAD_CAST str);
+	g_free (str);
+	
+	node = xmlNewChild (form, NULL, BAD_CAST "input", BAD_CAST _("Token:"));
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "hidden");
+	xmlSetProp (node, BAD_CAST "name", BAD_CAST "etoken");
+
+	node = xmlNewChild (form, NULL, BAD_CAST "input", NULL);
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "password");
+	xmlSetProp (node, BAD_CAST "name", BAD_CAST "token");
+
+	node = xmlNewChild (form, NULL, BAD_CAST "input", NULL);
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "submit");
+	xmlSetProp (node, BAD_CAST "value", BAD_CAST "login");
+	xmlSetProp (node, BAD_CAST "colspan", BAD_CAST "2");
+
+	xstr = html_doc_to_string (hdoc, &size);
+	buffer = soup_buffer_new_with_owner (xstr, size, xstr, (GDestroyNotify)xmlFree);
+	soup_message_body_append_buffer (msg->response_body, buffer);
+	soup_buffer_free (buffer);
+	html_doc_free (hdoc);
+
+	soup_message_set_status (msg, SOUP_STATUS_OK);
+	return FALSE;
+}
+
 /*
  * GET for the / path
  */
 static void
-get_root (SoupServer *server, SoupMessage *msg)
+get_root (WebServer *server, SoupMessage *msg)
 {
 	HtmlDoc *hdoc;
 	xmlChar *xstr;
@@ -633,22 +658,14 @@
 
 	const GSList *list;
 	list = gda_sql_get_all_connections ();
-	if (0 && list && !list->next) {
+	if (list && !list->next) {
 		/* only 1 connection => go to this one */
 		ConnectionSetting *cs = (ConnectionSetting*) list->data;
 		soup_message_set_status (msg, SOUP_STATUS_TEMPORARY_REDIRECT);
 		soup_message_headers_append (msg->response_headers, "Location", cs->name);
 		return;
 	}
-	hdoc = html_doc_new (_("Database information"));
-	if (!list) {
-		/* no connection at all */
-		xmlNodePtr node;
-
-		node = xmlNewChild (hdoc->content, NULL, "h1", _("No connection opened."));
-		node = xmlNewChild (hdoc->content, NULL, "p", _("Open a connection from the console and reload this page"));
-	}
-	else {
+	else if (list) {
 		/* more than one connection, redirect to the current one */
 		const ConnectionSetting *cs = gda_sql_get_current_connection ();
 		soup_message_set_status (msg, SOUP_STATUS_TEMPORARY_REDIRECT);
@@ -656,6 +673,139 @@
 		return;
 	}
 
+	hdoc = create_new_htmldoc (server, NULL);
+	soup_message_headers_replace (msg->response_headers,
+				      "Content-Type", "text/html");
+	xstr = html_doc_to_string (hdoc, &size);
+	buffer = soup_buffer_new_with_owner (xstr, size, xstr, (GDestroyNotify)xmlFree);
+	soup_message_body_append_buffer (msg->response_body, buffer);
+	soup_buffer_free (buffer);
+	html_doc_free (hdoc);
+
+	soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+static xmlNodePtr
+cnc_ul ()
+{
+	xmlNodePtr ul, li, a;
+	const GSList *clist, *list;
+	gchar *str;
+
+	/* other connections in the sidebar */
+	list = gda_sql_get_all_connections ();
+	ul = xmlNewNode (NULL, BAD_CAST "ul");
+	xmlNodeSetContent(ul, BAD_CAST _("Connections"));
+	xmlSetProp (ul, BAD_CAST "id", BAD_CAST "cnclist");
+
+	if (!list) {
+		/* no connection at all */
+		str = g_strdup_printf ("(%s)",  _("None"));
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+		xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST str);
+		g_free (str);
+	}
+	else {
+		/*
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+		str = g_strdup_printf ("(%s)",  _("From console"));
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST str);
+		g_free (str);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST "/");
+		*/
+		for (clist = list; clist; clist = clist->next) {
+			gchar *tmp;
+			ConnectionSetting *cs2 = (ConnectionSetting*) clist->data;
+			
+			li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+			a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST cs2->name);
+			tmp = gda_rfc1738_encode (cs2->name);
+			str = g_strdup_printf ("/%s", tmp);
+			g_free (tmp);
+			xmlSetProp (a, BAD_CAST "href", BAD_CAST  str);
+			g_free (str);
+		}
+	}
+	return ul;
+}
+
+static void
+get_for_cnclist (WebServer *webserver, SoupMessage *msg)
+{
+	xmlNodePtr ul;
+	SoupBuffer *buffer;
+
+	ul = cnc_ul ();
+	soup_message_headers_replace (msg->response_headers,
+				      "Content-Type", "text/html");
+
+	xmlBufferPtr buf;
+	buf = xmlBufferCreate ();
+	xmlNodeDump (buf, NULL, ul, 1, 1);
+	xmlFreeNode (ul);
+	
+	buffer = soup_buffer_new (SOUP_MEMORY_TEMPORARY, (gchar *) xmlBufferContent (buf),
+				  strlen ((gchar *) xmlBufferContent (buf)));
+	soup_message_body_append_buffer (msg->response_body, buffer);
+	soup_buffer_free (buffer);
+	xmlBufferFree (buf);
+
+	soup_message_set_status (msg, SOUP_STATUS_OK);
+}
+
+/*
+ * GET for the /~console path
+ */
+static void
+get_for_console (WebServer *server, SoupMessage *msg)
+{
+	HtmlDoc *hdoc;
+	xmlChar *xstr;
+	SoupBuffer *buffer;
+	gsize size;
+
+	xmlNodePtr div = NULL, node;
+
+	hdoc = create_new_htmldoc (server, NULL);
+	xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST _("SQL console:"));
+
+	div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
+	xmlSetProp (div, BAD_CAST "id", BAD_CAST "terminal");
+
+	div = xmlNewChild (div, NULL, BAD_CAST "div", BAD_CAST "");
+	xmlSetProp (div, BAD_CAST "id", BAD_CAST "irb");
+
+	div = xmlNewChild (hdoc->footer, NULL, BAD_CAST "input", NULL);
+	xmlSetProp (div, BAD_CAST "class", BAD_CAST "keyboard-selector-input");
+	xmlSetProp (div, BAD_CAST "type", BAD_CAST "text");
+	xmlSetProp (div, BAD_CAST "id", BAD_CAST "irb_input");
+	xmlSetProp (div, BAD_CAST "autocomplete", BAD_CAST "off");
+	
+	node = xmlNewChild (hdoc->head, NULL, BAD_CAST "script", BAD_CAST "");
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "text/javascript");
+	xmlSetProp (node, BAD_CAST "src", BAD_CAST "/jquery.js");
+
+	node = xmlNewChild (hdoc->head, NULL, BAD_CAST "script", BAD_CAST "");
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "text/javascript");
+	xmlSetProp (node, BAD_CAST "src", BAD_CAST "/mouseapp_2.js");
+
+	node = xmlNewChild (hdoc->head, NULL, BAD_CAST "script", BAD_CAST "");
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "text/javascript");
+	xmlSetProp (node, BAD_CAST "src", BAD_CAST "/mouseirb_2.js");
+
+	node = xmlNewChild (hdoc->head, NULL, BAD_CAST "script", BAD_CAST "");
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "text/javascript");
+	xmlSetProp (node, BAD_CAST "src", BAD_CAST "/irb.js");
+
+	node = xmlNewChild (hdoc->head, NULL, BAD_CAST "script", BAD_CAST "");
+	xmlSetProp (node, BAD_CAST "type", BAD_CAST "text/javascript");
+	xmlSetProp (node, BAD_CAST "src", BAD_CAST "/cnc.js");
+
+	node = xmlNewChild (hdoc->head, NULL, BAD_CAST "link", BAD_CAST "");
+	xmlSetProp(node, BAD_CAST "href", (xmlChar*)"/irb.css");
+	xmlSetProp(node, BAD_CAST "rel", (xmlChar*)"stylesheet");
+	xmlSetProp(node, BAD_CAST "type", (xmlChar*)"text/css");
+
 	soup_message_headers_replace (msg->response_headers,
 				      "Content-Type", "text/html");
 	xstr = html_doc_to_string (hdoc, &size);
@@ -688,80 +838,22 @@
 	xmlChar *xstr;
 	SoupBuffer *buffer;
 	gsize size;
-	gchar *str;
-
-	gchar *rfc_cnc_name;
-
-	xmlNodePtr ul, li, a;
-
-	const GSList *clist;
-
-
-	str = g_strdup_printf (_("Database information for '%s'"), cs->name);
-	hdoc = html_doc_new (str);
-	g_free (str);
-
-	/* other connections in the sidebar */
-	ul = xmlNewChild (hdoc->sidebar, NULL, "ul", _("Connections"));
-	li = xmlNewChild (ul, NULL, "li", NULL);
-	str = g_strdup_printf ("(%s)",  _("From console"));
-	a = xmlNewChild (li, NULL, "a", str);
-	g_free (str);
-	xmlSetProp (a, "href", (xmlChar*) "/");
 
-	for (clist = gda_sql_get_all_connections (); clist; clist = clist->next) {
-		gchar *tmp;
-		ConnectionSetting *cs = (ConnectionSetting*) clist->data;
-			
-		li = xmlNewChild (ul, NULL, "li", NULL);
-		a = xmlNewChild (li, NULL, "a", cs->name);
-		tmp = gda_rfc1738_encode (cs->name);
-		str = g_strdup_printf ("/%s", tmp);
-		g_free (tmp);
-		xmlSetProp (a, "href", (xmlChar*) str);
-		g_free (str);
-	}
-
-	/* list all database object's types for which information can be obtained */
-	rfc_cnc_name = gda_rfc1738_encode (cs->name);
-	ul = xmlNewChild (hdoc->sidebar, NULL, "ul", _("Objects"));
-	li = xmlNewChild (ul, NULL, "li", NULL);
-	a = xmlNewChild (li, NULL, "a", _("Tables"));
-	str = g_strdup_printf ("/%s/___tables", rfc_cnc_name);
-	xmlSetProp (a, "href", (xmlChar*) str);
-	g_free (str);
-	li = xmlNewChild (ul, NULL, "li", NULL);
-	a = xmlNewChild (li, NULL, "a", _("Views"));
-	str = g_strdup_printf ("/%s/___views", rfc_cnc_name);
-	xmlSetProp (a, "href", (xmlChar*) str);
-	g_free (str);
-	li = xmlNewChild (ul, NULL, "li", NULL);
-	a = xmlNewChild (li, NULL, "a", _("Triggers"));
-	str = g_strdup_printf ("/%s/___triggers", rfc_cnc_name);
-	xmlSetProp (a, "href", (xmlChar*) str);
-	g_free (str);
-	g_free (rfc_cnc_name);
+	hdoc = create_new_htmldoc (webserver, cs);
 	
-#ifdef GDA_DEBUG_NO
-	if (extra) {
-		gint i;
-		for (i = 0; extra[i]; i++) 
-			g_print ("EXTRA %d: #%s#\n", i, extra[i]);
-	}
-#endif
-	if (!extra || !strcmp (extra[0], "___tables")) {
+	if (!extra || !strcmp (extra[0], "~tables")) {
 		if (! compute_all_objects_content (hdoc, cs, 
 						   _("Tables"), _("Tables in the '%s' schema"), 
 						   "_tables", "table", "table_type LIKE \"%TABLE%\"", error))
 			goto onerror;
 	}
-	else if (!strcmp (extra[0], "___views")) {
+	else if (!strcmp (extra[0], "~views")) {
 		if (! compute_all_objects_content (hdoc, cs, 
 						   _("Views"), _("Views in the '%s' schema"), 
 						   "_tables", "table", "table_type LIKE \"%VIEW%\"", error))
 			goto onerror;
 	}
-	else if (!strcmp (extra[0], "___triggers")) {
+	else if (!strcmp (extra[0], "~triggers")) {
 		if (! compute_all_triggers_content (hdoc, cs, error))
 			goto onerror;
 	}
@@ -772,7 +864,7 @@
 				goto onerror;
 		}
 		else
-			xmlNewChild (hdoc->content, NULL, "h1", "Not yet implemented");
+			xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST "Not yet implemented");
 	}
 
 	soup_message_headers_replace (msg->response_headers,
@@ -817,39 +909,39 @@
 	GdaMetaStore *store;
 
 	tmp = g_strdup_printf (_("Columns for the '%s' table:"), dbo->obj_short_name);
-	xmlNewChild (hdoc->content, NULL, "h1", tmp);
+	xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST tmp);
 	g_free (tmp);
 
-	div = xmlNewChild (hdoc->content, NULL, "div", NULL);
-	table = xmlNewChild (div, NULL, "table", NULL);
-	xmlSetProp (table, "class", (xmlChar*) "ctable");
-	tr = xmlNewChild (table, NULL, "tr", NULL);
-	td = xmlNewChild (tr, NULL, "th", _("Column"));
-	td = xmlNewChild (tr, NULL, "th", _("Type"));
-	td = xmlNewChild (tr, NULL, "th", _("Nullable"));
-	td = xmlNewChild (tr, NULL, "th", _("Default"));
-	td = xmlNewChild (tr, NULL, "th", _("Extra"));
+	div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
+	table = xmlNewChild (div, NULL, BAD_CAST "table", NULL);
+	xmlSetProp (table, BAD_CAST "class", (xmlChar*) BAD_CAST "ctable");
+	tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
+	td = xmlNewChild (tr, NULL, BAD_CAST "th", BAD_CAST _("Column"));
+	td = xmlNewChild (tr, NULL, BAD_CAST "th", BAD_CAST _("Type"));
+	td = xmlNewChild (tr, NULL, BAD_CAST "th", BAD_CAST _("Nullable"));
+	td = xmlNewChild (tr, NULL, BAD_CAST "th", BAD_CAST _("Default"));
+	td = xmlNewChild (tr, NULL, BAD_CAST "th", BAD_CAST _("Extra"));
 
 	for (list = mt->columns; list; list = list->next) {
 		GdaMetaTableColumn *tcol = GDA_META_TABLE_COLUMN (list->data);
 		GString *string = NULL;
 		
-		tr = xmlNewChild (table, NULL, "tr", NULL);
-		td = xmlNewChild (tr, NULL, "td", tcol->column_name);
+		tr = xmlNewChild (table, NULL, BAD_CAST "tr", NULL);
+		td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST tcol->column_name);
 		if (tcol->pkey)
-			xmlSetProp (td, "class", "pkey");
-		td = xmlNewChild (tr, NULL, "td", tcol->column_type);
-		td = xmlNewChild (tr, NULL, "td", tcol->nullok ? _("yes") : _("no"));
-		td = xmlNewChild (tr, NULL, "td", tcol->default_value);
+			xmlSetProp (td, BAD_CAST "class", BAD_CAST "pkey");
+		td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST tcol->column_type);
+		td = xmlNewChild (tr, NULL, BAD_CAST "td", tcol->nullok ? BAD_CAST _("yes") : BAD_CAST _("no"));
+		td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST tcol->default_value);
 
 		gda_meta_table_column_foreach_attribute (tcol, 
 				    (GdaAttributesManagerFunc) meta_table_column_foreach_attribute_func, &string);
 		if (string) {
-			td = xmlNewChild (tr, NULL, "td", string->str);
+			td = xmlNewChild (tr, NULL, BAD_CAST "td", BAD_CAST string->str);
 			g_string_free (string, TRUE);
 		}
 		else
-			td = xmlNewChild (tr, NULL, "td", NULL);
+			td = xmlNewChild (tr, NULL, BAD_CAST "td", NULL);
 	}
 
 	/* finished if we don't have a table */
@@ -862,15 +954,15 @@
 	if (dbo_table->pk_cols_nb > 0) {
 		gint ipk;
 		xmlNodePtr ul = NULL;
-		xmlNewChild (hdoc->content, NULL, "h1", _("Primary key:"));
-		div = xmlNewChild (hdoc->content, NULL, "div", NULL);
+		xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST _("Primary key:"));
+		div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
 		for (ipk = 0; ipk < dbo_table->pk_cols_nb; ipk++) {
 			GdaMetaTableColumn *tcol;
 			if (!ul)
-				ul = xmlNewChild (div, NULL, "ul", NULL);
+				ul = xmlNewChild (div, NULL, BAD_CAST "ul", NULL);
 
 			tcol = g_slist_nth_data (dbo_table->columns, ipk);
-			xmlNewChild (ul, NULL, "li", tcol->column_name);
+			xmlNewChild (ul, NULL, BAD_CAST "li", tcol->column_name);
 		}
 	}
 #endif
@@ -951,8 +1043,8 @@
 				gchar *file_data;
 				gsize file_data_len;
 				if (g_file_get_contents (pngname, &file_data, &file_data_len, NULL)) {
-					tmp_filename = g_strdup_printf ("___tmp/g%d", counter);
-					tmp_data_add (webserver, tmp_filename, file_data, file_data_len);
+					tmp_filename = g_strdup_printf ("~tmp/g%d", counter);
+					tmp_ressource_add (webserver, tmp_filename, file_data, file_data_len);
 				}
 			}
 			g_unlink(pngname);
@@ -967,24 +1059,24 @@
 	if (tmp_filename) {
 		xmlNodePtr obj;
 		gchar *tmp;
-		xmlNewChild (hdoc->content, NULL, "h1", _("Relations:"));
-		div = xmlNewChild (hdoc->content, NULL, "div", NULL);
+		xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST _("Relations:"));
+		div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
 
 		if (map_node)
 			xmlAddChild (div, map_node);
 
-		xmlSetProp (div, "class", (xmlChar*) "graph");
-		obj = xmlNewChild (div, NULL, "img", NULL);
+		xmlSetProp (div, BAD_CAST "class", BAD_CAST  "graph");
+		obj = xmlNewChild (div, NULL, BAD_CAST "img", NULL);
 		tmp = g_strdup_printf ("/%s", tmp_filename);
-		xmlSetProp (obj, "src", (xmlChar*) tmp);
-		xmlSetProp (obj, "usemap", (xmlChar*) "#G");
+		xmlSetProp (obj, BAD_CAST "src", BAD_CAST tmp);
+		xmlSetProp (obj, BAD_CAST "usemap", BAD_CAST  "#G");
 		g_free (tmp);
 		g_free (tmp_filename);
 	}
 	else {
 		/* list foreign keys as we don't have a graph */
 		if (dbo_table->fk_list) {
-			xmlNewChild (hdoc->content, NULL, "h1", _("Foreign keys:"));
+			xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST _("Foreign keys:"));
 			GSList *list;
 			for (list = dbo_table->fk_list; list; list = list->next) {
 				GdaMetaTableForeignKey *tfk = GDA_META_TABLE_FOREIGN_KEY (list->data);
@@ -992,18 +1084,18 @@
 				xmlNodePtr ul = NULL;
 				gint ifk;
 				
-				div = xmlNewChild (hdoc->content, NULL, "div", NULL);
+				div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
 				for (ifk = 0; ifk < tfk->cols_nb; ifk++) {
 					gchar *tmp;
 					if (!ul) {
 						tmp = g_strdup_printf (_("To '%s':"), fkdbo->obj_short_name);
-						ul = xmlNewChild (div, NULL, "ul", tmp);
+						ul = xmlNewChild (div, NULL, BAD_CAST "ul", BAD_CAST tmp);
 						g_free (tmp);
 					}
 					tmp = g_strdup_printf ("%s --> %s.%s", 
 							       tfk->fk_names_array[ifk], 
 							       fkdbo->obj_short_name, tfk->ref_pk_names_array[ifk]);
-					xmlNewChild (ul, NULL, "li", tmp);
+					xmlNewChild (ul, NULL, BAD_CAST "li", BAD_CAST tmp);
 					g_free (tmp);
 				}
 			}
@@ -1170,10 +1262,10 @@
 	GdaMetaView *view = GDA_META_VIEW (dbo);
 	if (view->view_def) {
 		xmlNodePtr div, code;
-		xmlNewChild (hdoc->content, NULL, "h1", _("View definition:"));
-		div = xmlNewChild (hdoc->content, NULL, "div", NULL);
-		code = xmlNewChild (div, NULL, "code", view->view_def);
-		xmlSetProp (code, "class", (xmlChar*) "ccode");
+		xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST _("View definition:"));
+		div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
+		code = xmlNewChild (div, NULL, BAD_CAST "code", BAD_CAST view->view_def);
+		xmlSetProp (code, BAD_CAST "class", BAD_CAST "ccode");
 	}
 	return TRUE;
 }
@@ -1237,7 +1329,7 @@
 		if ((!tschema || gda_value_differ (tschema, cv3)) ||
 		    (!tname || gda_value_differ (tname, cv4))) {
 			if (tschema) 
-				xmlNewChild (div, NULL, "br", NULL);
+				xmlNewChild (div, NULL, BAD_CAST "br", NULL);
 			if (tschema) 
 				gda_value_free (tschema);
 			if (tname)
@@ -1249,33 +1341,33 @@
 					       g_value_get_string (cv0),
 					       g_value_get_string (tschema),
 					       g_value_get_string (tname));
-			xmlNewChild (hdoc->content, NULL, "h1", tmp);
+			xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST tmp);
 			g_free (tmp);
 
-			div = xmlNewChild (hdoc->content, NULL, "div", NULL);
+			div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
 		}
 
 		if (G_VALUE_TYPE (cv5) != GDA_TYPE_NULL)
 			tmp = g_strdup_printf ("On %s (%s):", g_value_get_string (cv2), g_value_get_string (cv5));
 		else
 			tmp = g_strdup_printf ("On %s:", g_value_get_string (cv2));
-		xmlNewChild (div, NULL, "h2", tmp);
+		xmlNewChild (div, NULL, BAD_CAST "h2", BAD_CAST tmp);
 		g_free (tmp);
 
-		sdiv = xmlNewChild (div, NULL, "div", NULL);
-		ul = xmlNewChild (sdiv, NULL, "ul", NULL);
+		sdiv = xmlNewChild (div, NULL, BAD_CAST "div", NULL);
+		ul = xmlNewChild (sdiv, NULL, BAD_CAST "ul", NULL);
 
 		tmp = g_strdup_printf (_("Trigger fired for: %s"), g_value_get_string (cv6));
-		li = xmlNewChild (ul, NULL, "li", tmp);
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", BAD_CAST tmp);
 		g_free (tmp);
 
 		tmp = g_strdup_printf (_("Time at which the trigger is fired: %s"), g_value_get_string (cv7));
-		li = xmlNewChild (ul, NULL, "li", tmp);
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", BAD_CAST tmp);
 		g_free (tmp);
 
-		li = xmlNewChild (ul, NULL, "li", _("Action:"));
-		code = xmlNewChild (li, NULL, "code", g_value_get_string (cv1));
-		xmlSetProp (code, "class", (xmlChar*) "ccode");
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", BAD_CAST _("Action:"));
+		code = xmlNewChild (li, NULL, BAD_CAST "code", BAD_CAST g_value_get_string (cv1));
+		xmlSetProp (code, BAD_CAST "class", BAD_CAST "ccode");
 	}
 	
 	
@@ -1379,10 +1471,10 @@
 		goto out;
 	nrows = gda_data_model_get_n_rows (model);
 	if (nrows > 0) {
-		xmlNewChild (hdoc->content, NULL, "h1", human_obj_type);
-		div = xmlNewChild (hdoc->content, NULL, "div", NULL);
-		xmlSetProp (div, "class", "clist");
-		ul = xmlNewChild (div, NULL, "ul", NULL);
+		xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST human_obj_type);
+		div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
+		xmlSetProp (div, BAD_CAST "class", BAD_CAST "clist");
+		ul = xmlNewChild (div, NULL, BAD_CAST "ul", NULL);
 		content_added = TRUE;
 	}
 	for (i = 0; i < nrows; i++) {
@@ -1394,21 +1486,21 @@
 		cv1 = gda_data_model_get_value_at (model, 1, i, error);
 		if (!cv1)
 			goto out;
-		li = xmlNewChild (ul, NULL, "li", NULL);
-		a = xmlNewChild (li, NULL, "a", g_value_get_string (cv1));
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST g_value_get_string (cv1));
 		e0 = gda_rfc1738_encode (g_value_get_string (cv0));
 		e1 = gda_rfc1738_encode (g_value_get_string (cv1));
 		tmp = g_strdup_printf ("/%s/%s/%s", rfc_cnc_name, e0, e1);
 		g_free (e0);
 		g_free (e1);
-		xmlSetProp (a, "href", tmp);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST tmp);
 		g_free (tmp);
 		tmp = g_strdup_printf ("%s.%s", g_value_get_string (cv0), g_value_get_string (cv1));
-		xmlSetProp (a, "title", tmp);
+		xmlSetProp (a, BAD_CAST "title", BAD_CAST tmp);
 		g_free (tmp);
 	}
 	if (nrows > 0)
-		xmlNewChild (div, NULL, "br", NULL);
+		xmlNewChild (div, NULL, BAD_CAST "br", NULL);
 	g_object_unref (model);
 
 	/* objects listed by schema */
@@ -1440,35 +1532,35 @@
 			xmlNodePtr header;
 			gchar *tmp;
 			if (schema) {
-				xmlNewChild (div, NULL, "br", NULL);
+				xmlNewChild (div, NULL, BAD_CAST "br", NULL);
 				gda_value_free (schema);
 			}
 			schema = gda_value_copy (cv0);
 			tmp = g_strdup_printf (human_obj_type_in_schema, g_value_get_string (schema));
-			header = xmlNewChild (hdoc->content, NULL, "h1", tmp);
+			header = xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST tmp);
 			g_free (tmp);
 			content_added = TRUE;
-			div = xmlNewChild (hdoc->content, NULL, "div", NULL);
-			xmlSetProp (div, "class", "clist");
-			ul = xmlNewChild (div, NULL, "ul", NULL);
+			div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
+			xmlSetProp (div, BAD_CAST "class", BAD_CAST "clist");
+			ul = xmlNewChild (div, NULL, BAD_CAST "ul", NULL);
 		}
 
-		li = xmlNewChild (ul, NULL, "li", NULL);
-		a = xmlNewChild (li, NULL, "a", g_value_get_string (cv1));
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST g_value_get_string (cv1));
 		e0 = gda_rfc1738_encode (g_value_get_string (cv0));
 		e1 = gda_rfc1738_encode (g_value_get_string (cv1));
 		tmp = g_strdup_printf ("/%s/%s/%s", rfc_cnc_name, e0, e1);
 		g_free (e0);
 		g_free (e1);
-		xmlSetProp (a, "href", tmp);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST tmp);
 		g_free (tmp);
 	}
 	if (nrows != 0)
-		xmlNewChild (div, NULL, "br", NULL);
+		xmlNewChild (div, NULL, BAD_CAST "br", NULL);
 	retval = TRUE;
 
 	if (! content_added) 
-		xmlNewChild (hdoc->content, NULL, "br", NULL);
+		xmlNewChild (hdoc->content, NULL, BAD_CAST "br", NULL);
 
  out:
 	if (schema)
@@ -1492,7 +1584,7 @@
 	GdaDataModel *model;
 	gint i, nrows;
 	GValue *schema = NULL, *tschema = NULL, *tname = NULL;
-	xmlNodePtr ul, sul, li, a, div = NULL, sdiv = NULL;
+	xmlNodePtr sul, li, a, div = NULL, sdiv = NULL;
 	gboolean content_added = FALSE;
 
 	rfc_cnc_name = gda_rfc1738_encode (cs->name);
@@ -1509,9 +1601,9 @@
 		goto out;
 	nrows = gda_data_model_get_n_rows (model);
 	if (nrows > 0) {
-		xmlNewChild (hdoc->content, NULL, "h1", _("Triggers:"));
-		div = xmlNewChild (hdoc->content, NULL, "div", NULL);
-		xmlSetProp (div, "class", "clist");
+		xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST _("Triggers:"));
+		div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
+		xmlSetProp (div, BAD_CAST "class", BAD_CAST "clist");
 		content_added = TRUE;
 	}
 	for (i = 0; i < nrows; i++) {
@@ -1533,7 +1625,7 @@
 		    (!tname || gda_value_differ (tname, cv4))) {
 			gchar *tmp;
 			if (tschema) 
-				xmlNewChild (sdiv, NULL, "br", NULL);
+				xmlNewChild (sdiv, NULL, BAD_CAST "br", NULL);
 			if (tschema) 
 				gda_value_free (tschema);
 			if (tname)
@@ -1543,17 +1635,17 @@
 
 			tmp = g_strdup_printf (_("For the '%s.%s' table:"), g_value_get_string (tschema),
 					       g_value_get_string (tname));
-			xmlNewChild (div, NULL, "h2", tmp);
+			xmlNewChild (div, NULL, BAD_CAST "h2", BAD_CAST tmp);
 			g_free (tmp);
 
-			sdiv = xmlNewChild (div, NULL, "div", NULL);
-			xmlSetProp (sdiv, "class", "clist");
-			sul = xmlNewChild (sdiv, NULL, "ul", NULL);
+			sdiv = xmlNewChild (div, NULL, BAD_CAST "div", NULL);
+			xmlSetProp (sdiv, BAD_CAST "class", BAD_CAST "clist");
+			sul = xmlNewChild (sdiv, NULL, BAD_CAST "ul", NULL);
 		}
 
-		li = xmlNewChild (sul, NULL, "li", NULL);
+		li = xmlNewChild (sul, NULL, BAD_CAST "li", NULL);
 		tmp = g_strdup_printf ("%s (%s)", g_value_get_string (cv1), g_value_get_string (cv2));
-		a = xmlNewChild (li, NULL, "a", tmp);
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST tmp);
 		g_free (tmp);
 
 		e0 = gda_rfc1738_encode (g_value_get_string (cv0));
@@ -1561,11 +1653,11 @@
 		tmp = g_strdup_printf ("/%s/%s/%s", rfc_cnc_name, e0, e1);
 		g_free (e0);
 		g_free (e1);
-		xmlSetProp (a, "href", tmp);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST tmp);
 		g_free (tmp);
 	}
 	if (nrows > 0)
-		xmlNewChild (sdiv, NULL, "br", NULL);
+		xmlNewChild (sdiv, NULL, BAD_CAST "br", NULL);
 	g_object_unref (model);
 	if (tschema) {
 		gda_value_free (tschema);
@@ -1603,16 +1695,16 @@
 		if (!schema || gda_value_differ (schema, cv0)) {
 			gchar *tmp;
 			if (schema) {
-				xmlNewChild (sdiv, NULL, "br", NULL);
+				xmlNewChild (sdiv, NULL, BAD_CAST "br", NULL);
 				gda_value_free (schema);
 			}
 			schema = gda_value_copy (cv0);
 			tmp = g_strdup_printf (_("Triggers in the '%s' schema:"), g_value_get_string (schema));
-			xmlNewChild (hdoc->content, NULL, "h1", tmp);
+			xmlNewChild (hdoc->content, NULL, BAD_CAST "h1", BAD_CAST tmp);
 			g_free (tmp);
 			content_added = TRUE;
-			div = xmlNewChild (hdoc->content, NULL, "div", NULL);
-			xmlSetProp (div, "class", "clist");
+			div = xmlNewChild (hdoc->content, NULL, BAD_CAST "div", NULL);
+			xmlSetProp (div, BAD_CAST "class", BAD_CAST "clist");
 
 			if (tschema) {
 				gda_value_free (tschema);
@@ -1627,7 +1719,7 @@
 		    (!tname || gda_value_differ (tname, cv4))) {
 			gchar *tmp;
 			if (tschema) 
-				xmlNewChild (sdiv, NULL, "br", NULL);
+				xmlNewChild (sdiv, NULL, BAD_CAST "br", NULL);
 			if (tschema) 
 				gda_value_free (tschema);
 			if (tname)
@@ -1637,17 +1729,17 @@
 
 			tmp = g_strdup_printf (_("For the '%s.%s' table:"), g_value_get_string (tschema),
 					       g_value_get_string (tname));
-			xmlNewChild (div, NULL, "h2", tmp);
+			xmlNewChild (div, NULL, BAD_CAST "h2", BAD_CAST tmp);
 			g_free (tmp);
 
-			sdiv = xmlNewChild (div, NULL, "div", NULL);
-			xmlSetProp (sdiv, "class", "clist");
-			sul = xmlNewChild (sdiv, NULL, "ul", NULL);
+			sdiv = xmlNewChild (div, NULL, BAD_CAST "div", NULL);
+			xmlSetProp (sdiv, BAD_CAST "class", BAD_CAST "clist");
+			sul = xmlNewChild (sdiv, NULL, BAD_CAST "ul", NULL);
 		}
 
-		li = xmlNewChild (sul, NULL, "li", NULL);
+		li = xmlNewChild (sul, NULL, BAD_CAST "li", NULL);
 		tmp = g_strdup_printf ("%s (%s)", g_value_get_string (cv1), g_value_get_string (cv2));
-		a = xmlNewChild (li, NULL, "a", tmp);
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST tmp);
 		g_free (tmp);
 
 		e0 = gda_rfc1738_encode (g_value_get_string (cv0));
@@ -1655,15 +1747,15 @@
 		tmp = g_strdup_printf ("/%s/%s/%s", rfc_cnc_name, e0, e1);
 		g_free (e0);
 		g_free (e1);
-		xmlSetProp (a, "href", tmp);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST tmp);
 		g_free (tmp);
 	}
 	if (nrows != 0)
-		xmlNewChild (sdiv, NULL, "br", NULL);
+		xmlNewChild (sdiv, NULL, BAD_CAST "br", NULL);
 	retval = TRUE;
 
 	if (! content_added) 
-		xmlNewChild (hdoc->content, NULL, "br", NULL);
+		xmlNewChild (hdoc->content, NULL, BAD_CAST "br", NULL);
 
  out:
 	if (schema)
@@ -1679,3 +1771,496 @@
 
 	return retval;
 }
+
+/*
+ * Get variables
+ *
+ * ...: a NULL terminated list of:
+ *      - variable name: const gchar*  
+ *      - place holder for tha variable's contents: gchar **
+ */
+static void
+get_variables (SoupMessage *msg, GHashTable *query, ...)
+{
+	va_list ap;
+	gchar *name;
+	GHashTable *rquery;
+
+	if (query)
+		rquery = query;
+	else if (msg->request_body->length)
+		rquery = soup_form_decode (msg->request_body->data);
+	else
+		return;
+
+	va_start (ap, query);
+	for (name = va_arg (ap, gchar*); name; name = va_arg (ap, gchar*)) {
+		gchar **stringadr = va_arg (ap, gchar**);
+		const gchar *tmp;
+		tmp = g_hash_table_lookup (rquery, name);
+		if (tmp)
+			*stringadr = g_strdup (tmp);
+		else
+			*stringadr = NULL;
+	}
+	va_end (ap);
+
+	if (!query)
+		g_hash_table_destroy (rquery);
+}
+
+/*
+ * Get variables
+ *
+ * ...: a NULL terminated list of:
+ *      - variable name: const gchar*  
+ *      - place holder for tha variable's contents: gchar **
+ */
+static void
+get_cookies (SoupMessage *msg, ...)
+{
+	const gchar *cookies;
+	GdaQuarkList *ql;
+	va_list ap;
+	gchar *name;
+
+	cookies = soup_message_headers_get (msg->request_headers, "Cookie");
+	ql = gda_quark_list_new_from_string (cookies);
+
+	va_start (ap, msg);
+	for (name = va_arg (ap, gchar*); name; name = va_arg (ap, gchar*)) {
+		gchar **stringadr = va_arg (ap, gchar**);
+		const gchar *tmp;
+		tmp = gda_quark_list_find (ql, name);
+		if (tmp)
+			*stringadr = g_strdup (tmp);
+		else
+			*stringadr = NULL;
+	}
+	va_end (ap);
+	
+	gda_quark_list_free (ql);
+}
+
+static HtmlDoc*
+create_new_htmldoc (WebServer *webserver, const ConnectionSetting *cs)
+{
+	HtmlDoc *hdoc;
+	gchar *str;
+
+	gchar *rfc_cnc_name;
+
+	xmlNodePtr ul, li, a;
+
+	if (cs) {
+		str = g_strdup_printf (_("Database information for '%s'"), cs->name);
+		hdoc = html_doc_new (str);
+		g_free (str);
+	}
+	else
+		hdoc = html_doc_new (_("Database information"));
+
+	/* other connections in the sidebar */
+	ul = cnc_ul ();
+	xmlAddChild (hdoc->sidebar, ul);
+
+	/* list all database object's types for which information can be obtained */
+	if (cs) {
+		rfc_cnc_name = gda_rfc1738_encode (cs->name);
+		ul = xmlNewChild (hdoc->sidebar, NULL, BAD_CAST "ul", BAD_CAST _("Objects"));
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST _("Tables"));
+		str = g_strdup_printf ("/%s/~tables", rfc_cnc_name);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST str);
+		g_free (str);
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST _("Views"));
+		str = g_strdup_printf ("/%s/~views", rfc_cnc_name);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST str);
+		g_free (str);
+		li = xmlNewChild (ul, NULL, BAD_CAST "li", NULL);
+		a = xmlNewChild (li, NULL, BAD_CAST "a", BAD_CAST _("Triggers"));
+		str = g_strdup_printf ("/%s/~triggers", rfc_cnc_name);
+		xmlSetProp (a, BAD_CAST "href", BAD_CAST str);
+		g_free (str);
+		g_free (rfc_cnc_name);
+	}
+	return hdoc;
+}
+
+
+/*
+ *
+ * IRB
+ *
+ */
+
+static gboolean
+delete_consoles (WebServer *server)
+{
+	GSList *list;
+	GTimeVal tv;
+
+	g_get_current_time (&tv);
+	for (list = server->priv->terminals_list; list; ) {
+		SqlConsole *con = (SqlConsole *) list->data;
+		if (con->last_time_used.tv_sec + 600 > tv.tv_sec) {
+			GSList *n = list->next;
+			server->priv->terminals_list = g_slist_delete_link (server->priv->terminals_list, list);
+			list = n;
+			gda_sql_console_free (con);
+		}
+		else
+			list = list->next;
+	}
+	if (! server->priv->terminals_list) {
+		server->priv->term_timer = 0;
+		return FALSE;
+	}
+	else
+		return TRUE;
+}
+
+
+/*
+ * GET/POST  method for IRB
+ */
+static gboolean
+get_post_for_irb (WebServer *webserver, SoupMessage *msg, const ConnectionSetting *cs,
+		  GHashTable *query, GError **error)
+{
+	gboolean retval = FALSE;
+	SoupBuffer *buffer;
+	xmlChar *contents = NULL;
+	static gint counter = 0;
+	
+	gchar *cmd;
+	gchar *cid;
+
+	SqlConsole *console = NULL;
+
+	/* fetch variables */
+	get_variables (msg, query, "cmd", &cmd, "cid", &cid, NULL);
+	if (!cmd)
+		return FALSE;
+
+	if (cid) {
+		GSList *list;
+		for (list = webserver->priv->terminals_list; list; list = list->next) {
+			if (((SqlConsole*) list->data)->id && !strcmp (((SqlConsole*) list->data)->id, cid)) {
+				console = (SqlConsole*) list->data;
+				break;
+			}
+		}
+	}
+
+	if (!console) {
+		if (!cid || !strcmp (cid, "none")) {
+			gchar *str;
+			str = g_strdup_printf ("console%d", counter++);
+			console = gda_sql_console_new (str);
+			g_free (str);
+		}
+		else {
+			console = gda_sql_console_new (cid);
+		}
+			
+		webserver->priv->terminals_list = g_slist_prepend (webserver->priv->terminals_list,
+								   console);
+		g_get_current_time (&(console->last_time_used));
+		if (!webserver->priv->term_timer)
+			webserver->priv->term_timer = g_timeout_add_seconds (5, (GSourceFunc) delete_consoles,
+									     webserver);
+
+		if (!cid || !strcmp (cid, "none")) {
+			soup_message_headers_replace (msg->response_headers,
+						      "Content-Type", "text/xml");
+			soup_message_set_status (msg, SOUP_STATUS_OK);
+			g_free (cmd);
+			
+			/* send console's ID */
+			xmlDocPtr doc;
+			xmlNodePtr topnode;
+			gchar *tmp;
+			
+			doc = xmlNewDoc (BAD_CAST "1.0");
+			topnode = xmlNewDocNode (doc, NULL, BAD_CAST "result", NULL);
+			xmlDocSetRootElement (doc, topnode);
+			
+			xmlNewChild (topnode, NULL, BAD_CAST "cid", BAD_CAST (console->id));
+			tmp = gda_sql_console_compute_prompt (console);
+			xmlNewChild (topnode, NULL, BAD_CAST "prompt", BAD_CAST tmp);
+			g_free (tmp);
+			
+			int size;
+			xmlDocDumpFormatMemory (doc, &contents, &size, 1);
+			xmlFreeDoc (doc);
+			goto resp;
+		}
+	}
+
+	/* create response */
+	g_get_current_time (&(console->last_time_used));
+	cmd = g_strstrip (cmd);
+	if (*cmd) {
+		xmlDocPtr doc;
+		GError *lerror = NULL;
+		xmlNodePtr topnode;
+		gchar *tmp;
+
+		doc = xmlNewDoc (BAD_CAST "1.0");
+		topnode = xmlNewDocNode (doc, NULL, BAD_CAST "result", NULL);
+		xmlDocSetRootElement (doc, topnode);
+
+		tmp = gda_sql_console_execute (console, cmd, &lerror);
+		if (!tmp) 
+			tmp = g_strdup_printf (_("Error: %s"), 
+						    lerror && lerror->message ? lerror->message : _("No detail"));
+		if (lerror)
+			g_error_free (lerror);
+
+		xmlNewChild (topnode, NULL, BAD_CAST "cmde", BAD_CAST tmp);
+		g_free (tmp);
+
+		tmp = gda_sql_console_compute_prompt (console);
+		xmlNewChild (topnode, NULL, BAD_CAST "prompt", BAD_CAST tmp);
+		g_free (tmp);
+
+		int size;
+		xmlDocDumpFormatMemory (doc, &contents, &size, 1);
+		xmlFreeDoc (doc);
+	}
+	g_free (cmd);
+
+	/* send response */
+ resp:
+	soup_message_headers_replace (msg->response_headers,
+				      "Content-Type", "text/xml");
+	if (contents) {
+		buffer = soup_buffer_new_with_owner (contents, strlen ((gchar*)contents), contents, 
+						     (GDestroyNotify) xmlFree);
+		soup_message_body_append_buffer (msg->response_body, buffer);
+		soup_buffer_free (buffer);
+	}
+	soup_message_set_status (msg, SOUP_STATUS_OK);
+	retval = TRUE;
+
+	return retval;
+}
+
+
+
+/*
+ *
+ * Temporary data management
+ *
+ */
+
+/*
+ * @data is stolen!
+ */
+static TmpRessource *
+tmp_ressource_add (WebServer *server, const gchar *path, gchar *data, gsize data_length)
+{
+	TmpRessource *td;
+	GTimeVal tv;
+
+	g_get_current_time (&tv);
+	td = g_new0 (TmpRessource, 1);
+	td->path = g_strdup (path);
+	td->data = data;
+	td->size = data_length;
+	td->expiration_date = tv.tv_sec + 30;
+	g_hash_table_insert (server->priv->ressources_hash, g_strdup (path), td);
+	server->priv->ressources_list = g_slist_prepend (server->priv->ressources_list, td);
+	if (!server->priv->ressources_timer)
+		server->priv->ressources_timer = g_timeout_add_seconds (5, (GSourceFunc) delete_tmp_ressource, server);
+	return td;
+}
+
+/*
+ * @data is static
+ */
+static TmpRessource *
+tmp_static_data_add (WebServer *server, const gchar *path, gchar *data, gsize data_length)
+{
+	TmpRessource *td;
+
+	td = g_new0 (TmpRessource, 1);
+	td->path = g_strdup (path);
+	td->data = data;
+	td->size = data_length;
+	td->expiration_date = 0;
+	g_hash_table_insert (server->priv->ressources_hash, g_strdup (path), td);
+	server->priv->ressources_list = g_slist_prepend (server->priv->ressources_list, td);
+	return td;
+}
+
+static gboolean
+delete_tmp_ressource (WebServer *server)
+{
+	GSList *list;
+	GTimeVal tv;
+	gint n_timed = 0;
+
+	g_get_current_time (&tv);
+	for (list = server->priv->ressources_list; list; ) {
+		TmpRessource *td = (TmpRessource *) list->data;
+		if ((td->expiration_date > 0) && (td->expiration_date < tv.tv_sec)) {
+			GSList *n = list->next;
+			g_hash_table_remove (server->priv->ressources_hash, td->path);
+			server->priv->ressources_list = g_slist_delete_link (server->priv->ressources_list, list);
+			list = n;
+		}
+		else {
+			if (td->expiration_date > 0)
+				n_timed ++;
+			list = list->next;
+		}
+	}
+	if (n_timed == 0) {
+		server->priv->ressources_timer = 0;
+		return FALSE;
+	}
+	else
+		return TRUE;
+}
+
+static void
+tmp_ressource_free (TmpRessource *data)
+{
+	g_free (data->data);
+	g_free (data);
+}
+
+/*
+ *
+ * Misc functions
+ *
+ */
+static TimedString *
+timed_string_new (guint duration)
+{
+#define STRING_SIZE 16
+
+	TimedString *ts;
+	gint i;
+	GString *string;
+
+	ts = g_new0 (TimedString, 1);
+	string = g_string_new ("");
+	for (i = 0; i < STRING_SIZE; i++) {
+		gushort r = (((gfloat) rand ()) / (gfloat)RAND_MAX) * 255;
+		g_string_append_printf (string, "%0x", r);
+	}
+	ts->string = string->str;
+	
+	g_string_free (string, FALSE);
+	g_get_current_time (& (ts->validity));
+	ts->validity.tv_sec += duration;
+	return ts;
+}
+
+static void
+timed_string_free (TimedString *ts)
+{
+	g_free (ts->string);
+	g_free (ts);
+}
+
+static gboolean
+auth_timer_manage (WebServer *server)
+{
+	challenges_manage (server);
+	auth_cookies_manage (server);
+	
+	if ((server->priv->challenges->len == 0) &&
+	    (server->priv->cookies->len == 0)) {
+		/* remove timer */
+		server->priv->auth_timer = 0;
+		return FALSE;
+	}
+	else
+		return TRUE;
+}
+
+static TimedString *
+challenge_add (WebServer *server)
+{
+	TimedString *ts = timed_string_new (180);
+	g_array_append_val (server->priv->challenges, ts);
+	if (!server->priv->auth_timer)
+		server->priv->auth_timer = g_timeout_add_seconds (5, (GSourceFunc) auth_timer_manage, server);
+	else
+		challenges_manage (server);
+	return ts;
+}
+
+static void
+challenges_manage (WebServer *server)
+{
+	gint i;
+	GTimeVal current_ts;
+	
+	if (server->priv->challenges->len > MAX_CHALLENGES) {
+		/* remove the oldest challenge */
+		TimedString *ts = g_array_index (server->priv->challenges, TimedString *, 0);
+		timed_string_free (ts);
+		g_array_remove_index (server->priv->challenges, 0);
+	}
+
+	g_get_current_time (&current_ts);
+	for (i = 0; i < server->priv->challenges->len; ) {
+		TimedString *ts = g_array_index (server->priv->challenges, TimedString *, 0);
+		if (ts->validity.tv_sec < current_ts.tv_sec) {
+			timed_string_free (ts);
+			g_array_remove_index (server->priv->challenges, i);
+		}
+		else
+			i++;
+	}
+}
+
+static TimedString *
+auth_cookie_add (WebServer *server)
+{
+	TimedString *ts = timed_string_new (1800); /* half hour availability */
+	g_array_append_val (server->priv->cookies, ts);
+	if (!server->priv->auth_timer)
+		server->priv->auth_timer = g_timeout_add_seconds (5, (GSourceFunc) auth_timer_manage,
+								  server);
+	else
+		auth_cookies_manage (server);
+#ifdef DEBUG_SERVER
+	g_print ("Added cookie %s\n", ts->string);
+#endif
+	return ts;
+}
+
+static void
+auth_cookies_manage (WebServer *server)
+{
+	gint i;
+	GTimeVal current_ts;
+	
+	if (server->priv->cookies->len > MAX_AUTH_COOKIES) {
+		/* remove the oldest auth_cookie */
+		TimedString *ts = g_array_index (server->priv->cookies, TimedString *, 0);
+		timed_string_free (ts);
+		g_array_remove_index (server->priv->cookies, 0);
+	}
+
+	g_get_current_time (&current_ts);
+	for (i = 0; i < server->priv->cookies->len; ) {
+		TimedString *ts = g_array_index (server->priv->cookies, TimedString *, 0);
+		if (ts->validity.tv_sec < current_ts.tv_sec) {
+#ifdef DEBUG_SERVER
+			g_print ("Removed cookie %s\n", ts->string);
+#endif
+			timed_string_free (ts);
+			g_array_remove_index (server->priv->cookies, i);
+		}
+		else
+			i++;
+	}
+}

Modified: trunk/tools/web-server.h
==============================================================================
--- trunk/tools/web-server.h	(original)
+++ trunk/tools/web-server.h	Fri Dec 26 14:37:32 2008
@@ -59,7 +59,7 @@
 };
 
 GType               web_server_get_type                (void) G_GNUC_CONST;
-WebServer          *web_server_new                     (gint port);
+WebServer          *web_server_new                     (gint port, const gchar *auth_token);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]