diff -Nur courier-imap-1.3.12.orig/authlib/README.myownquery courier-imap-1.3.12/authlib/README.myownquery
--- courier-imap-1.3.12.orig/authlib/README.myownquery	Thu Jan  1 01:00:00 1970
+++ courier-imap-1.3.12/authlib/README.myownquery	Sun Dec 30 01:41:38 2001
@@ -0,0 +1,543 @@
+
+
+
+
+	    Developer Notes for courier-imap-myownquery.patch
+
+
+
+
+							document version: 1.03
+							author:	Pawel Wilk
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+0 What's that?
+
+1 Modifications overview
+
+2 Definitions
+
+3 New data types
+  3.1 struct var_data
+  3.2 typedef size_t (*parsefunc)
+  
+4 New functions
+  4.1 get_variable
+  4.2 parse_core
+  4.3 ParsePlugin_counter
+  4.4 ParsePlugin_builder
+  4.5 parse_string
+  4.6 validate_password
+  4.7 get_localpart
+  4.8 get_domain
+  4.9 parse_select_clause
+  4.10 parse_chpass_clause
+  
+5 Ideas and TODO
+
+6 Thanks
+
+
+
+
+		    *-----------------------
+		     0 What's that?
+		    *-----------------------
+
+Courier-imap-myownquery.patch allows administrator to set own MySQL queries
+used by authdaemon to authenticate user (including fetchig credentials) and to
+change user's password. It allows to construct SELECT or UPDATE clause in the
+configuration file (authmysqlrc) by adding two new configuration variables:
+MYSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE. It may be useful in the mail
+environments where there is such a need to have different database structure
+and/or tables scheme than expected by authmysql module.
+
+It also implements a small parsing engine for substitution variables which
+may appear in the clauses and are used to put informations like username
+or domain into the right place of a query.
+
+This patch was created using `diff -Nur` on courier-imap-1.3.12 source.
+
+
+
+
+
+		    *-----------------------
+		     1 Modifications overview
+		    *-----------------------
+
+Modified files:	authmysqllib.c authmysqlrc
+
+Each modified set of instructions is marked by my e-mail address:
+siefca@pld.org.pl
+
+Changes in the current source code are related to:
+
+- sections where the queries are constructed
+  (including memory allocation for the buffers)
+
+	when MYSQL_SELECT_CLAUSE or MYSQL_CHPASS_CLAUSE is
+	used then the query goes through the parsing functions
+	passing over current memory allocation and query construction
+	subroutines
+	  
+- section where the configuration file is read
+
+	i've had to modify read_env() function to allow line breaks
+	- now each sequence of the backslash as a first character and
+	newline as the second is replaced by two whitespaces while
+	putting into the buffer
+
+- sections where the query is constructed
+
+	selection is made, depending on configuration variables which
+	are set or not - if own query is used 
+
+
+
+
+
+		    *-----------------------
+		     2 Definitions
+		    *-----------------------
+
+#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)
+
+These definitions allows to change substitution marks in an easy way.
+SV_BEGIN_MARK refers to sequence of characters treated as a prefix of
+each substitution variable and SV_END_MARK refers to string which is
+a closing suffix. If the expected substitution variable is called
+'local_part' (without apostrophes) then '$(local_part)' is a valid
+string representation for SV_BEGIN_MARK set to "$(" and SV_END_MARK to ")".
+MAX_SUBSTITUTION_LEN defines maximal length of a substitution variable's
+identifier (name).
+
+The last two definitions are just for code simplification.
+
+
+
+
+
+
+		    *-----------------------
+		     3 New data types
+		    *-----------------------
+
+This section describes new data type definitions and variables.
+
+3.1 struct var_data
+
+struct var_data {			
+	const char *name;
+	const char *value;
+	const size_t size;
+	size_t value_length;
+	} ;
+
+This structure holds information needed by parsing routines.
+Using var_data array you may specify a set of string substitutions
+which should be done while parsing a query. Last element in array
+should have all fields set to zero (null). 
+
+name field	- should contain substituted variable name
+value 		- should contain string which replaces it
+size		- should contain string size including the last zero byte ('\0')
+value_length	- should be set to zero - it is used as a value size cache
+
+
+explanation: size is used to increase speed of calculation proccess
+	     value_length is used to cache length of a value during the
+	     parsing subroutines - it helps when substitution variable
+	     occures more than once within the query
+ 
+Example:
+
+struct var_data vdt[] =	{
+    {"some",	"replacement",	sizeof("some"),		0},
+    {"anotha",	NULL,		sizeof("anotha"),	0},
+    {NULL,	NULL,		0,			0}
+};
+
+In this example we've declared that $(some) in the query should be
+replaced by 'replacement' text, and replacement for $(anotha) will
+be defined in the code before passing on the array pointer to
+the paring function.
+
+
+3.2 typedef size_t (*parsefunc)
+
+typedef int (*parsefunc)(const char *, size_t, void *);
+
+This type definition refers to the function pointer, which is used
+to pass plugin functions into the core parsing subroutine. This definition
+is included to simplify the declaration of the parse_core() function.
+
+
+
+
+
+		    *-----------------------
+		     4 New functions
+		    *-----------------------
+
+This section describes added functions.
+
+4.1 get_variable
+
+NAME
+
+	get_variable
+
+SYNOPSIS
+
+	static const struct var_data *get_variable (const char *begin,
+						    size_t len,
+  		  	        	  	    struct var_data *vdt);
+
+DESCRIPTION
+
+	This function searches an array pointed by vdt and tries to find
+	the substitution variable, which name is identified with begin
+	pointer and length of len bytes long.
+	
+	This function is also responsible for updating length cache field
+	of vdt elements and validating requested variables.
+	
+	This function repports errors by sending human readable
+	messages to the standard error stream.
+
+RETURN VALUE
+
+	This function returns a pointer to the array element which is
+	structure of var_data type, which contains variable definition
+	of a given name. It returns NULL on error or failure.
+
+
+4.2 parse_core
+
+NAME
+
+	parse_core
+
+SYNOPSIS
+	static int    parse_core (const char *source, struct var_data *vdt,
+				  parsefunc outfn, void *result);
+
+DESCRIPTION
+
+	This is the parsing routine for query strings containing the
+	substitution variables. It reads the string pointed with source
+	and tries to catch a valid substitution variables or parts which
+	are plain text blocks. The main purpose of using this function
+	it to split source string into parts and for each part call
+	outfn() function. Those parts are substrings identified by
+	pointer to some element of the source string and size.
+	Those elements are the result of splitting source string into
+	logical parts: plain text substrings and substitution variables'
+	values. To get the values of any found substitution variables
+	parse_core() uses get_variable() function. To find places
+	where substitution variables occurs it uses strstr() function
+	in conjunction with SV_BEGIN_MARK and SV_END_MARK definitions.
+	It passes vdt structure pointer to get_variable() function if
+	it calls it.
+
+	outfn() function should be passed by its pointer which
+	refers to declaration:
+	
+ 	int (*outfn)    (const char *begin,
+			 size_t string_length,
+			 void *void_pointer);
+	
+	Each time outfn() is called the result argument of parse_core()
+	is passed to the outfn() as a last argument (void_pointer).
+	
+	Example:
+	
+	    Example string "$(local_part) AND $(domain)" will cause the
+	    outfn() to be called 3 times. First time for a value of
+	    $(local_part) substitution variable, second time
+	    for " AND " string, and the last time for $(domain) variable's
+	    value. Variables are passed to outfn() by their (found) values,
+	    plain text blocks are passed as they appear.
+
+	This function repports errors by sending human readable
+	messages to the standard error stream.
+
+RETURN VALUE
+
+	This function returns -1 if an error has occured and 0 if
+	everything went good.
+	
+4.3 ParsePlugin_counter
+
+NAME
+
+	ParsePlugin_counter
+
+SYNOPSIS
+
+	int    ParsePlugin_counter (const char *begin, size_t len,
+			            void *vp);
+
+DESCRIPTION
+
+	This is parsing plugin function. It simply increments the value
+	found in the memory area pointed by vp. It assumes that
+	the memory area is allocated for the variable of size_t
+	type and that area was passed by (size_t *) pointer.
+	The value is incremented by len argument. Begin argument
+	is not used.
+
+	This function repports errors by sending human readable
+	messages to the standard error stream.
+
+RETURN VALUE
+
+	This function returns the variable size or -1 if an error
+	has occured, 0 if everything went good.
+
+4.4 ParsePlugin_builder
+
+NAME
+
+	ParsePlugin_builder
+
+SYNOPSIS
+
+	int    ParsePlugin_builder (const char *begin, size_t len,
+			            void *vp);
+
+DESCRIPTION
+
+	This is parsing plugin function. It simply copies len bytes
+	of a string pointed by begin to the end of memory area pointed by
+	vp. It assumes that the area pointed by vp is passed by (char **)
+	type pointer and refers to the (char *) pointer variable.
+	After each call it shifts the value of pointer variable (char *)
+	incrementing it by len bytes. Be careful when using this function
+	- its changes the given pointer value. Always operate on an
+	additional pointer type variable when passing it as the third
+	argument.
+
+RETURN VALUE
+
+	This function returns the variable size or -1 if an error
+	has occured, 0 if everything went good.
+
+4.5 parse_string
+
+NAME
+	parse_string
+
+SYNOPSIS
+
+	static char *parse_string (const char *source, struct var_data *vdt);
+
+DESCRIPTION
+
+	This function parses the string pointed with source according to the
+	replacement instructions set in var_data array, which is passed with
+	its pointer vdt. It produces changed string located in newly allocated
+	memory area.
+
+	This function calls parse_core() function with various parsing
+	subroutines passed as function pointers.
+	
+	1. It uses parse_core() with ParsePlugin_counter to obtain the
+	   total amount	of memory needed for the output string.
+	
+	2. It allocates the memory.
+	
+	3. It uses parse_core() with ParsePlugin_builder to build the
+	   output string.
+
+	This function repports errors by sending human readable
+	messages to the standard error stream.
+
+RETURN VALUE
+			   
+	Function returns pointer to the result buffer or NULL
+	if an error has occured.
+
+WARNINGS
+
+	This function allocates some amount of memory using standard
+	ANSI C routines. Memory allocated by this function should be
+	freed with free().
+
+
+4.6 validate_password
+
+NAME
+	validate_password
+
+SYNOPSIS
+
+	static const char *validate_password (const char *password);
+
+DESCRIPTION
+
+	This function checks whether password string does contain
+	any dangerous characters, which may be used to pass command
+	strings to the database connection stream. If it founds one
+	it replaces it by the backslash character.
+
+RETURN VALUE
+
+	It returns a pointer to the static buffer which contains
+	validated password string or NULL if an error has occured.
+
+
+4.7 get_localpart
+
+NAME
+	
+	get_localpart
+
+SYNOPSIS
+
+	static const char *get_localpart (const char *username);
+
+DESCRIPTION
+
+	This function detaches local part of an e-mail address
+	from string pointed with username and puts it to the
+	buffer of the fixed length. All necessary cleaning is
+	made on the result string.
+
+RETURN VALUE
+
+	Pointer to the static buffer containing local part or
+	NULL if there was some error.
+
+
+4.8 get_domain
+
+NAME
+
+	get_domain
+
+SYNOPSIS
+
+	static const char *get_domain (const char *username, 
+				       const char *defdomain);
+
+DESCRIPTION
+
+        This function detaches domain part of an e-mail address
+	from string pointed with username and puts it to the
+	buffer of the fixed length. All necessary cleaning is
+	made on the result string. If function cannot find domain
+	part in the string the string pointed by defdomain is
+	used instead.
+
+RETURN VALUE
+
+        Pointer to the static buffer containing domain name or
+	NULL if there was some error.
+
+
+4.9 parse_select_clause
+
+NAME
+
+	parse_select_clause
+
+SYNOPSIS
+
+	static char *parse_select_clause (const char *clause,
+					  const char *username,
+                                  	  const char *defdomain);
+
+DESCRIPTION
+
+	This function is a simple wrapper to the parse_string()
+	function. It parses a query pointed by caluse. username
+	and defdomain strings are used to replace corresponding
+	substitution strings if present in the query: $(local_part)
+	and $(domain).
+	
+
+RETURN VALUE
+
+	Same as parse_string().
+
+
+4.10 parse_chpass_clause
+
+NAME
+
+        parse_chpass_clause
+
+SYNOPSIS
+
+        static char *parse_chpass_clause (const char *clause,
+                                          const char *username,
+                                          const char *defdomain,
+					  const char *newpass,
+					  const char *newpass_crypt);
+								    
+DESCRIPTION
+
+	This function is a simple wrapper to the parse_string()
+	function. It parses a query pointed by caluse. username,
+	defdomain, newpass and newpass_crypt strings are used to
+	replace corresponding substitution strings if present in
+	the query: $(local_part), $(domain), $(newpass),
+	$(newpass_crypt).
+
+RETURN VALUE
+
+	Same as parse_string().
+
+
+
+
+
+		    *------------------------
+		     5 Ideas and TODO
+		    *------------------------
+
+- solve problem with fixed buffer length of local part and the domain part
+  strings after split						(problem?)
+- allow admin to set a group name instead of numerical group id
+- allow admin to set a username instead of numerical user id
+
+- add clauses:
+
+  - MYSQL_PRESELECT_CLAUSE (query which comes before MYSQL_SELECT_CLAUSE)
+  - MYSQL_POSTSELECT_CLAUSE (query which comes after MYSQL_SELECT_CLAUSE)
+
+
+
+
+
+		    *------------------------
+		     6 Thanks
+		    *------------------------
+
+At the beginning this patch was messy indeed. :> I would like to thank
+Sam Varshavchik for pointing me a lot how to make it more fast and solid.
+I would also thank Philip Hazel, Chris Lightfoot and Mike Bremford which
+by their software capabilities inspired me to write it.
+
+---------------------------------------------------------------------------
+
diff -Nur courier-imap-1.3.12.orig/authlib/authmysqllib.c courier-imap-1.3.12/authlib/authmysqllib.c
--- courier-imap-1.3.12.orig/authlib/authmysqllib.c	Mon Aug  6 05:12:39 2001
+++ courier-imap-1.3.12/authlib/authmysqllib.c	Sun Dec 30 01:41:00 2001
@@ -3,7 +3,6 @@
 ** distribution information.
 */
 
-
 #include	<stdio.h>
 #include	<stdlib.h>
 #include	<string.h>
@@ -18,8 +17,26 @@
 #include	"authmysqlrc.h"
 #include	"auth.h"
 
+/* 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.14 2001/08/01 22:23:01 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;
@@ -51,7 +68,13 @@
 
 		for (i=0; i<mysqlauth_size; i++)
 			if (mysqlauth[i] == '\n')
-				mysqlauth[i]=0;
+			{	/* siefca@pld.org.pl */
+				if (!i || mysqlauth[i-1] != '\\')
+					mysqlauth[i]	='\0';
+				else
+					mysqlauth[i]	=
+					mysqlauth[i-1]	= ' ';
+			}
 		fclose(f);
 	}
 
@@ -199,17 +222,370 @@
 		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 */
+	{
+		fprintf (stderr, "authmysql: critical error while "
+				 "parsing substitution variable\n");
+		return NULL;
+	}
+	if (len < 1)
+	{
+		fprintf (stderr, "authmysql: unknown empty substitution "
+				 "variable - aborting\n");
+		return NULL;
+	}
+	if (len > MAX_SUBSTITUTION_LEN)
+	{
+		fprintf (stderr, "authmysql: variable name too long "
+				 "while parsing substitution\n"
+				 "authmysql: name begins with "
+				 SV_BEGIN_MARK
+				 "%.*s...\n", 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;
+		}
+	
+	fprintf (stderr, "authmysql: unknown substitution variable "
+			 SV_BEGIN_MARK
+			 "%.*s"
+			 SV_END_MARK
+			 "\n", 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)
+	{
+		fprintf (stderr, "authmysql: bad arguments while counting "
+				 "query string\n");
+		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)
+	{
+		fprintf (stderr, "authmysql: bad arguments while building "
+				 "query string\n");
+		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)
+	{
+		fprintf (stderr, "authmysql: no memory allocated for result"
+				 "while parser core was invoked\n");
+		return -1;
+	}
+	if (!vdt)
+	{
+		fprintf (stderr, "authmysql: no substitution table found "
+				 "while parser core was invoked\n");
+		return -1;
+	}
+	
+	q = source;
+	while ( (p=strstr(q, SV_BEGIN_MARK)) )
+	{
+		e = strstr (p, SV_END_MARK);
+		if (!e)
+		{
+			fprintf (stderr, "authmysql: syntax error in "
+					 "substitution "
+					 "- no closing symbol fould!\n"
+					 "authmysql: bad variable begins with:"
+					 "%.*s...\n", 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)
+	{
+		fprintf (stderr, "authmysql: source clause is empty "
+				 "- this is critical error\n");
+		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 *validate_password (const char *password)
+{
+static char	pass_buf[260];
+const char	*p;
+char		*q;
+	
+	if (!password || *password == '\0' || (strlen(password)) > 256)
+		return NULL;
+	
+	p = password;
+	q = pass_buf;
+	
+	while (*p)
+		if (*p == '\"' || *p == '\\' || *p == '\'')
+			*q++ = '\\';
+		else
+			*q++ = *p++;	
+	
+	*q = '\0';
+	return pass_buf;
+}
+
+
+/* siefca@pld.org.pl */
+static char *parse_select_clause (const char *clause, const char *username,
+				  const char *defdomain)
+{
+static struct var_data vd[]={
+	    {"local_part",	NULL,	sizeof("local_part"),	0},
+	    {"domain",		NULL,	sizeof("domain"),	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;
+	
+	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	= validate_password (newpass);
+	vd[3].value	= validate_password (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 *user_table;
-const char *defdomain;
+const char *user_table	=NULL;
+const char *defdomain	=NULL;
 char	*querybuf, *p;
 MYSQL_ROW	row;
 MYSQL_RES	*result;
 
-const char *crypt_field, *clear_field, *maildir_field, *home_field,
-	*name_field,
-	*login_field, *uid_field, *gid_field, *quota_field, *where_clause;
+const char  *crypt_field	=NULL, 
+	    *clear_field	=NULL,
+	    *maildir_field	=NULL,
+	    *home_field		=NULL,
+	    *name_field		=NULL,
+	    *login_field	=NULL,
+	    *uid_field		=NULL,
+	    *gid_field		=NULL,
+	    *quota_field	=NULL,
+	    *where_clause	=NULL,
+	    *select_clause	=NULL; /* siefca@pld.org.pl */
 
 static const char query[]=
 	"SELECT %s, %s, %s, %s, %s, %s, %s, %s, %s FROM %s WHERE %s = \"";
@@ -232,79 +608,95 @@
 		free(ui.fullname);
 	memset(&ui, 0, sizeof(ui));
 
-	user_table=read_env("MYSQL_USER_TABLE");
-	defdomain=read_env("DEFAULT_DOMAIN");
-
-	if (!user_table)
+	select_clause=read_env("MYSQL_SELECT_CLAUSE");
+	defdomain=read_env("DEFAULT_DOMAIN");	
+	
+	if (!select_clause) /* siefca@pld.org.pl */
 	{
-		fprintf(stderr, "authmysql: MYSQL_USER_TABLE not set in "
-			AUTHMYSQLRC ".\n");
-		return (0);
-	}
+		user_table=read_env("MYSQL_USER_TABLE");
 
-	crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
-	clear_field=read_env("MYSQL_CLEAR_PWFIELD");
-	name_field=read_env("MYSQL_NAME_FIELD");
+		if (!user_table)
+		{
+			fprintf(stderr, "authmysql: MYSQL_USER_TABLE not set in "
+				AUTHMYSQLRC ".\n");
+			return (0);
+		}
 
-	if (!crypt_field && !clear_field)
-	{
-		fprintf(stderr,
-			"authmysql: MYSQL_CRYPT_PWFIELD and "
-			"MYSQL_CLEAR_PWFIELD not set in " AUTHMYSQLRC ".\n");
-		return (0);
-	}
-	if (!crypt_field) crypt_field="\"\"";
-	if (!clear_field) clear_field="\"\"";
-	if (!name_field) name_field="\"\"";
+		crypt_field=read_env("MYSQL_CRYPT_PWFIELD");
+		clear_field=read_env("MYSQL_CLEAR_PWFIELD");
+		name_field=read_env("MYSQL_NAME_FIELD");
+
+		if (!crypt_field && !clear_field)
+		{
+			fprintf(stderr,
+				"authmysql: MYSQL_CRYPT_PWFIELD and "
+				"MYSQL_CLEAR_PWFIELD not set in " AUTHMYSQLRC ".\n");
+			return (0);
+		}
+		if (!crypt_field) crypt_field="\"\"";
+		if (!clear_field) clear_field="\"\"";
+		if (!name_field) name_field="\"\"";
 
-	uid_field = read_env("MYSQL_UID_FIELD");
-	if (!uid_field) uid_field = "uid";
+		uid_field = read_env("MYSQL_UID_FIELD");
+		if (!uid_field) uid_field = "uid";
 
-	gid_field = read_env("MYSQL_GID_FIELD");
-	if (!gid_field) gid_field = "gid";
+		gid_field = read_env("MYSQL_GID_FIELD");
+		if (!gid_field) gid_field = "gid";
 
-	login_field = read_env("MYSQL_LOGIN_FIELD");
-	if (!login_field) login_field = "id";
+		login_field = read_env("MYSQL_LOGIN_FIELD");
+		if (!login_field) login_field = "id";
 
-	home_field = read_env("MYSQL_HOME_FIELD");
-	if (!home_field) home_field = "home";
+		home_field = read_env("MYSQL_HOME_FIELD");
+		if (!home_field) home_field = "home";
 
-	maildir_field=read_env("MYSQL_MAILDIR_FIELD");
-	if (!maildir_field) maildir_field="\"\"";
+		maildir_field=read_env("MYSQL_MAILDIR_FIELD");
+		if (!maildir_field) maildir_field="\"\"";
 
-	quota_field=read_env("MYSQL_QUOTA_FIELD");
-	if (!quota_field) quota_field="\"\""; 
+		quota_field=read_env("MYSQL_QUOTA_FIELD");
+		if (!quota_field) quota_field="\"\""; 
 
-	where_clause=read_env("MYSQL_WHERE_CLAUSE");
-	if (!where_clause) where_clause = "";
+		where_clause=read_env("MYSQL_WHERE_CLAUSE");
+		if (!where_clause) where_clause = "";
+	}
 	
 	if (!defdomain)	defdomain="";
 
-	querybuf=malloc(sizeof(query) + 100 + strlen(user_table) + strlen(defdomain)
-		+ strlen(crypt_field) + strlen(clear_field) + strlen(maildir_field)
-		+ strlen(uid_field) + strlen(gid_field) + 2 * strlen(login_field)
-		+ strlen(home_field) + strlen(quota_field) + strlen(where_clause)
-			+ strlen(name_field));
-	if (!querybuf)
+	if (!select_clause) /* siefca@pld.org.pl */
 	{
-		perror("malloc");
-		return (0);
-	}
+		querybuf=malloc(sizeof(query) + 100 + strlen(user_table) + strlen(defdomain)
+			+ strlen(crypt_field) + strlen(clear_field) + strlen(maildir_field)
+			+ strlen(uid_field) + strlen(gid_field) + 2 * strlen(login_field)
+			+ strlen(home_field) + strlen(quota_field) + strlen(where_clause)
+			+ strlen(name_field));
+
+		if (!querybuf)
+		{
+			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, user_table, login_field);
 
-	sprintf(querybuf, query, login_field, crypt_field, clear_field, 
-		uid_field, gid_field, home_field, maildir_field, quota_field,
-		name_field, user_table, login_field);
-	p=querybuf+strlen(querybuf);
+		p=querybuf+strlen(querybuf);
 
-	append_username(p, username, defdomain);
-	strcat(p, "\"");
+		append_username(p, username, defdomain);
+		strcat(p, "\"");
 	
-	if (strcmp(where_clause, "")) {
-		strcat(p, " AND (");
-		strcat(p, where_clause);
-		strcat(p, ")");
+		if (strcmp(where_clause, "")) {
+			strcat(p, " AND (");
+			strcat(p, where_clause);
+			strcat(p, ")");
+		}
 	}
-	
+	else
+	{
+		/* siefca@pld.org.pl */
+		querybuf=parse_select_clause (select_clause, username, defdomain);
+		if (!querybuf) return 0;
+	}
+
 	if (mysql_query (mysql, querybuf))
 	{
 		/* <o.blasnik@nextra.de> */
@@ -379,12 +771,13 @@
 	const char *comma;
 	int rc=0;
 
-	const char *clear_field;
-	const char *crypt_field;
-	const char *defdomain;
-	const char *where_clause;
-	const char *user_table;
-	const char *login_field;
+	const char  *clear_field	=NULL,
+		    *crypt_field	=NULL,
+		    *defdomain		=NULL,
+		    *where_clause	=NULL,
+		    *user_table		=NULL,
+		    *login_field	=NULL,
+		    *chpass_clause	=NULL; /* siefca@pld.org.pl */
 
 	if (!mysql)
 		return (-1);
@@ -412,21 +805,34 @@
 		++l;
 	}
 
-	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");
+	/* siefca@pld.org.pl */
+	chpass_clause=read_env("MYSQL_CHPASS_CLAUSE");
 	defdomain=read_env("DEFAULT_DOMAIN");
-	where_clause=read_env("MYSQL_WHERE_CLAUSE");
 	user_table=read_env("MYSQL_USER_TABLE");
-
-	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);
+	if (!chpass_clause)
+	{
+		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)
 	{
@@ -434,53 +840,57 @@
 		return (-1);
 	}
 
-	sprintf(sql_buf, "UPDATE %s SET", user_table);
-
-	comma="";
-
-	if (clear_field && *clear_field)
+	if (!chpass_clause) /*siefca@pld.org.pl */
 	{
-		char *q;
+		sprintf(sql_buf, "UPDATE %s SET", user_table);
 
-		strcat(strcat(strcat(sql_buf, " "), clear_field),
-		       "=\"");
+		comma="";
 
-		q=sql_buf+strlen(sql_buf);
-		while (*pass)
+		if (clear_field && *clear_field)
 		{
-			if (*pass == '"' || *pass == '\\')
-				*q++= '\\';
-			*q++ = *pass++;
+			char *q;
+
+			strcat(strcat(strcat(sql_buf, " "), clear_field),
+		    		"=\"");
+
+			q=sql_buf+strlen(sql_buf);
+			while (*pass)
+			{
+				if (*pass == '"' || *pass == '\\')
+					*q++= '\\';
+				*q++ = *pass++;
+			}
+			strcpy(q, "\"");
+			comma=", ";
 		}
-		strcpy(q, "\"");
-		comma=", ";
-	}
 
-	if (crypt_field && *crypt_field)
-	{
-		strcat(strcat(strcat(strcat(strcat(strcat(sql_buf, comma),
-						   " "),
-					    crypt_field),
-				     "=\""),
-			      newpass_crypt_ptr),
-		       "\"");
-	}
-	free(newpass_crypt);
+		if (crypt_field && *crypt_field)
+		{
+			strcat(strcat(strcat(strcat(strcat(strcat(sql_buf, comma),
+							   " "),
+						    crypt_field),
+					     "=\""),
+			    	newpass_crypt_ptr),
+		    	     "\"");
+		}
+		free(newpass_crypt);
 
-	strcat(strcat(strcat(sql_buf, " WHERE "),
-		      login_field),
-	       "=\"");
+		strcat(strcat(strcat(sql_buf, " WHERE "),
+			      login_field),
+	    	    "=\"");
 
-	append_username(sql_buf+strlen(sql_buf), user, defdomain);
+		append_username(sql_buf+strlen(sql_buf), user, defdomain);
 
-	strcat(sql_buf, "\"");
+		strcat(sql_buf, "\"");
 
-	if (where_clause && *where_clause)
-	{
-		strcat(sql_buf, " AND (");
-		strcat(sql_buf, where_clause);
-		strcat(sql_buf, ")");
-	}
+		if (where_clause && *where_clause)
+		{
+			strcat(sql_buf, " AND (");
+			strcat(sql_buf, where_clause);
+			strcat(sql_buf, ")");
+		}
+		
+	} /* end of: if (!chpass_clause) */
 
 	if (mysql_query (mysql, sql_buf))
 	{
diff -Nur courier-imap-1.3.12.orig/authlib/authmysqlrc courier-imap-1.3.12/authlib/authmysqlrc
--- courier-imap-1.3.12.orig/authlib/authmysqlrc	Sun Oct  7 18:32:56 2001
+++ courier-imap-1.3.12/authlib/authmysqlrc	Fri Dec 28 02:08:01 2001
@@ -141,4 +141,65 @@
 #
 # MYSQL_WHERE_CLAUSE	server='mailhost.example.com'
 
-
+##NAME: MYSQL_SELECT_CLAUSE:0
+#
+# (EXPERIMENTAL)
+# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database,
+# which is structuraly different from proposed. The fixed string will
+# be used to do a SELECT operation on database, which should return fields
+# in order specified bellow:
+#
+# username, cryptpw, clearpw, uid, gid, home, maildir, quota, fullname
+#
+# Enabling this option causes ignorance of any other field-related
+# options, excluding default domain.
+#
+# There are two variables, which you can use. Substitution will be made
+# for them, so you can put entered username (local part) and domain name
+# in the right place of your query. These variables are:
+#	 	$(local_part) and $(domain)
+#
+# If a $(domain) is empty (not given by the remote user) the default domain
+# name is used in its place.
+#
+# This example is a little bit modified adaptation of vmail-sql
+# database scheme:
+#
+# MYSQL_SELECT_CLAUSE	SELECT popbox.local_part,			\
+#			CONCAT('{MD5}', popbox.password_hash),		\
+#			popbox.clearpw,					\
+#			domain.uid,					\
+#			domain.gid,					\
+#			CONCAT(domain.path, '/', popbox.mbox_name),	\
+#			'',						\
+#			domain.quota,					\
+#			'',						\
+#			FROM popbox, domain				\
+#			WHERE popbox.local_part = '$(local_part)'	\
+#			AND popbox.domain_name = '$(domain)'		\
+#			AND popbox.domain_name = domain.domain_name
+#
+##NAME: MYSQL_CHPASS_CLAUSE:0
+#
+# (EXPERIMENTAL)
+# This is optional, MYSQL_CHPASS_CLAUSE can be set when you have a database,
+# which is structuraly different from proposed. The fixed string will
+# be used to do an UPDATE operation on database. In other words, it is
+# used, when changing password.
+#
+# There are four variables, which you can use. Substitution will be made
+# for them, so you can put entered username (local part) and domain name
+# in the right place of your query. There variables are:
+# 	$(local_part) , $(domain) , $(newpass) , $(newpass_crypt)
+#
+# If a $(domain) is empty (not given by the remote user) the default domain
+# name is used in its place.
+# $(newpass) contains plain password
+# $(newpass_crypt) contains its crypted form
+#
+# MYSQL_CHPASS_CLAUSE	UPDATE	popbox					\
+#			SET	clearpw='$(newpass)',			\
+#				password_hash='$(newpass_crypt)'	\
+#			WHERE	local_part='$(local_part)'		\
+#			AND	domain_name='$(domain)'
+#
