5 Developer Notes for courier-imap-myownquery.patch
10 document version: 1.03
30 1 Modifications overview
36 3.2 typedef size_t (*parsefunc)
41 4.3 ParsePlugin_counter
42 4.4 ParsePlugin_builder
47 4.9 parse_select_clause
48 4.10 parse_chpass_clause
57 *-----------------------
59 *-----------------------
61 Courier-imap-myownquery.patch allows administrator to set own SQL queries
62 used by authdaemon to authenticate user (including fetchig credentials) and to
63 change user's password. It allows to construct SELECT or UPDATE clause in the
64 configuration file (authmysqlrc or authpgsqlrc) by adding two new configuration
65 variables: MYSQL_SELECT_CLAUSE or PGSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE
66 or PGSQL_CHPASS_CLAUSE. It may be useful in the mail environments where there
67 is such a need to have different database structure and/or tables scheme than
68 expected by authmysql or authpgsql module.
70 It also implements a small parsing engine for substitution variables which
71 may appear in the clauses and are used to put informations like username
72 or domain into the right place of a query.
74 This patch was created using `diff -Nur` on courier-imap-1.3.12 source.
80 *-----------------------
81 1 Modifications overview
82 *-----------------------
84 Modified files: authmysqllib.c authmysqlrc authpgsqllib.c authpgsqlrc
86 Each modified set of instructions is marked by my e-mail address:
87 siefca@pld.org.pl (for MySQL files) or tom@minnesota.com (for PostgreSQL files)
89 Changes in the current source code are related to:
91 - sections where the queries are constructed
92 (including memory allocation for the buffers)
94 when MYSQL_SELECT_CLAUSE or MYSQL_CHPASS_CLAUSE or
95 PGSQL_SELECT_CLAUSE or PGSQL_CHPASS_CLAUSE is
96 used then the query goes through the parsing functions
97 passing over current memory allocation and query construction
100 - section where the configuration file is read
102 i've had to modify read_env() function to allow line breaks
103 - now each sequence of the backslash as a first character and
104 newline as the second is replaced by two whitespaces while
105 putting into the buffer
107 - sections where the query is constructed
109 selection is made, depending on configuration variables which
110 are set or not - if own query is used
116 *-----------------------
118 *-----------------------
120 #define MAX_SUBSTITUTION_LEN 32
121 #define SV_BEGIN_MARK "$("
122 #define SV_END_MARK ")"
123 #define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1)
124 #define SV_END_LEN ((sizeof(SV_END_MARK))-1)
126 These definitions allows to change substitution marks in an easy way.
127 SV_BEGIN_MARK refers to sequence of characters treated as a prefix of
128 each substitution variable and SV_END_MARK refers to string which is
129 a closing suffix. If the expected substitution variable is called
130 'local_part' (without apostrophes) then '$(local_part)' is a valid
131 string representation for SV_BEGIN_MARK set to "$(" and SV_END_MARK to ")".
132 MAX_SUBSTITUTION_LEN defines maximal length of a substitution variable's
135 The last two definitions are just for code simplification.
142 *-----------------------
144 *-----------------------
146 This section describes new data type definitions and variables.
157 This structure holds information needed by parsing routines.
158 Using var_data array you may specify a set of string substitutions
159 which should be done while parsing a query. Last element in array
160 should have all fields set to zero (null).
162 name field - should contain substituted variable name
163 value - should contain string which replaces it
164 size - should contain string size including the last zero byte ('\0')
165 value_length - should be set to zero - it is used as a value size cache
168 explanation: size is used to increase speed of calculation proccess
169 value_length is used to cache length of a value during the
170 parsing subroutines - it helps when substitution variable
171 occures more than once within the query
175 struct var_data vdt[] = {
176 {"some", "replacement", sizeof("some"), 0},
177 {"anotha", NULL, sizeof("anotha"), 0},
181 In this example we've declared that $(some) in the query should be
182 replaced by 'replacement' text, and replacement for $(anotha) will
183 be defined in the code before passing on the array pointer to
187 3.2 typedef size_t (*parsefunc)
189 typedef int (*parsefunc)(const char *, size_t, void *);
191 This type definition refers to the function pointer, which is used
192 to pass plugin functions into the core parsing subroutine. This definition
193 is included to simplify the declaration of the parse_core() function.
199 *-----------------------
201 *-----------------------
203 This section describes added functions.
213 static const struct var_data *get_variable (const char *begin,
215 struct var_data *vdt);
219 This function searches an array pointed by vdt and tries to find
220 the substitution variable, which name is identified with begin
221 pointer and length of len bytes long.
223 This function is also responsible for updating length cache field
224 of vdt elements and validating requested variables.
226 This function repports errors by sending human readable
227 messages to the standard error stream.
231 This function returns a pointer to the array element which is
232 structure of var_data type, which contains variable definition
233 of a given name. It returns NULL on error or failure.
243 static int parse_core (const char *source, struct var_data *vdt,
244 parsefunc outfn, void *result);
248 This is the parsing routine for query strings containing the
249 substitution variables. It reads the string pointed with source
250 and tries to catch a valid substitution variables or parts which
251 are plain text blocks. The main purpose of using this function
252 it to split source string into parts and for each part call
253 outfn() function. Those parts are substrings identified by
254 pointer to some element of the source string and size.
255 Those elements are the result of splitting source string into
256 logical parts: plain text substrings and substitution variables'
257 values. To get the values of any found substitution variables
258 parse_core() uses get_variable() function. To find places
259 where substitution variables occurs it uses strstr() function
260 in conjunction with SV_BEGIN_MARK and SV_END_MARK definitions.
261 It passes vdt structure pointer to get_variable() function is
264 outfn() function should be passed by its pointer which
265 refers to declaration:
267 int (*outfn) (const char *begin,
268 size_t string_length,
271 Each time outfn() is called the result argument of parse_core()
272 is passed to the outfn() as a last argument (void_pointer).
276 Example string "$(local_part) AND $(domain)" will cause the
277 outfn() to be called 3 times. First time for a value of
278 $(local_part) substitution variable, second time
279 for " AND " string, and the last time for $(domain) variable's
280 value. Variables are passed to outfn() by their (found) values,
281 plain text blocks are passed as they appear.
283 This function repports errors by sending human readable
284 messages to the standard error stream.
288 This function returns -1 if an error has occured and 0 if
289 everything went good.
291 4.3 ParsePlugin_counter
299 int ParsePlugin_counter (const char *begin, size_t len,
304 This is parsing plugin function. It simply increments the value
305 found in the memory area pointed by vp. It assumes that
306 the memory area is allocated for the variable of size_t
307 type and that area was passed by (size_t *) pointer.
308 The value is incremented by len argument. Begin argument
311 This function repports errors by sending human readable
312 messages to the standard error stream.
316 This function returns the variable size or -1 if an error
317 has occured, 0 if everything went good.
319 4.4 ParsePlugin_builder
327 int ParsePlugin_builder (const char *begin, size_t len,
332 This is parsing plugin function. It simply copies len bytes
333 of a string pointed by begin to the end of memory area pointed by
334 vp. It assumes that the area pointed by vp is passed by (char **)
335 type pointer and refers to the (char *) pointer variable.
336 After each call it shifts the value of pointer variable (char *)
337 incrementing it by len bytes. Be careful when using this function
338 - its changes the given pointer value. Always operate on an
339 additional pointer type variable when passing it as the third
344 This function returns the variable size or -1 if an error
345 has occured, 0 if everything went good.
354 static char *parse_string (const char *source, struct var_data *vdt);
358 This function parses the string pointed with source according to the
359 replacement instructions set in var_data array, which is passed with
360 its pointer vdt. It produces changed string located in newly allocated
363 This function calls parse_core() function with various parsing
364 subroutines passed as function pointers.
366 1. It uses parse_core() with ParsePlugin_counter to obtain the
367 total amount of memory needed for the output string.
369 2. It allocates the memory.
371 3. It uses parse_core() with ParsePlugin_builder to build the
374 This function repports errors by sending human readable
375 messages to the standard error stream.
379 Function returns pointer to the result buffer or NULL
380 if an error has occured.
384 This function allocates some amount of memory using standard
385 ANSI C routines. Memory allocated by this function should be
389 4.6 validate_password
396 static const char *validate_password (const char *password);
400 This function checks whether password string does contain
401 any dangerous characters, which may be used to pass command
402 strings to the database connection stream. If it founds one
403 it replaces it by the backslash character.
407 It returns a pointer to the static buffer which contains
408 validated password string or NULL if an error has occured.
419 static const char *get_localpart (const char *username);
423 This function detaches local part of an e-mail address
424 from string pointed with username and puts it to the
425 buffer of the fixed length. All necessary cleaning is
426 made on the result string.
430 Pointer to the static buffer containing local part or
431 NULL if there was some error.
442 static const char *get_domain (const char *username,
443 const char *defdomain);
447 This function detaches domain part of an e-mail address
448 from string pointed with username and puts it to the
449 buffer of the fixed length. All necessary cleaning is
450 made on the result string. If function cannot find domain
451 part in the string the string pointed by defdomain is
456 Pointer to the static buffer containing domain name or
457 NULL if there was some error.
460 4.9 parse_select_clause
468 static char *parse_select_clause (const char *clause,
469 const char *username,
470 const char *defdomain);
474 This function is a simple wrapper to the parse_string()
475 function. It parses a query pointed by caluse. username
476 and defdomain strings are used to replace corresponding
477 substitution strings if present in the query: $(local_part)
483 Same as parse_string().
486 4.10 parse_chpass_clause
494 static char *parse_chpass_clause (const char *clause,
495 const char *username,
496 const char *defdomain,
498 const char *newpass_crypt);
502 This function is a simple wrapper to the parse_string()
503 function. It parses a query pointed by caluse. username,
504 defdomain, newpass and newpass_crypt strings are used to
505 replace corresponding substitution strings if present in
506 the query: $(local_part), $(domain), $(newpass),
511 Same as parse_string().
517 *------------------------
519 *------------------------
521 - solve problem with fixed buffer length of local part and the domain part
522 strings after split (problem?)
523 - allow admin to set a group name instead of numerical group id
524 - allow admin to set a username instead of numerical user id
528 - MYSQL_PRESELECT_CLAUSE (query which comes before MYSQL_SELECT_CLAUSE)
529 - MYSQL_POSTSELECT_CLAUSE (query which comes after MYSQL_SELECT_CLAUSE)
530 - PGSQL_PRESELECT_CLAUSE (query which comes before PGSQL_SELECT_CLAUSE)
531 - PGSQL_POSTSELECT_CLAUSE (query which comes after PGSQL_SELECT_CLAUSE)
537 *------------------------
539 *------------------------
541 At the beginning this patch was messy indeed. :> I would like to thank
542 Sam Varshavchik for pointing me a lot how to make it more fast and solid.
543 I would also thank Philip Hazel, Chris Lightfoot and Mike Bremford which
544 by their software capabilities inspired me to write it.
546 Thomas T. Thai <tom@minnesota.com> ported author's original MySQL code
547 to the PostgreSQL module.
549 ---------------------------------------------------------------------------