Backport to wheezy
[hcoop/debian/courier-authlib.git] / authmysqllib.c
index 40be961..08ebdee 100644 (file)
@@ -1,5 +1,5 @@
 /*
-** Copyright 2000-2005 Double Precision, Inc.  See COPYING for
+** Copyright 2000-2010 Double Precision, Inc.  See COPYING for
 ** distribution information.
 */
 
 
 #define err courier_auth_err
 
-/* siefca@pld.org.pl */
-#define                MAX_SUBSTITUTION_LEN    32
-#define                SV_BEGIN_MARK           "$("
-#define                SV_END_MARK             ")"
-#define                SV_BEGIN_LEN            ((sizeof(SV_BEGIN_MARK))-1)
-#define                SV_END_LEN              ((sizeof(SV_END_MARK))-1)
-
-static const char rcsid[]="$Id: authmysqllib.c,v 1.45 2007/10/07 02:50:45 mrsam Exp $";
-
-/* siefca@pld.org.pl */
-struct var_data {                      
-       const char *name;
-       const char *value;
-       const size_t size;
-       size_t value_length;
-       } ;
-
-/* siefca@pld.org.pl */
-typedef int (*parsefunc)(const char *, size_t, void *);
-
 static const char *read_env(const char *env)
 {
-static char *mysqlauth=0;
-static size_t mysqlauth_size=0;
-size_t i;
-char   *p=0;
-int    l=strlen(env);
+       return authgetconfig(AUTHMYSQLRC, env);
+}
 
-       if (!mysqlauth)
-       {
-       FILE    *f=fopen(AUTHMYSQLRC, "r");
-       struct  stat    buf;
+static MYSQL mysql_buf;
 
-               if (!f) return (0);
-               if (fstat(fileno(f), &buf) ||
-                       (mysqlauth=malloc(buf.st_size+2)) == 0)
-               {
-                       fclose(f);
-                       return (0);
-               }
-               if (fread(mysqlauth, buf.st_size, 1, f) != 1)
-               {
-                       free(mysqlauth);
-                       mysqlauth=0;
-                       fclose(f);
-                       return (0);
-               }
-               mysqlauth[mysqlauth_size=buf.st_size]=0;
-
-               for (i=0; i<mysqlauth_size; i++)
-                       if (mysqlauth[i] == '\n')
-                       {       /* siefca@pld.org.pl */
-                               if (!i || mysqlauth[i-1] != '\\')
-                               {
-                                       mysqlauth[i]='\0';
-                               }
-                               else
-                               {
-                                       mysqlauth[i]=mysqlauth[i-1]= ' ';
-                               }
-                       }
-               fclose(f);
-       }
+static MYSQL *mysql=0;
 
-       for (i=0; i<mysqlauth_size; )
-       {
-               p=mysqlauth+i;
-               if (memcmp(p, env, l) == 0 &&
-                       isspace((int)(unsigned char)p[l]))
-               {
-                       p += l;
-                       while (*p && *p != '\n' &&
-                               isspace((int)(unsigned char)*p))
-                               ++p;
-                       break;
-               }
+static char *escape_str(const char *c, size_t n)
+{
+       char *buf=malloc(n*2+1);
 
-               while (i < mysqlauth_size)
-                       if (mysqlauth[i++] == 0)        break;
+       if (!buf)
+       {
+               perror("malloc");
+               return NULL;
        }
 
-       if (i < mysqlauth_size)
-               return (p);
-       return (0);
+       mysql_real_escape_string(mysql, buf, c, n);
+       return buf;
 }
 
-static MYSQL mysql_buf;
+static void set_session_options(void)
+/*
+* session variables can be set once for the whole session
+*/
+{
+/* Anton Dobkin <anton@viansib.ru>, VIAN, Ltd. */
+#if MYSQL_VERSION_ID >= 41000    
+       const char *character_set=read_env("MYSQL_CHARACTER_SET"), *check;
+    
+        if(character_set){
 
-static MYSQL *mysql=0;
+            /*
+            * This function works like the SET NAMES statement, but also sets
+            * the value of mysql->charset, and thus affects the character set
+            * used by mysql_real_escape_string()
+            *
+            * (return value apparently work the opposite of what is documented)
+            */
+            mysql_set_character_set(mysql, character_set);
+            check = mysql_character_set_name(mysql);
+            if (strcmp(character_set, check) != 0)
+            {
+                err("Cannot set MySQL character set \"%s\", working with \"%s\"\n",
+                    character_set, check);
+            }
+            else
+            {
+                DPRINTF("Install of a character set for MySQL: %s", character_set);
+            }
+        }
+#endif /* 41000 */
+}
 
 static int do_connect()
 {
@@ -236,6 +202,16 @@ unsigned int  use_ssl=0;
                mysql=0;
                return (-1);
        }
+       
+       DPRINTF("authmysqllib: connected. Versions: "
+               "header %lu, "
+               "client %lu, "
+               "server %lu",
+               (long)MYSQL_VERSION_ID,
+               mysql_get_client_version(),
+               mysql_get_server_version(mysql));
+
+       set_session_options();  
        return (0);
 }
 
@@ -272,383 +248,11 @@ static void initui()
        memset(&ui, 0, sizeof(ui));
 }
 
-static void append_username(char *p, const char *username,
-                           const char *defdomain)
-{
-       for (strcpy(p, username); *p; p++)
-               if (*p == '\'' || *p == '"' || *p == '\\' ||
-                   (int)(unsigned char)*p < ' ')
-                       *p=' '; /* No funny business */
-       if (strchr(username, '@') == 0 && defdomain && *defdomain)
-               strcat(strcpy(p, "@"), defdomain);
-}
-
-/* siefca@pld.org.pl */
-static struct var_data *get_variable (const char *begin, size_t len,
-                                          struct var_data *vdt)
-{
-struct var_data *vdp;
-
-       if (!begin || !vdt) /* should never happend */
-       {
-               err("authmysql: critical error while "
-                                "parsing substitution variable");
-               return NULL;
-       }
-       if (len < 1)
-       {
-               err("authmysql: unknown empty substitution "
-                                "variable - aborting");
-               return NULL;
-       }
-       if (len > MAX_SUBSTITUTION_LEN)
-       {
-               err("authmysql: variable name too long "
-                                "while parsing substitution. "
-                                "name begins with "
-                                SV_BEGIN_MARK
-                                "%.*s...", MAX_SUBSTITUTION_LEN, begin);
-               return NULL;
-       }
-       
-       for (vdp=vdt; vdp->name; vdp++)
-               if (vdp->size == len+1 &&
-                   !strncmp(begin, vdp->name, len))
-               {
-                       if (!vdp->value)
-                               vdp->value = "";
-                       if (!vdp->value_length)         /* length cache */
-                               vdp->value_length = strlen (vdp->value);
-                       return vdp;
-               }
-       
-       err("authmysql: unknown substitution variable "
-                        SV_BEGIN_MARK
-                        "%.*s"
-                        SV_END_MARK
-                        , (int)len, begin);
-       
-       return NULL;
-}
-
-/* siefca@pld.org.pl */
-static int ParsePlugin_counter (const char *p, size_t length, void *vp)
-{
-       if (!p || !vp || length < 0)
-       {
-               err("authmysql: bad arguments while counting "
-                                "query string");
-               return -1;
-       }
-       
-       *((size_t *)vp) += length;
-   
-       return 0;
-}
-
-/* siefca@pld.org.pl */
-static int ParsePlugin_builder (const char *p, size_t length, void *vp)
-{
-char   **strptr = (char **) vp;
-
-       if (!p || !vp || length < 0)
-       {
-               err("authmysql: bad arguments while building "
-                                "query string");
-               return -1;
-       }
-       
-       if (!length) return 0;
-       memcpy ((void *) *strptr, (void *) p, length);
-       *strptr += length;
-       
-       return 0;
-}
-
-/* siefca@pld.org.pl */
-static int parse_core  (const char *source, struct var_data *vdt,
-                       parsefunc outfn, void *result)
-{
-size_t v_size          = 0,
-       t_size          = 0;
-const char     *p, *q, *e,
-               *v_begin, *v_end,
-               *t_begin, *t_end;
-struct var_data        *v_ptr;
-
-       if (!source)
-               source = "";
-       if (!result)
-       {
-               err("authmysql: no memory allocated for result "
-                                "while parser core was invoked");
-               return -1;
-       }
-       if (!vdt)
-       {
-               err("authmysql: no substitution table found "
-                                "while parser core was invoked");
-               return -1;
-       }
-       
-       q = source;
-       while ( (p=strstr(q, SV_BEGIN_MARK)) )
-       {
-               e = strstr (p, SV_END_MARK);
-               if (!e)
-               {
-                       err("authmysql: syntax error in "
-                                        "substitution "
-                                        "- no closing symbol found! "
-                                        "bad variable begins with:"
-                                        "%.*s...", MAX_SUBSTITUTION_LEN, p);
-                       return -1;
-               }
-               
-               /*
-                **
-                **          __________sometext$(variable_name)_________
-                **                    |      |  |           |  
-                **             t_begin' t_end'  `v_begin    `v_end
-                **
-                 */
-
-               v_begin = p+SV_BEGIN_LEN; /* variable field ptr             */
-               v_end   = e-SV_END_LEN;   /* variable field last character  */
-               v_size  = v_end-v_begin+1;/* variable field length          */
-               
-               t_begin = q;              /* text field ptr                 */
-               t_end   = p-1;            /* text field last character      */
-               t_size  = t_end-t_begin+1;/* text field length              */
-
-               /* work on text */
-               if ( (outfn (t_begin, t_size, result)) == -1 )
-                       return -1;
-               
-               /* work on variable */
-               v_ptr = get_variable (v_begin, v_size, vdt);
-               if (!v_ptr) return -1;
-               
-               if ( (outfn (v_ptr->value, v_ptr->value_length, result)) == -1 )
-                       return -1;
-               
-               q = e + 1;
-       }
-
-       /* work on last part of text if any */
-       if (*q != '\0')
-               if ( (outfn (q, strlen(q), result)) == -1 )
-                       return -1;
-
-       return 0;
-}
-
-/* siefca@pld.org.pl */
-static char *parse_string (const char *source, struct var_data *vdt)
-{
-struct var_data *vdp   = NULL;
-char   *output_buf     = NULL,
-       *pass_buf       = NULL;
-size_t buf_size        = 2;
-
-       if (source == NULL || *source == '\0' || 
-           vdt == NULL    || vdt[0].name == NULL)
-       {
-               err("authmysql: source clause is empty "
-                      "- this is critical error");
-               return NULL;
-       }
-
-       /* zero var_data length cache - important! */
-       for (vdp=vdt; vdp->name; vdp++)
-               vdp->value_length = 0;
-
-
-       /* phase 1 - count and validate string */
-       if ( (parse_core (source, vdt, &ParsePlugin_counter, &buf_size)) != 0)
-               return NULL;
-
-       /* phase 2 - allocate memory */
-       output_buf = malloc (buf_size);
-       if (!output_buf)
-       {
-               perror ("malloc");
-               return NULL;
-       }
-       pass_buf = output_buf;
-
-       /* phase 3 - build the output string */
-       if ( (parse_core (source, vdt, &ParsePlugin_builder, &pass_buf)) != 0)
-       {
-               free (output_buf);
-               return NULL;
-       }       
-       *pass_buf = '\0';
-       
-       return output_buf;
-}
-
-/* siefca@pld.org.pl */
-static const char *get_localpart (const char *username)
-{
-size_t         lbuf    = 0;
-const char     *l_end, *p;
-char           *q;
-static char    localpart_buf[130];
-       
-       if (!username || *username == '\0')     return NULL;
-       
-       p = strchr(username,'@');
-       if (p)
-       {
-               if ((p-username) > 128)
-                       return NULL;
-               l_end = p;
-       }
-       else
-       {
-               if ((lbuf = strlen(username)) > 128)
-                       return NULL;
-               l_end = username + lbuf;
-       }
-
-       p=username;
-       q=localpart_buf;
-       
-       while (*p && p != l_end)
-               if (*p == '\"' || *p == '\\' ||
-                   *p == '\'' || (int)(unsigned char)*p < ' ')
-                       p++;
-               else
-                       *q++ = *p++;
-
-       *q = '\0';
-       return localpart_buf;
-}
-
-/* siefca@pld.org.pl */
-static const char *get_domain (const char *username, const char *defdomain)
-{
-static char    domain_buf[260];
-const char     *p;
-char           *q;
-       
-       if (!username || *username == '\0')     return NULL;
-       p = strchr(username,'@');
-       
-       if (!p || *(p+1) == '\0')
-       {
-               if (defdomain && *defdomain)
-                       return defdomain;
-               else
-                       return NULL;
-       }
-
-       p++;
-       if ((strlen(p)) > 256)
-               return NULL;
-       
-       q = domain_buf;
-       while (*p)
-               if (*p == '\"' || *p == '\\' ||
-                   *p == '\'' || (int)(unsigned char)*p < ' ')
-                       p++;
-               else
-                       *q++ = *p++;
-
-       *q = '\0';
-       return domain_buf;
-}
-
-/* siefca@pld.org.pl */
-
-static const char *validateMyPassword (const char *password)
-{
-static char pass_buf[2][540]; /* Use two buffers, see parse_chpass_clause */
-static int next_pass=0;
-const char     *p;
-char           *q, *endq;
-       
-       if (!password || *password == '\0' || (strlen(password)) > 256)
-               return NULL;
-       
-       next_pass= 1-next_pass;
-
-       p = password;
-       q = pass_buf[next_pass];
-       endq = q + sizeof pass_buf[next_pass];
-       
-       while (*p && q < endq)
-       {
-               if (*p == '\"' || *p == '\\' || *p == '\'')
-                       *q++ = '\\';
-               *q++ = *p++;
-       }
-       
-       if (q >= endq)
-               return NULL;
-       
-       *q = '\0';
-       return pass_buf[next_pass];
-}
-
-/* siefca@pld.org.pl */
-static char *parse_select_clause (const char *clause, const char *username,
-                                 const char *defdomain,
-                                 const char *service)
-{
-static struct var_data vd[]={
-           {"local_part",      NULL,   sizeof("local_part"),   0},
-           {"domain",          NULL,   sizeof("domain"),       0},
-           {"service",         NULL,   sizeof("service"),      0},
-           {NULL,              NULL,   0,                      0}};
-
-       if (clause == NULL || *clause == '\0' ||
-           !username || *username == '\0')
-               return NULL;
-       
-       vd[0].value     = get_localpart (username);
-       vd[1].value     = get_domain (username, defdomain);
-       if (!vd[0].value || !vd[1].value)
-               return NULL;
-       vd[2].value     = service;
-
-       return (parse_string (clause, vd));
-}
-
-/* siefca@pld.org.pl */
-static char *parse_chpass_clause (const char *clause, const char *username,
-                                 const char *defdomain, const char *newpass,
-                                 const char *newpass_crypt)
-{
-static struct var_data vd[]={
-           {"local_part",      NULL,   sizeof("local_part"),           0},
-           {"domain",          NULL,   sizeof("domain"),               0},
-           {"newpass",         NULL,   sizeof("newpass"),              0},
-           {"newpass_crypt",   NULL,   sizeof("newpass_crypt"),        0},
-           {NULL,              NULL,   0,                              0}};
-
-       if (clause == NULL || *clause == '\0'           ||
-           !username || *username == '\0'              ||
-           !newpass || *newpass == '\0'                ||
-           !newpass_crypt || *newpass_crypt == '\0')   return NULL;
-
-       vd[0].value     = get_localpart (username);
-       vd[1].value     = get_domain (username, defdomain);
-       vd[2].value     = validateMyPassword (newpass);
-       vd[3].value     = validateMyPassword (newpass_crypt);
-       
-       if (!vd[0].value || !vd[1].value ||
-           !vd[2].value || !vd[3].value)       return NULL;
-
-       return (parse_string (clause, vd));
-}
-
 struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username,
                                                 const char *service)
 {
 const char *defdomain  =NULL;
-char   *querybuf, *p;
+char   *querybuf;
 MYSQL_ROW      row;
 MYSQL_RES      *result;
 int            num_fields;
@@ -656,8 +260,14 @@ char               *endp;
 
 const char     *select_clause; /* siefca@pld.org.pl */
 
-static const char query[]=
-       "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = \"";
+#define DEFAULT_SELECT_QUERY "SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = '%s%s%s' %s%s%s", \
+               login_field, crypt_field, clear_field, uid_field,\
+               gid_field, home_field, maildir_field, quota_field,\
+               name_field, options_field, user_table, login_field,\
+               username_escaped,\
+               has_domain || !*defdomain ? "":"@", has_domain ? "":defdomain, \
+               *where_clause ? " AND (":"", where_clause,\
+               *where_clause ? ")":""
 
        if (do_connect())       return (0);
 
@@ -681,6 +291,10 @@ static const char query[]=
                                *quota_field,
                                *options_field,
                                *where_clause;
+               char            *username_escaped;
+               size_t  query_size;
+               char dummy_buf[1];
+               int has_domain;
 
                user_table=read_env("MYSQL_USER_TABLE");
 
@@ -731,47 +345,40 @@ static const char query[]=
                where_clause=read_env("MYSQL_WHERE_CLAUSE");
                if (!where_clause) where_clause = "";
 
-               querybuf=malloc(sizeof(query) + 100
-                       + 2 * strlen(login_field)
-                       + strlen(crypt_field)
-                       + strlen(clear_field)
-                       + strlen(uid_field) + strlen(gid_field)
-                       + strlen(home_field)
-                       + strlen(maildir_field)
-                       + strlen(quota_field)
-                       + strlen(name_field)
-                       + strlen(options_field)
-                       + strlen(user_table)
-                       + strlen(username)
-                       + strlen(defdomain)
-                       + strlen(where_clause));
+               username_escaped=malloc(strlen(username)*2+1);
 
-               if (!querybuf)
+               if (!username_escaped)
                {
                        perror("malloc");
                        return (0);
                }
 
-               sprintf(querybuf, query, login_field, crypt_field, clear_field, 
-                       uid_field, gid_field, home_field, maildir_field, quota_field,
-                       name_field, options_field, user_table, login_field);
+               mysql_real_escape_string(mysql, username_escaped,
+                                        username, strlen(username));
 
-               p=querybuf+strlen(querybuf);
+               has_domain=strchr(username, '@') != NULL;
 
-               append_username(p, username, defdomain);
-               strcat(p, "\"");
-       
-               if (strcmp(where_clause, "")) {
-                       strcat(p, " AND (");
-                       strcat(p, where_clause);
-                       strcat(p, ")");
+               query_size=snprintf(dummy_buf, 1, DEFAULT_SELECT_QUERY);
+
+               querybuf=malloc(query_size+1);
+
+               if (!querybuf)
+               {
+                       free(username_escaped);
+                       perror("malloc");
+                       return(0);
                }
+
+               snprintf(querybuf, query_size+1, DEFAULT_SELECT_QUERY);
+               free(username_escaped);
+
        }
        else
        {
                /* siefca@pld.org.pl */
-               querybuf=parse_select_clause (select_clause, username,
-                                             defdomain, service);
+               querybuf=auth_parse_select_clause (escape_str,
+                                                  select_clause, username,
+                                                  defdomain, service);
                if (!querybuf)
                {
                        DPRINTF("parse_select_clause failed (DEFAULT_DOMAIN not set?)");
@@ -779,43 +386,6 @@ static const char query[]=
                }
        }
 
-/* Anton Dobkin <anton@viansib.ru>, VIAN, Ltd. */
-#if MYSQL_VERSION_ID >= 41000    
-       const char *character_set=read_env("MYSQL_CHARACTER_SET");
-    
-        if(character_set){
-            
-           char *character_set_buf;
-               
-            character_set_buf=malloc(strlen(character_set)+11);
-                       
-           if (!character_set_buf)
-            {
-               perror("malloc");
-               return (0);
-           }
-                                                   
-           strcpy(character_set_buf, "SET NAMES ");
-           strcat(character_set_buf, character_set);
-                                                               
-            DPRINTF("Install of a character set for MySQL. SQL query: SET NAMES %s", character_set);   
-                                                                       
-            if(mysql_query (mysql, character_set_buf))
-            {    
-                err("Install of a character set for MySQL is failed: %s MYSQL_CHARACTER_SET: may be invalid character set", mysql_error(mysql));
-               auth_mysql_cleanup();
-                           
-               if (do_connect())
-               {
-                   free(character_set_buf);
-                   return (0);
-               }
-            }
-           
-           free(character_set_buf);
-        }
-#endif 
-
        DPRINTF("SQL query: %s", querybuf);
        if (mysql_query (mysql, querybuf))
        {
@@ -920,12 +490,12 @@ int auth_mysql_setpass(const char *user, const char *pass,
                       const char *oldpass)
 {
        char *newpass_crypt;
-       const char *p;
-       int l;
        char *sql_buf;
-       const char *comma;
        int rc=0;
 
+       char *clear_escaped;
+       char *crypt_escaped;
+
        const char  *clear_field        =NULL,
                    *crypt_field        =NULL,
                    *defdomain          =NULL,
@@ -934,116 +504,122 @@ int auth_mysql_setpass(const char *user, const char *pass,
                    *login_field        =NULL,
                    *chpass_clause      =NULL; /* siefca@pld.org.pl */
 
-       if (!mysql)
-               return (-1);
-
+       if (do_connect())       return (-1);
 
        if (!(newpass_crypt=authcryptpasswd(pass, oldpass)))
                return (-1);
 
-       for (l=0, p=pass; *p; p++)
+       clear_escaped=malloc(strlen(pass)*2+1);
+
+       if (!clear_escaped)
        {
-               if ((int)(unsigned char)*p < ' ')
-               {
-                       free(newpass_crypt);
-                       return (-1);
-               }
-               if (*p == '"' || *p == '\\')
-                       ++l;
-               ++l;
+               perror("malloc");
+               free(newpass_crypt);
+               return -1;
        }
 
+       crypt_escaped=malloc(strlen(newpass_crypt)*2+1);
+
+       if (!crypt_escaped)
+       {
+               perror("malloc");
+               free(clear_escaped);
+               free(newpass_crypt);
+               return -1;
+       }
+
+       mysql_real_escape_string(mysql, clear_escaped, pass, strlen(pass));
+       mysql_real_escape_string(mysql, crypt_escaped,
+                                newpass_crypt, strlen(newpass_crypt));
+
        /* siefca@pld.org.pl */
        chpass_clause=read_env("MYSQL_CHPASS_CLAUSE");
        defdomain=read_env("DEFAULT_DOMAIN");
        user_table=read_env("MYSQL_USER_TABLE");
        if (!chpass_clause)
        {
+               int has_domain=strchr(user, '@') != NULL;
+               char *username_escaped;
+               char dummy_buf[1];
+               size_t sql_buf_size;
+
+               username_escaped=malloc(strlen(user)*2+1);
+
+               if (!username_escaped)
+               {
+                       perror("malloc");
+                       free(clear_escaped);
+                       free(crypt_escaped);
+                       free(newpass_crypt);
+                       return -1;
+               }
+
+               mysql_real_escape_string(mysql, username_escaped,
+                                        user, strlen(user));
+
                login_field = read_env("MYSQL_LOGIN_FIELD");
                if (!login_field) login_field = "id";
                crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
                clear_field=read_env("MYSQL_CLEAR_PWFIELD");
                where_clause=read_env("MYSQL_WHERE_CLAUSE");
-               sql_buf=malloc(strlen(crypt_field ? crypt_field:"")
-                               + strlen(clear_field ? clear_field:"")
-                               + strlen(defdomain ? defdomain:"")
-                               + strlen(login_field) + l + strlen(newpass_crypt)
-                               + strlen(user_table)
-                               + strlen(where_clause ? where_clause:"")
-                               + 200);
-       }
-       else
-       {
-               sql_buf=parse_chpass_clause(chpass_clause,
-                                           user,
-                                           defdomain,
-                                           pass,
-                                           newpass_crypt);
-       }
-       
 
-       if (!sql_buf)
-       {
-               free(newpass_crypt);
-               return (-1);
-       }
+               if (!where_clause)
+                       where_clause="";
 
-       if (!chpass_clause) /*siefca@pld.org.pl */
-       {
-               sprintf(sql_buf, "UPDATE %s SET", user_table);
+               if (!crypt_field)
+                       crypt_field="";
 
-               comma="";
+               if (!clear_field)
+                       clear_field="";
 
-               if (clear_field && *clear_field)
-               {
-                       char *q;
+               if (!defdomain)
+                       defdomain="";
 
-                       strcat(strcat(strcat(sql_buf, " "), clear_field),
-                               "=\"");
+#define DEFAULT_SETPASS_UPDATE \
+               "UPDATE %s SET %s%s%s%s %s %s%s%s%s WHERE %s='%s%s%s' %s%s%s", \
+                       user_table,                                     \
+                       *clear_field ? clear_field:"",                  \
+                       *clear_field ? "='":"",                         \
+                       *clear_field ? clear_escaped:"",                \
+                       *clear_field ? "'":"",                          \
+                                                                       \
+                       *clear_field && *crypt_field ? ",":"",          \
+                                                                       \
+                       *crypt_field ? crypt_field:"",                  \
+                       *crypt_field ? "='":"",                         \
+                       *crypt_field ? crypt_escaped:"",                \
+                       *crypt_field ? "'":"",                          \
+                       login_field,                                    \
+                       username_escaped,                               \
+                       has_domain || !*defdomain ? "":"@",             \
+                       has_domain ? "":defdomain,                      \
+                       *where_clause ? " AND (":"", where_clause,      \
+                       *where_clause ? ")":""
 
-                       q=sql_buf+strlen(sql_buf);
-                       while (*pass)
-                       {
-                               if (*pass == '"' || *pass == '\\')
-                                       *q++= '\\';
-                               *q++ = *pass++;
-                       }
-                       strcpy(q, "\"");
-                       comma=", ";
-               }
-
-               if (crypt_field && *crypt_field)
-               {
-                       strcat(strcat(strcat(strcat(strcat(strcat(sql_buf, comma),
-                                                          " "),
-                                                   crypt_field),
-                                            "=\""),
-                               newpass_crypt),
-                            "\"");
-               }
-               free(newpass_crypt);
 
-               strcat(strcat(strcat(sql_buf, " WHERE "),
-                             login_field),
-                   "=\"");
+               sql_buf_size=snprintf(dummy_buf, 1, DEFAULT_SETPASS_UPDATE);
 
-               append_username(sql_buf+strlen(sql_buf), user, defdomain);
+               sql_buf=malloc(sql_buf_size+1);
 
-               strcat(sql_buf, "\"");
+               if (sql_buf)
+                       snprintf(sql_buf, sql_buf_size+1,
+                                DEFAULT_SETPASS_UPDATE);
 
-               if (where_clause && *where_clause)
-               {
-                       strcat(sql_buf, " AND (");
-                       strcat(sql_buf, where_clause);
-                       strcat(sql_buf, ")");
-               }
-               
+               free(username_escaped);
        }
        else
        {
-               free(newpass_crypt);
+               sql_buf=auth_parse_chpass_clause(escape_str,
+                                                chpass_clause,
+                                                user,
+                                                defdomain,
+                                                clear_escaped,
+                                                crypt_escaped);
        }
-
+       
+       free(clear_escaped);
+       free(crypt_escaped);
+       free(newpass_crypt);
 
        if (courier_authdebug_login_level >= 2)
        {
@@ -1069,14 +645,15 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name,
                           void *void_arg)
 {
        const char *defdomain, *select_clause;
-       char    *querybuf, *p;
+       char    *querybuf;
        MYSQL_ROW       row;
        MYSQL_RES       *result;
 
-       static const char query[]=
-               "SELECT %s, %s, %s, %s, %s, %s FROM %s WHERE 1=1";
-
-       if (do_connect())       return;
+       if (do_connect())
+       {
+               (*cb_func)(NULL, 0, 0, NULL, NULL, NULL, void_arg);
+               return;
+       }
 
        initui();
 
@@ -1095,6 +672,8 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name,
                                *maildir_field,
                                *options_field,
                                *where_clause;
+               char dummy_buf[1];
+               size_t query_len;
 
                user_table=read_env("MYSQL_USER_TABLE");
 
@@ -1126,14 +705,18 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name,
                where_clause=read_env("MYSQL_WHERE_CLAUSE");
                if (!where_clause) where_clause = "";
 
-               querybuf=malloc(sizeof(query) + 100
-                               + strlen(login_field)
-                               + strlen(uid_field) + strlen(gid_field)
-                               + strlen(home_field)
-                               + strlen(maildir_field)
-                               + strlen(options_field)
-                               + strlen(user_table)
-                               + strlen(where_clause));
+
+#define DEFAULT_ENUMERATE_QUERY \
+               "SELECT %s, %s, %s, %s, %s, %s FROM %s %s%s",\
+                       login_field, uid_field, gid_field,              \
+                       home_field, maildir_field,                      \
+                       options_field, user_table,                      \
+                       *where_clause ? " WHERE ":"",                   \
+                       where_clause
+
+               query_len=snprintf(dummy_buf, 1, DEFAULT_ENUMERATE_QUERY);
+
+               querybuf=malloc(query_len+1);
 
                if (!querybuf)
                {
@@ -1141,23 +724,14 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name,
                        return;
                }
 
-               sprintf(querybuf, query, login_field, 
-                       uid_field, gid_field, home_field, maildir_field,
-                       options_field, user_table);
-
-               p=querybuf+strlen(querybuf);
-       
-               if (strcmp(where_clause, "")) {
-                       strcat(p, " AND (");
-                       strcat(p, where_clause);
-                       strcat(p, ")");
-               }
+               snprintf(querybuf, query_len+1, DEFAULT_ENUMERATE_QUERY);
        }
        else
        {
                /* siefca@pld.org.pl */
-               querybuf=parse_select_clause (select_clause, "*",
-                                             defdomain, "enumerate");
+               querybuf=auth_parse_select_clause (escape_str,
+                                                  select_clause, "*",
+                                                  defdomain, "enumerate");
                if (!querybuf)
                {
                        DPRINTF("authmysql: parse_select_clause failed");
@@ -1202,9 +776,9 @@ void auth_mysql_enumerate( void(*cb_func)(const char *name,
                while ((row = mysql_fetch_row (result)) != NULL)
                {
                        if(!row[0] || !row[0][0]
-                       || !row[1] || !row[1][0]
-                       || !row[2] || !row[2][0]
-                       || !row[3] || !row[3][0])
+                          || !row[1] || !row[1][0]
+                          || !row[2] || !row[2][0]
+                          || !row[3] || !row[3][0])
                        {
                                continue;
                        }