[libgda/LIBGDA_4.0] Corrections in the Gda-Sql tool regarding username and password



commit 713b9523252ec5ba404d4a55fd30cb6a9d771806
Author: Vivien Malerba <malerba gnome-db org>
Date:   Mon Jun 7 18:37:32 2010 +0200

    Corrections in the Gda-Sql tool regarding username and password
    
    - don't display password's characters while they are entered
    - automatically detect when username and password are required
    - removed the "-p" comand line option
    - improved man page

 doc/C/gda-sql-manual.xml |    6 +-
 tools/gda-sql.1.in       |   26 ++++----
 tools/gda-sql.c          |  173 +++++++++++++++++++++++++++++++++-------------
 3 files changed, 141 insertions(+), 64 deletions(-)
---
diff --git a/doc/C/gda-sql-manual.xml b/doc/C/gda-sql-manual.xml
index 98579fc..a75df25 100644
--- a/doc/C/gda-sql-manual.xml
+++ b/doc/C/gda-sql-manual.xml
@@ -54,7 +54,6 @@
 	<arg>-C <replaceable>command</replaceable></arg>
 	<arg>-f <replaceable>commands file</replaceable></arg>
 	<arg>-o <replaceable>output file</replaceable></arg>
-	<arg>-p</arg>
 	<arg>-s<replaceable>port to use to run embedded HTTP server</replaceable></arg>
 	<arg>-t<replaceable>authentication token required to authenticate clients when running the embedded HTTP server</replaceable></arg>
 	<arg rep="repeat"><replaceable>connection specification</replaceable></arg>
@@ -96,9 +95,6 @@ MSAccess    | Provider for Microsoft Access files | DB_NAME,           |
 	    before the tool exits</para></listitem>
 	<listitem><para>the <option>-o</option> option allows to specify a file to write the 
 	    output to</para></listitem>
-	<listitem><para>the <option>-p</option> speficies that the tool should not ask for
-	    passwords when a username has been specified for a connection but no password has
-	    been specified.</para></listitem>
 	<listitem><para>the <option>-s</option> requests the embedded HTTP server to be executed, listening on
 	    the port specified.</para></listitem>
 	<listitem><para>the <option>-t</option> specifies a token string which clients will have to
@@ -111,6 +107,8 @@ MSAccess    | Provider for Microsoft Access files | DB_NAME,           |
 	<listitem><para>defined data sources (the ones listed using the <option>-l</option> option)</para></listitem>
 	<listitem><para>connection strings which have the following format: 
 	    "[&lt;provider&gt;://][&lt;username&gt;[:&lt;password&gt;] ]&lt;connection_params&gt;". 
+	    If a username or password is required, and is not specified either by a DSN's definition or in
+	    the connection string, then it will be requested dynamically.
 	    Note that if provided, &lt;username&gt;, &lt;password&gt; and &lt;provider&gt; must be encoded as per RFC 1738</para></listitem>
 	<listitem><para>for SQLite and MS Access files: the file name</para></listitem>
       </itemizedlist>
diff --git a/tools/gda-sql.1.in b/tools/gda-sql.1.in
index 3b59dba..737661a 100644
--- a/tools/gda-sql.1.in
+++ b/tools/gda-sql.1.in
@@ -1,4 +1,4 @@
-.TH gda-sql 1 "January 09 2009" "Version @PACKAGE_VERSION@" "LIBGDA Manual Pages"
+.TH gda-sql 1 "June 03 2010" "Version @PACKAGE_VERSION@" "LIBGDA Manual Pages"
 
 .SH NAME
 gda-sql - an SQL console based on Libgda
@@ -6,7 +6,6 @@ gda-sql - an SQL console based on Libgda
 .SH SYNOPSIS
 .B gda-sql
 [\-\-help] [\-v] [\-\-version]
-[\-p] [\-\-no\-password\-ask]
 [\-o] [\-\-output\-file \fI<filename>\fP]
 [\-C] [\-\-command]
 [\-f] [\-\-commands\-file \fI<filename>\fP]
@@ -14,7 +13,8 @@ gda-sql - an SQL console based on Libgda
 [\-l] [\-\-list\-dsn]
 [\-L] [\-\-list\-providers]
 [\-s] [\-\-http\-port \fI<port>\fP]
-[\-t] [\-\-http\-token \fI<token phrase>\fP] ...
+[\-t] [\-\-http\-token \fI<token phrase>\fP]
+[connection's spec] [connection's spec...]
 
 .SH DESCRIPTION
 .PP
@@ -25,8 +25,9 @@ query results.
 .PP
 Several connections can be opened at the same time, allowing you to switch the active connection to any
 opened connection. When starting, gda-sql opens a connection for each connection specified on the command
-line (plus optionnally one corresponding to the \fBGDA_SQL_CNC\fP environment variable). The prompt
-indicates the current connection used when executing commands.
+line (plus optionally one corresponding to the \fBGDA_SQL_CNC\fP environment variable). The prompt
+indicates the current connection used when executing commands. See the \fB.c\fP internal command for
+an explanation about the syntax to specify a connection on the command line.
 
 .PP
 Alternatively, input can be from a file.
@@ -42,9 +43,6 @@ gda-sql accepts the following options:
 .B  \-\-help
 Show command\-line options.
 .TP 8
-.B \-p, \-\-no\-password\-ask
-Don't ask for a password when it is empty.
-.TP 8
 .B \-o, \-\-output\-file \fI<filename>\fP
 Specifies a file to which outputs are redirected.
 .TP 8
@@ -106,11 +104,11 @@ relative to the location of the gda-sql executable file.
 
 .SH FILES
 gda-sql stores data source definitions (DSN) in Libgda defined files 
-(\fB$HOME\fP/.libgda/config and @gdasysconfdir@/libgda- GDA_ABI_VERSION@/config
+($HOME/.local/share/libgda and @gdasysconfdir@/libgda- GDA_ABI_VERSION@/config
 where ${prefix} is typically /usr).
 
 For each connection defined by a DSN, the meta data is stored in a
-\fB$HOME\fP/.libgda/gda-sql-<DSN>.db file.
+$HOME/.local/share/libgda/gda-sql-<DSN>.db file.
 
 
 .SH SQL commands
@@ -134,12 +132,14 @@ connections). \fB.bind <CNC_NAME> <CNC_NAME1> <CNC_NAME2> [<CNC_NAME> ...]\fP cr
 \fI<CNC_NAME>\fP which binds the tables of the \fI<CNC_NAME1>\fP, \fI<CNC_NAME2>\fP and any other
 connection specified.
 .IP \fB.c\fP
-Opens a connection or sets the current connection.
+Opens a connection or sets the current connection. Username and password can pe specified using
+the \fB<USERNAME>[:<PASSWORD>]@<DSN_NAME>\fP or \fB<USERNAME>[:<PASSWORD>]@<CNC_DEFINITION>\fP syntax,
+and if a username or a password is required but not specified, it will ba asked interactively.
 
-\fB.c <CNC_NAME> <DSN_NAME> [USER [PASSWORD]]\fP opens a connection internally known as \fI<CNC_NAME>\fP,
+\fB.c <CNC_NAME> <DSN_NAME>\fP opens a connection internally known as \fI<CNC_NAME>\fP,
 using the specified DSN.
 
-\fB.c <CNC_NAME> <CNC_DEFINITION> [USER [PASSWORD]] \fP opens a connection internally known as \fI<CNC_NAME>\fP,
+\fB.c <CNC_NAME> <CNC_DEFINITION>] \fP opens a connection internally known as \fI<CNC_NAME>\fP,
 using a connection specified by \fI<CNC_DEFINITION>\fP which is similar to the \fI<DSN_DEFINITION>\fP parameter
 of the \fB.lc\fP command.
 
diff --git a/tools/gda-sql.c b/tools/gda-sql.c
index 4280853..98f3d7f 100644
--- a/tools/gda-sql.c
+++ b/tools/gda-sql.c
@@ -31,6 +31,7 @@
 #include "tools-input.h"
 #include "command-exec.h"
 #include <unistd.h>
+#include <termios.h>
 #include <sys/types.h>
 #include <libgda/gda-quark-list.h>
 #include <libgda/gda-meta-struct.h>
@@ -54,7 +55,6 @@
 
 /* options */
 gboolean show_version = FALSE;
-gboolean ask_pass = FALSE;
 
 gchar *single_command = NULL;
 gchar *commandsfile = NULL;
@@ -73,8 +73,6 @@ gchar *auth_token = NULL;
 
 static GOptionEntry entries[] = {
 	{ "version", 'v', 0, G_OPTION_ARG_NONE, &show_version, "Show version information and exit", NULL },	
-        { "no-password-ask", 'p', 0, G_OPTION_ARG_NONE, &ask_pass, "Don't ask for a password when it is empty", NULL },
-
         { "output-file", 'o', 0, G_OPTION_ARG_STRING, &outfile, "Output file", "output file"},
         { "command", 'C', 0, G_OPTION_ARG_STRING, &single_command, "Run only single command (SQL or internal) and exit", "command" },
         { "commands-file", 'f', 0, G_OPTION_ARG_STRING, &commandsfile, "Execute commands from file, then exit (except if -i specified)", "filename" },
@@ -179,7 +177,6 @@ main (int argc, char *argv[])
 	data = g_new0 (MainData, 1);
 	data->parameters = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
 	main_data = data;
-	ask_pass = !ask_pass;
 
 	/* output file */
 	if (outfile) {
@@ -1241,6 +1238,10 @@ static gpointer thread_start_update_meta_store (MetaUpdateData *data);
 static void thread_ok_cb_update_meta_store (GdaThreader *threader, guint job, MetaUpdateData *data);
 static void thread_cancelled_cb_update_meta_store (GdaThreader *threader, guint job, MetaUpdateData *data);
 
+static gchar* read_hidden_passwd ();
+static void user_password_needed (GdaDsnInfo *info, const gchar *real_provider,
+				  gboolean *out_need_username, gboolean *out_need_password);
+
 /*
  * Open a connection
  */
@@ -1259,6 +1260,7 @@ open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_st
 	}
 	
 	GdaDsnInfo *info;
+	gboolean need_user, need_pass;
 	gchar *user, *pass, *real_cnc, *real_provider, *real_auth_string = NULL;
 
 	/* if cnc string is a regular file, then use it with SQLite */
@@ -1287,6 +1289,10 @@ open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_st
 		gda_connection_string_split (cnc_string, &real_cnc, &real_provider, &user, &pass);
 		real_cnc_string = g_strdup (cnc_string);
 	}
+
+	info = gda_config_get_dsn_info (real_cnc);
+	user_password_needed (info, real_provider, &need_user, &need_pass);
+
 	if (!real_cnc) {
 		g_free (user);
 		g_free (pass);
@@ -1297,55 +1303,70 @@ open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_st
 		return NULL;
 	}
 
-	if (ask_pass) {
-		if (user && !*user) {
-			gchar buf[80];
-			g_print (_("\tUsername for '%s': "), cnc_name);
-			if (scanf ("%80s", buf) == -1) {
-				g_free (real_cnc);
-				g_free (user);
-				g_free (pass);
-				g_free (real_provider);
-				g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR, 
-					     _("No username for '%s'"), cnc_string);
-				g_free (real_cnc_string);
-				return NULL;
-			}
+	if (!user && info && info->auth_string) {
+		GdaQuarkList* ql;
+		const gchar *tmp;
+		ql = gda_quark_list_new_from_string (info->auth_string);
+		tmp = gda_quark_list_find (ql, "USERNAME");
+		if (tmp)
+			user = g_strdup (tmp);
+		gda_quark_list_free (ql);
+	}
+	if (need_user && ((user && !*user) || !user)) {
+		gchar buf[80], *ptr;
+		g_print (_("\tUsername for '%s': "), cnc_name);
+		if (! fgets (buf, 80, stdin)) {
+			g_free (real_cnc);
 			g_free (user);
-			user = g_strdup (buf);
+			g_free (pass);
+			g_free (real_provider);
+			g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR, 
+				     _("No username for '%s'"), cnc_string);
+			g_free (real_cnc_string);
+			return NULL;
 		}
-		if (pass && !*pass) {
-			gchar buf[80];
-			g_print (_("\tPassword for '%s': "), cnc_name);
-			if (scanf ("%80s", buf) == -1) {
-				g_free (real_cnc);
-				g_free (user);
-				g_free (pass);
-				g_free (real_provider);
-				g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR, 
-					     _("No password for '%s'"), cnc_string);
-				g_free (real_cnc_string);
-				return NULL;
+		for (ptr = buf; *ptr; ptr++) {
+			if (*ptr == '\n') {
+				*ptr = 0;
+				break;
 			}
+		}
+		g_free (user);
+		user = g_strdup (buf);
+	}
+	if (need_pass && ((pass && !*pass) || !pass)) {
+		gchar *tmp;
+		g_print (_("\tPassword for '%s': "), cnc_name);
+		tmp = read_hidden_passwd ();
+		g_print ("\n");
+		if (! tmp) {
+			g_free (real_cnc);
+			g_free (user);
 			g_free (pass);
-			pass = g_strdup (buf);
+			g_free (real_provider);
+			g_set_error (error, GDA_CONNECTION_ERROR, GDA_CONNECTION_DSN_NOT_FOUND_ERROR, 
+				     _("No password for '%s'"), cnc_string);
+			g_free (real_cnc_string);
+			return NULL;
 		}
-		if (user || pass) {
-			gchar *s1;
-			s1 = gda_rfc1738_encode (user);
-			if (pass) {
-				gchar *s2;
-				s2 = gda_rfc1738_encode (pass);
-				real_auth_string = g_strdup_printf ("USERNAME=%s;PASSWORD=%s", s1, s2);
-				g_free (s2);
-			}
-			else
-				real_auth_string = g_strdup_printf ("USERNAME=%s", s1);
-			g_free (s1);
+		g_free (pass);
+		pass = tmp;
+	}
+
+	if (user || pass) {
+		gchar *s1;
+		s1 = gda_rfc1738_encode (user);
+		if (pass) {
+			gchar *s2;
+			s2 = gda_rfc1738_encode (pass);
+			real_auth_string = g_strdup_printf ("USERNAME=%s;PASSWORD=%s", s1, s2);
+			g_free (s2);
 		}
+		else
+			real_auth_string = g_strdup_printf ("USERNAME=%s", s1);
+		g_free (s1);
 	}
 	
-	info = gda_config_get_dsn_info (real_cnc);
 	if (info && !real_provider)
 		newcnc = gda_connection_open_from_dsn (real_cnc_string, real_auth_string, 0, error);
 	else 
@@ -1456,6 +1477,64 @@ open_connection (SqlConsole *console, const gchar *cnc_name, const gchar *cnc_st
 	return cs;
 }
 
+static void
+user_password_needed (GdaDsnInfo *info, const gchar *real_provider,
+		      gboolean *out_need_username, gboolean *out_need_password)
+{
+	GdaProviderInfo *pinfo = NULL;
+	if (out_need_username)
+		*out_need_username = FALSE;
+	if (out_need_password)
+		*out_need_password = FALSE;
+
+	if (real_provider)
+		pinfo = gda_config_get_provider_info (real_provider);
+	else if (info)
+		pinfo = gda_config_get_provider_info (info->provider);
+
+	if (pinfo && pinfo->auth_params) {
+		if (out_need_password && gda_set_get_holder (pinfo->auth_params, "PASSWORD"))
+			*out_need_password = TRUE;
+		if (out_need_username && gda_set_get_holder (pinfo->auth_params, "USERNAME"))
+			*out_need_username = TRUE;
+	}
+}
+
+static gchar*
+read_hidden_passwd (void)
+{
+	gchar *p, password [100];
+	int fail;
+	struct termios termio;
+	
+	fail = tcgetattr (0, &termio);
+	if (fail)
+		return NULL;
+	
+	termio.c_lflag &= ~ECHO;
+	fail = tcsetattr (0, TCSANOW, &termio);
+	if (fail)
+		return NULL;
+	
+	p = fgets (password, sizeof (password) - 1, stdin);
+	termio.c_lflag |= ECHO;
+	tcsetattr (0, TCSANOW, &termio);
+	
+	if (!p)
+		return NULL;
+
+	for (p = password; *p; p++) {
+		if (*p == '\n') {
+			*p = 0;
+			break;
+		}
+	}
+	p = g_strdup (password);
+	memset (password, 0, sizeof (password));
+
+	return p;
+}
+
 static gpointer
 thread_start_update_meta_store (MetaUpdateData *data)
 {
@@ -1958,8 +2037,8 @@ build_internal_commands_list (void)
 	/* specific commands */
 	c = g_new0 (GdaInternalCommand, 1);
 	c->group = _("General");
-	c->name = g_strdup_printf (_("%s [CNC_NAME [DSN [USER [PASSWORD]]]]"), "c");
-	c->description = _("Connect to another defined data source (DSN, see \\l)");
+	c->name = g_strdup_printf (_("%s [CNC_NAME [DSN|CONNECTION STRING]]"), "c");
+	c->description = _("Opens a new connection or lists opened connections");
 	c->args = NULL;
 	c->command_func = (GdaInternalCommandFunc) extra_command_manage_cnc;
 	c->user_data = NULL;



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