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

Reply via email to