PQsetdbLogin() in fe-connect.c in libpq
- PQsetdbLogin OPTIONS EXTENSION PATCH - This patch to fe-connect.c is intended to make it possible to set options like sslmode or connect_timeout with the PQsetdbLogin() function. This is needed as many popular interfaces or routines (i.e. ECPG, mod_auth_pgsql ...) use PQsetdbLogin function rather than PQconnectdb() the latter provides for such functionnalities. Thus most of the PQsetdbLogin limitations over the PQconnectdb function can be avoided. In addition, this patch prevents the setting via URL of the parameters normally supplied to PQsetdbLogin() outside URL as : host, login, pwd ... Principles : i) The parsed (pgoptions string) string is supplied via URL for ECPG, and via Auth_PG_Options runtime configuration variable for mod_auth_pgsql... In all cases the parsed string is the string passed through the pgoptions parameter to PQsetdbLogin ii) If only command-line-options for backend are supplied or no options supplied at all : then the old behaviour fully applies. iii) If command-line-options for backend provided and option/value groups for sslmode, connect_timeout or eventually for requiressl are present : - The command-line-options must appear first (right after the question-mark for ECPG,in parameter string for Auth_PG_Options) and must be separated from our options=value groups by an ampersand ('&') character. Of course the command-line-options for backend may contain option=value groups of their own if this is the cases the latter should not be separated by '&' : Valid examples: (for mod_auth_pgsql) Auth_PG_Options "-c 'geqo=off'&sslmod=require" Auth_PG_Options &sslmod=require Auth_PG_Options sslmod=require # is valid too # see below the # "anti-idiosyncracy" statement. - The other option=value groups must be separated by an '&' character (for URL parameter compatibility. Auth_PG_Options sslmod=require&connect_timeout=<timeout_value> Other examples ECPG URL example : tcp:postgresql://<ip|domain>:5432/<dbname>[?[<options-for-backend>][<&sslmode=require|&requiressl=1>[&connect_timeout=<value>[& ...]]]] tcp:postgresql://<Dotted IP Number>:5432/dbname?&sslmode=require tcp:postgresql://<Dotted IP Number>:5432/dbname?sslmode=require (for the latter example see * below) (*) For anti-idiosyncratic convenience : if the first parameter is one of { sslmode, requiressl, connect_timeout } the leading ampersand might be omitted and that parameter and the following charachers won't be considered to be intended for the backend as (command-line-options). So for instance tcp:postgresql://<Dotted IP Number>:5432/dbname?sslmode=require will be considered as tcp:postgresql://<Dotted IP Number>:5432/dbname?&sslmode=require See signed fe-connect.c.URL.patch file attached. My public CD31DA11 key can be found on pgp.mit.edu keyserver Your are welcome to send your comments to : Waldemar Olenycz <[EMAIL PROTECTED]>
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 - --- ./postgresql-8.0.4/src/interfaces/libpq/fe-connect.c.orig 2005-07-13 17:26:06.000000000 +0200 +++ ./postgresql-8.0.4/src/interfaces/libpq/fe-connect.c 2005-11-09 10:34:25.263540736 +0100 @@ -532,8 +532,241 @@ return connOptions; } - -/* ---------------- - - * PQsetdbLogin +/* + * - PQsetdbLogin OPTIONS EXTENSION PATCH - + * + * + * To make it possible to set options like sslmode with ECPG + * as ECPG uses PQsetdbLogin with its limitations + * Use : essentially to override default settings with + * PQsetdbLogin() for parameters like sslmode or + * connect_timeout, while at the same time preventing + * the setting via URL of the parameters normally + * supplied to PQsetdbLogin outside URL as : host, login, + * pwd ... + * + * URL ex. tcp:postgresql://<ip|domain>:5432/<dbname>[?[<options-for-backend>][<&sslmode=require|&requiressl=1>[&connect_timeout=<value>[& ...]]]] + * + * Ex. tcp:postgresql://<Dotted IP Number>:5432/dbname?&sslmode=require + * + * Previously only options for the backend as "Backend-Debug-Options" could be passed. + * In this version everything between the '?' character and the first ampersand character + * is considered as the former <options string> if no ampersand is present, the behaviour + * is that of the former. + * + * For anti-idiosyncratic convenience : if the first parameter is + * one of { sslmode, requiressl, connect_timeout } the leading ampersand might + * be omitted and that parameter and the following won't be considered + * to be intended for the backend as (command-line-options). + * So for instance tcp:postgresql://<Dotted IP Number>:5432/dbname?sslmode=require + * will be considered as tcp:postgresql://<Dotted IP Number>:5432/dbname?&sslmode=require + * + * conninfo_parseURL : Parses a substring from a previously strdup-licated options string passed + * + * Most of the parsing code is adapted from conninfo_parse(). + * + * Send your comments to + * + * Waldemar Olenycz <[EMAIL PROTECTED]> 2005-11-04 2005-11-09 + * + */ + +/* (The following prototype is to keep gcc happy + * should be moved into an include file ?) + */ + +char * +conninfo_parseURL(char *, PQExpBuffer ); + +/* + * - PQsetdbLogin OPTIONS EXTENSION PATCH - (continued) + * + * conninfo_parseURL + * + * Parses a substring from a previously strdup-licated options string passed + * to PQsetdbLogin by an ECPG connection request and tests the options + * allowed (ex. dbname, login, pwd etc. are not allowed here as they + * are passed as parameters to PQsetdbLogin) and replaces the '&' characters by ' '. + * + * Return values : the modified string or NULL if not okay. + * + * Most of the parsing code is adapted from conninfo_parse(). + * + */ + +char * +conninfo_parseURL(char *pgparms, PQExpBuffer errorMessage) +{ + char *cp; + char buf[512]; + int i; + cp = pgparms; + + while (*cp) + { + /* Skip blanks before the parameter name */ + if (isspace((unsigned char) *cp)) + { + cp++; + continue; + } + + /* Get the parameter name */ + + for (i = 0; *cp && i < sizeof(buf) - 1; i++) + { + if (*cp == '=') + break; + if (isspace((unsigned char) *cp)) + { + cp++; + buf[i]='\0'; + while (*cp) + { + if (!isspace((unsigned char) *cp)) + { + break; + } + cp++; + } + break; + } + else + buf[i]=*cp++; + } + + + /* Check that there is a following '=' */ + if (*cp != '=') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("missing \"=\" after \"%s\" in URL connection info string\n"), + buf); + return NULL; + } + cp++; + + buf[i]='\0'; + + if(strcmp(buf,"sslmode") && +#ifdef USE_SSL + +/* +* "requiressl" is deprecated, its purpose having been taken over by +* "sslmode". It remains for backwards compatibility. +*/ + strcmp(buf,"requiressl") && +#endif + + strcmp(buf,"connect_timeout")) + { + if(strcmp(buf,"requiressl")) + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid connection parameter name in URL \"%s\"\n"), + buf); + else + printfPQExpBuffer(errorMessage, + libpq_gettext("USE_SSL not compiled-in and requiressl used in URL\n")); + return NULL; + } + + /* Skip blanks after the '=' */ + while (*cp) + { + if (!isspace((unsigned char) *cp)) + { + break; + } + *cp++=' '; + } + + /* Get the parameter value */ + + if (*cp != '\'') + { + for (i = 0; *cp && i < sizeof(buf) - 1; i++) + { + if (isspace((unsigned char) *cp)) + { + *cp++ = ' '; + break; + } + if (*cp == '\\') + { + cp++; + if (*cp != '\0') + buf[i]=*cp++; + } + else if(*cp=='&') + break; + else if(*cp=='=') + { + printfPQExpBuffer(errorMessage, + libpq_gettext("invalid character '=' in parameter (connection info URL string)\n")); + return NULL; + } + else + buf[i]=*cp++; + } + if(!i) + { printfPQExpBuffer(errorMessage, + libpq_gettext("No value present for parameter in URL string\n")); + return NULL; + } + } + else + { cp++; + for (;;) + { + if (*cp == '\0') + { printfPQExpBuffer(errorMessage, + libpq_gettext("unterminated quoted string in connection info URL string\n")); + return NULL; + } + if (*cp == '\\') + { + cp++; + if (*cp != '\0') + cp++; + continue; + } + if (*cp == '\'') + { + cp++; + break; + } + cp++; + } + } + /* Skip blanks after parameter value */ + while (*cp) + { + if (!isspace((unsigned char) *cp)) + { + break; + } + cp++; + } + if(*cp == '&') + *cp++=' '; + else if(*cp != '\0') + { printfPQExpBuffer(errorMessage, + libpq_gettext("In URL string : ampersand '&' delimiter required between parameter/value expressions\n")); + return NULL; + } + } + return pgparms; +} + +/* + * - PQsetdbLogin OPTIONS EXTENSION PATCH - (continued) + * + * PQsetdbLogin : Modified to work with conninfo_parseURL() see above comments + * + * + * + * ---------------- + * PQsetdbLogin (with OPTIONS EXTENSION PATCH) * * establishes a connection to a postgres backend through the postmaster * at the specified host and port. @@ -550,6 +783,8 @@ const char *pwd) { PGconn *conn; + char *p_pgoptions=NULL; + char *p_parms=NULL; /* * Allocate memory for the conn structure @@ -562,9 +797,75 @@ * Parse an empty conninfo string in order to set up the same defaults * that PQconnectdb() would use. */ - - if (!connectOptions1(conn, "")) + if (pgoptions == NULL || *pgoptions == '\0') + { + if (!connectOptions1(conn, "")) return conn; + } + else + { int i,j=0; + char *cp; + + if((p_pgoptions=strdup(pgoptions)) == NULL) + { + printfPQExpBuffer(&conn->errorMessage, libpq_gettext("out of memory\n")); + return NULL; + } + + cp = p_pgoptions; + /* + * Parse the first ampersand of the URL string + */ + + for (i=0; *cp ; i++,cp++) + if(*cp=='&') + { + i++; + break; + } + + if(i) + { for ( ; j < i && isspace((unsigned char) p_pgoptions[j]) ; j++) + ; + + if(i-j >= 7 && (!strncmp(p_pgoptions+j,"sslmode",7) || +#ifdef USE_SSL + !strncmp(p_pgoptions+j,"requiressl",10) || +#endif + !strncmp(p_pgoptions+j,"connect_timeout",15))) + { /* + * Assume no command-line-options for back-ground present + * (leading ampersand omitted : ok) + */ + i=j; + } + else /* + * command-line-options for back-ground present + * in first part of p_pgoptions string + */ + + *cp='\0'; + } + + p_parms = conninfo_parseURL(p_pgoptions+i,&conn->errorMessage); + + /* + * Eventually Force URL string parameters in defaults for this connection + */ + + if (!connectOptions1(conn,(p_parms != NULL ? p_parms : ""))) + { + free(p_pgoptions); + return conn; + } + + if(i==j) + { free(p_pgoptions); + p_pgoptions=NULL; + } + } + /* * Absorb specified options into conn structure, overriding defaults */ @@ -582,12 +883,19 @@ conn->pgport = strdup(pgport); } - - if (pgoptions && pgoptions[0] != '\0') + /* + * Modified for compatibility with conninfo_parseURL + * - begin - + */ + if (p_pgoptions && p_pgoptions[0] != '\0') { if (conn->pgoptions) free(conn->pgoptions); - - conn->pgoptions = strdup(pgoptions); + conn->pgoptions = strdup(p_pgoptions); + /* and cleanup */ + free(p_pgoptions); } + /* - end - */ if (pgtty && pgtty[0] != '\0') { -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.0.6 (GNU/Linux) Comment: Pour information voir http://www.gnupg.org iD8DBQFDcdc0cZZkWM0x2hERAt6YAJ9e52YDCYv7kOFHFfA4uz69sBlDrACgiEvo RjMszGSpb1LCwcinYbPW51k= =RdXI -----END PGP SIGNATURE-----
---------------------------(end of broadcast)--------------------------- TIP 9: In versions below 8.0, the planner will ignore your desire to choose an index scan if your joining column's datatypes do not match