d9898ee8 |
1 | |
2 | |
3 | |
4 | |
5 | Developer Notes for courier-imap-myownquery.patch |
6 | |
7 | |
8 | |
9 | |
10 | document version: 1.03 |
11 | author: Pawel Wilk |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | 0 What's that? |
29 | |
30 | 1 Modifications overview |
31 | |
32 | 2 Definitions |
33 | |
34 | 3 New data types |
35 | 3.1 struct var_data |
36 | 3.2 typedef size_t (*parsefunc) |
37 | |
38 | 4 New functions |
39 | 4.1 get_variable |
40 | 4.2 parse_core |
41 | 4.3 ParsePlugin_counter |
42 | 4.4 ParsePlugin_builder |
43 | 4.5 parse_string |
44 | 4.6 validate_password |
45 | 4.7 get_localpart |
46 | 4.8 get_domain |
47 | 4.9 parse_select_clause |
48 | 4.10 parse_chpass_clause |
49 | |
50 | 5 Ideas and TODO |
51 | |
52 | 6 Thanks |
53 | |
54 | |
55 | |
56 | |
57 | *----------------------- |
58 | 0 What's that? |
59 | *----------------------- |
60 | |
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. |
69 | |
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. |
73 | |
74 | This patch was created using `diff -Nur` on courier-imap-1.3.12 source. |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | *----------------------- |
81 | 1 Modifications overview |
82 | *----------------------- |
83 | |
84 | Modified files: authmysqllib.c authmysqlrc authpgsqllib.c authpgsqlrc |
85 | |
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) |
88 | |
89 | Changes in the current source code are related to: |
90 | |
91 | - sections where the queries are constructed |
92 | (including memory allocation for the buffers) |
93 | |
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 |
98 | subroutines |
99 | |
100 | - section where the configuration file is read |
101 | |
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 |
106 | |
107 | - sections where the query is constructed |
108 | |
109 | selection is made, depending on configuration variables which |
110 | are set or not - if own query is used |
111 | |
112 | |
113 | |
114 | |
115 | |
116 | *----------------------- |
117 | 2 Definitions |
118 | *----------------------- |
119 | |
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) |
125 | |
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 |
133 | identifier (name). |
134 | |
135 | The last two definitions are just for code simplification. |
136 | |
137 | |
138 | |
139 | |
140 | |
141 | |
142 | *----------------------- |
143 | 3 New data types |
144 | *----------------------- |
145 | |
146 | This section describes new data type definitions and variables. |
147 | |
148 | 3.1 struct var_data |
149 | |
150 | struct var_data { |
151 | const char *name; |
152 | const char *value; |
153 | const size_t size; |
154 | size_t value_length; |
155 | } ; |
156 | |
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). |
161 | |
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 |
166 | |
167 | |
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 |
172 | |
173 | Example: |
174 | |
175 | struct var_data vdt[] = { |
176 | {"some", "replacement", sizeof("some"), 0}, |
177 | {"anotha", NULL, sizeof("anotha"), 0}, |
178 | {NULL, NULL, 0, 0} |
179 | }; |
180 | |
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 |
184 | the paring function. |
185 | |
186 | |
187 | 3.2 typedef size_t (*parsefunc) |
188 | |
189 | typedef int (*parsefunc)(const char *, size_t, void *); |
190 | |
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. |
194 | |
195 | |
196 | |
197 | |
198 | |
199 | *----------------------- |
200 | 4 New functions |
201 | *----------------------- |
202 | |
203 | This section describes added functions. |
204 | |
205 | 4.1 get_variable |
206 | |
207 | NAME |
208 | |
209 | get_variable |
210 | |
211 | SYNOPSIS |
212 | |
213 | static const struct var_data *get_variable (const char *begin, |
214 | size_t len, |
215 | struct var_data *vdt); |
216 | |
217 | DESCRIPTION |
218 | |
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. |
222 | |
223 | This function is also responsible for updating length cache field |
224 | of vdt elements and validating requested variables. |
225 | |
226 | This function repports errors by sending human readable |
227 | messages to the standard error stream. |
228 | |
229 | RETURN VALUE |
230 | |
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. |
234 | |
235 | |
236 | 4.2 parse_core |
237 | |
238 | NAME |
239 | |
240 | parse_core |
241 | |
242 | SYNOPSIS |
243 | static int parse_core (const char *source, struct var_data *vdt, |
244 | parsefunc outfn, void *result); |
245 | |
246 | DESCRIPTION |
247 | |
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 |
262 | it calls it. |
263 | |
264 | outfn() function should be passed by its pointer which |
265 | refers to declaration: |
266 | |
267 | int (*outfn) (const char *begin, |
268 | size_t string_length, |
269 | void *void_pointer); |
270 | |
271 | Each time outfn() is called the result argument of parse_core() |
272 | is passed to the outfn() as a last argument (void_pointer). |
273 | |
274 | Example: |
275 | |
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. |
282 | |
283 | This function repports errors by sending human readable |
284 | messages to the standard error stream. |
285 | |
286 | RETURN VALUE |
287 | |
288 | This function returns -1 if an error has occured and 0 if |
289 | everything went good. |
290 | |
291 | 4.3 ParsePlugin_counter |
292 | |
293 | NAME |
294 | |
295 | ParsePlugin_counter |
296 | |
297 | SYNOPSIS |
298 | |
299 | int ParsePlugin_counter (const char *begin, size_t len, |
300 | void *vp); |
301 | |
302 | DESCRIPTION |
303 | |
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 |
309 | is not used. |
310 | |
311 | This function repports errors by sending human readable |
312 | messages to the standard error stream. |
313 | |
314 | RETURN VALUE |
315 | |
316 | This function returns the variable size or -1 if an error |
317 | has occured, 0 if everything went good. |
318 | |
319 | 4.4 ParsePlugin_builder |
320 | |
321 | NAME |
322 | |
323 | ParsePlugin_builder |
324 | |
325 | SYNOPSIS |
326 | |
327 | int ParsePlugin_builder (const char *begin, size_t len, |
328 | void *vp); |
329 | |
330 | DESCRIPTION |
331 | |
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 |
340 | argument. |
341 | |
342 | RETURN VALUE |
343 | |
344 | This function returns the variable size or -1 if an error |
345 | has occured, 0 if everything went good. |
346 | |
347 | 4.5 parse_string |
348 | |
349 | NAME |
350 | parse_string |
351 | |
352 | SYNOPSIS |
353 | |
354 | static char *parse_string (const char *source, struct var_data *vdt); |
355 | |
356 | DESCRIPTION |
357 | |
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 |
361 | memory area. |
362 | |
363 | This function calls parse_core() function with various parsing |
364 | subroutines passed as function pointers. |
365 | |
366 | 1. It uses parse_core() with ParsePlugin_counter to obtain the |
367 | total amount of memory needed for the output string. |
368 | |
369 | 2. It allocates the memory. |
370 | |
371 | 3. It uses parse_core() with ParsePlugin_builder to build the |
372 | output string. |
373 | |
374 | This function repports errors by sending human readable |
375 | messages to the standard error stream. |
376 | |
377 | RETURN VALUE |
378 | |
379 | Function returns pointer to the result buffer or NULL |
380 | if an error has occured. |
381 | |
382 | WARNINGS |
383 | |
384 | This function allocates some amount of memory using standard |
385 | ANSI C routines. Memory allocated by this function should be |
386 | freed with free(). |
387 | |
388 | |
389 | 4.6 validate_password |
390 | |
391 | NAME |
392 | validate_password |
393 | |
394 | SYNOPSIS |
395 | |
396 | static const char *validate_password (const char *password); |
397 | |
398 | DESCRIPTION |
399 | |
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. |
404 | |
405 | RETURN VALUE |
406 | |
407 | It returns a pointer to the static buffer which contains |
408 | validated password string or NULL if an error has occured. |
409 | |
410 | |
411 | 4.7 get_localpart |
412 | |
413 | NAME |
414 | |
415 | get_localpart |
416 | |
417 | SYNOPSIS |
418 | |
419 | static const char *get_localpart (const char *username); |
420 | |
421 | DESCRIPTION |
422 | |
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. |
427 | |
428 | RETURN VALUE |
429 | |
430 | Pointer to the static buffer containing local part or |
431 | NULL if there was some error. |
432 | |
433 | |
434 | 4.8 get_domain |
435 | |
436 | NAME |
437 | |
438 | get_domain |
439 | |
440 | SYNOPSIS |
441 | |
442 | static const char *get_domain (const char *username, |
443 | const char *defdomain); |
444 | |
445 | DESCRIPTION |
446 | |
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 |
452 | used instead. |
453 | |
454 | RETURN VALUE |
455 | |
456 | Pointer to the static buffer containing domain name or |
457 | NULL if there was some error. |
458 | |
459 | |
460 | 4.9 parse_select_clause |
461 | |
462 | NAME |
463 | |
464 | parse_select_clause |
465 | |
466 | SYNOPSIS |
467 | |
468 | static char *parse_select_clause (const char *clause, |
469 | const char *username, |
470 | const char *defdomain); |
471 | |
472 | DESCRIPTION |
473 | |
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) |
478 | and $(domain). |
479 | |
480 | |
481 | RETURN VALUE |
482 | |
483 | Same as parse_string(). |
484 | |
485 | |
486 | 4.10 parse_chpass_clause |
487 | |
488 | NAME |
489 | |
490 | parse_chpass_clause |
491 | |
492 | SYNOPSIS |
493 | |
494 | static char *parse_chpass_clause (const char *clause, |
495 | const char *username, |
496 | const char *defdomain, |
497 | const char *newpass, |
498 | const char *newpass_crypt); |
499 | |
500 | DESCRIPTION |
501 | |
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), |
507 | $(newpass_crypt). |
508 | |
509 | RETURN VALUE |
510 | |
511 | Same as parse_string(). |
512 | |
513 | |
514 | |
515 | |
516 | |
517 | *------------------------ |
518 | 5 Ideas and TODO |
519 | *------------------------ |
520 | |
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 |
525 | |
526 | - add clauses: |
527 | |
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) |
532 | |
533 | |
534 | |
535 | |
536 | |
537 | *------------------------ |
538 | 6 Thanks |
539 | *------------------------ |
540 | |
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. |
545 | |
546 | Thomas T. Thai <tom@minnesota.com> ported author's original MySQL code |
547 | to the PostgreSQL module. |
548 | |
549 | --------------------------------------------------------------------------- |
550 | |