]> git.pld-linux.org Git - packages/courier-imap.git/blob - courier-imap-1.5.3-myownquery.patch
a590da067060644c891deace1ca67646a3fbe9a5
[packages/courier-imap.git] / courier-imap-1.5.3-myownquery.patch
1 diff -ur courier-imap-1.5.3-orig/authlib/README.authmysql.myownquery courier-imap-1.5.3/authlib/README.authmysql.myownquery
2 --- courier-imap-1.5.3-orig/authlib/README.authmysql.myownquery Tue Jan  8 06:01:22 2002
3 +++ courier-imap-1.5.3/authlib/README.authmysql.myownquery      Mon Oct 14 01:05:11 2002
4 @@ -2,13 +2,18 @@
5  
6  
7  
8 -           Developer Notes for courier-imap-myownquery.patch
9  
10 +                   Developer Notes and Usage Instructions
11 +                                   
12 +                                   of
13 +                                   
14 +                      courier-imap-authmysql-myownquery
15  
16 +                                   by
17 +                       
18 +                       Pawel Wilk <siefca@kernel.pl>
19  
20  
21 -                                                       document version: 1.03
22 -                                                       author: Pawel Wilk
23  
24  
25  
26 @@ -19,75 +24,843 @@
27  
28  
29  
30 + .. table of contents..
31  
32  
33 +PREAMBLE
34  
35 +PART I - Usage Instructions
36  
37 +       1 What's that?
38 +       
39 +       2 When will I need it?
40 +       
41 +       3 How does it work?
42 +         3.1 configuration variables
43 +         3.2 queries
44 +         3.3 substitutions
45 +         3.4 triggers
46 +         3.5 empty default domain name
47 +         3.6 whitespaces in queries
48 +       
49 +       4 Examples of usage
50 +         4.1 corporate mail system
51 +           4.1.1 database structure
52 +           4.1.2 authdaemon configuration
53 +         4.2 virtual mail domains provider
54 +           4.2.1 database structure
55 +           4.2.2 authdaemon configuration
56 +
57 +PART II - Developer Notes
58 +
59 +       1 Modifications overview
60 +
61 +       2 Definitions
62 +
63 +       3 New data types
64 +         3.1 struct var_data
65 +         3.2 typedef size_t (*parsefunc)
66 +  
67 +       4 New functions
68 +         4.1 get_variable
69 +         4.2 parse_core
70 +         4.3 ParsePlugin_counter
71 +         4.4 ParsePlugin_builder
72 +         4.5 parse_string
73 +         4.6 validate_password
74 +         4.7 get_localpart
75 +         4.8 get_domain
76 +         4.9 get_username
77 +         4.10 parse_select_clause
78 +         4.11 parse_chpass_clause
79 +         4.12 auth_mysql_on_trigger
80 +         4.13 auth_mysql_on_pass
81 +         4.14 auth_mysql_checkpassword
82  
83 +       5 Ideas and TODO
84  
85 -0 What's that?
86 +       6 Thanks
87  
88 -1 Modifications overview
89  
90 -2 Definitions
91  
92 -3 New data types
93 -  3.1 struct var_data
94 -  3.2 typedef size_t (*parsefunc)
95 -  
96 -4 New functions
97 -  4.1 get_variable
98 -  4.2 parse_core
99 -  4.3 ParsePlugin_counter
100 -  4.4 ParsePlugin_builder
101 -  4.5 parse_string
102 -  4.6 validate_password
103 -  4.7 get_localpart
104 -  4.8 get_domain
105 -  4.9 parse_select_clause
106 -  4.10 parse_chpass_clause
107 -  
108 -5 Ideas and TODO
109  
110 -6 Thanks
111  
112  
113 +//////////////////////////////// PREAMBLE /////////////////////////////////////
114 +
115 +This is README document for "myownquery" patch for Courier's Authdaemon.
116 +This document version is 1.36
117 +
118 +* The patch, which this document describes is developed for Courier-IMAP
119 +  version 1.5.3 and the official patch revision is 2.
120 +
121 +* You can download the patch from the FTP server using URI:
122 +
123 +ftp://ftp.pld.org.pl/people/siefca/patches/courier/courier-imap-1.5.3-myownquery.patch
124  
125 +   it should also be accessible on mirroring servers, which list can be
126 +   obtained under: http://www.pld.org.pl/
127 +
128 +* To know more about getting Courier see http://www.courier-mta.org/
129 +
130 +* This patch, including the documentation, is released under GNU GPL
131 +  license terms. You should look at the COPYING file present in
132 +  Courier sources.
133 +
134 +.
135 +.
136 +.
137 +.
138 +.
139 +.
140 +
141 +//////////////////////////////// PART I - Usage ///////////////////////////////
142  
143                     *-----------------------
144 -                    0 What's that?
145 +                    1 What's that?
146                     *-----------------------
147  
148 -Courier-imap-myownquery.patch allows administrator to set own MySQL queries
149 -used by authdaemon to authenticate user (including fetchig credentials) and to
150 -change user's password. It allows to construct SELECT or UPDATE clause in the
151 -configuration file (authmysqlrc) by adding two new configuration variables:
152 -MYSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE. It may be useful in the mail
153 -environments where there is such a need to have different database structure
154 -and/or tables scheme than expected by authmysql module.
155 +Courier-imap-myownquery's features allow the administrator to set his
156 +own MySQL queries used by authdaemon to authenticate a user (including
157 +fetchig his credentials) and to change the user's password. It allows
158 +one to write a SELECT and UPDATE clause in the configuration file
159 +(authmysqlrc) using the new configuration options. It may be useful in
160 +mail environments where there is a need to have a different database
161 +structure and/or tables scheme than expected by authmysql module.
162 +
163 +It also implements a small parsing engine for substitution of
164 +variables which may appear in the SQL clauses, such as a username or a
165 +domain.
166  
167 -It also implements a small parsing engine for substitution variables which
168 -may appear in the clauses and are used to put informations like username
169 -or domain into the right place of a query.
170  
171 -This patch was created using `diff -Nur` on courier-imap-1.3.12 source.
172  
173  
174  
175 +                   *-----------------------
176 +                    2 When will I need it?
177 +                   *-----------------------
178 +
179 + o When you already have some MySQL database filled up with the data
180 +    and there is no chance to change the whole structure to make it
181 +    work with a standard authmysql table. Typical situation is when
182 +    all the
183 +    data required to authenticate a user is arranged in more than one
184 +    table.
185 +
186 + o When you have some great idea how to make the database structure
187 +    more efficient due to your needs and your requirements.
188 +       
189 + o When doing something 'by-myself' is in your style and you just want
190 +    to create your own database, just to feel the pleasure of doing
191 +    something original. :)
192  
193  
194 +
195 +
196 +
197 +                   *-----------------------
198 +                    3 How does it work?
199 +                   *-----------------------
200 +
201 +There are three things which the feature concerns:
202 +
203 +- fetching clauses from the configuration file
204 +- doing substitution replacements inside of SQL clauses
205 +- passing prepared query on to the mysql interface funtions
206 +
207 +3.1 configuration options
208 +
209 +You can apply your own MySQL queries using a set of the configuration
210 +options.  The options you'll need to make the authmysql your slave
211 +are:
212 +
213 +MYSQL_SERVER           (required)
214 +MYSQL_USERNAME         (required)
215 +MYSQL_PASSWORD         (required)
216 +
217 +               The server name, userid, and password used to log in.
218 +
219 +MYSQL_DATABASE (required)
220 +
221 +               The name of the MySQL database we will open.
222 +
223 +DEFAULT_DOMAIN (optional)
224 +
225 +               If DEFAULT_DOMAIN is defined, and someone tries to log
226 +               in as 'user', we will look up 'user@DEFAULT_DOMAIN'
227 +               instead.
228 +
229 +USER_DOMAIN_SEPARATORS (optional)
230 +
231 +               This may contain the set of characters used by parsing
232 +               routines to split local part of the virtual mailbox
233 +               name from the part which describes the domain name. If
234 +               it's not defined the set containing @% is assumed, so
235 +               the user can enter either: user@domain or user%domain
236 +
237 +MYSQL_SELECT_CLAUSE    (required)
238 +MYSQL_CHPASS_CLAUSE    (required under some circumstances)
239 +
240 +               These are the major options you should use. See 3.2
241 +               section for more info.
242 +
243 +ON_PASS_OK_CLAUSE      (optional)
244 +ON_PASS_FAIL_CLAUSE    (optional)
245 +ON_PASS_CHANGE_CLAUSE  (optional)
246 +
247 +               These are used to do a MySQL query whether user has
248 +               passed the authentication verification
249 +               (ON_PASS_OK_CLAUSE) or there was the authentication
250 +               failure (ON_PASS_FAIL_CLAUSE), or whether user has
251 +               changed his password (ON_PASS_CHANGE_CLAUSE).
252 +               Query results have no meaning. You can use the same
253 +               substitution variables in your query as with
254 +               MYSQL_SELECT_CLAUSE. See 3.4 section for more info.
255 +
256 +The options which have no effect, and may be safetly left blank are:
257 +
258 +MYSQL_USER_TABLE
259 +MYSQL_CRYPT_PWFIELD
260 +MYSQL_CLEAR_PWFIELD
261 +MYSQL_UID_FIELD
262 +MYSQL_GID_FIELD
263 +MYSQL_LOGIN_FIELD
264 +MYSQL_HOME_FIELD
265 +MYSQL_NAME_FIELD
266 +MYSQL_MAILDIR_FIELD
267 +MYSQL_QUOTA_FIELD
268 +MYSQL_WHERE_CLAUSE
269 +
270 +3.2 queries
271 +
272 +The feature adds two configuration options (clauses), which are parsed
273 +first, and then applied as MySQL queries to MySQL interface
274 +routines. These options are: MYSQL_SELECT_CLAUSE and
275 +MYSQL_CHPASS_CLAUSE. After each option a number of spaces and/or tabs
276 +is allowed, and then MySQL query is expected. For better look, your
277 +queries can have line breaks. Each line break should be preceded by
278 +the backslash sign. Look into examples chapter (4) to see how it
279 +should look like. First clause is used to authenticate a user, and the
280 +second to change his password.
281 +
282 +You should note that a query identified by MYSQL_SELECT_CLAUSE should
283 +return fixed number (9) of fields and each field should match the
284 +variable expected by authentication routines. These fields are:
285 +
286 +
287 +* username - which is the currently logged user's username (or the
288 +             username with domain if you want it)
289 +
290 +  cryptpw - which is the user's crypted password
291 +*
292 +  clearpw - which is the user's plaintext password
293 +
294 +* uid - which is a numerical UID value used as a process's UID when
295 +        accessing the mailbox directory
296 +
297 +* gid - as above, but refers to GID
298 +
299 +* home - which contains full path to the user's home directory
300 +
301 +  maildir - which contains the directory name inside the user's home;
302 +           treated as INBOX folder when accessing mailbox - if it's
303 +           empty then the 'Maildir' string is used
304 +
305 +quota - which describes a quota size for the mailbox
306 +
307 +fullname - which may contain the user's fullname
308 +
309 +(The fields marked by the asterix sign are required and cannot have an
310 + empty results. In case of passwords, at least one of the shown fields
311 + should contain some result.)
312 +
313 +So, the typical query clause may start with:
314 +
315 +MYSQL_SELECT_CLAUSE SELECT                                      \
316 + users.username,                                               \
317 + users.cryptpw,                                                        \
318 + users.clearpw,                                                \
319 + domains.uid,                                                  \
320 + domains.gid,                                                  \
321 + users.mailbox_path)                                           \
322 + ''                                                            \ 
323 + domains.quota,                                                        \
324 + ''                                                            \
325 +...
326 +
327 +Note that in this short example we're assuming that we have two tables
328 +(users and domains) and INBOX path is always called 'Maildir' and
329 +we're not using the fullname field (the query will always return an
330 +empty string in its place).
331 +
332 +Also note that you may discard one of the password fields if you don't
333 +want to use an authentication mechanism, which needs it. For example,
334 +if you don't want to use MD5-CRAM you may put '' into the place of
335 +clearpw (because, for example you're in paranoid mode and you don't
336 +even want to keep plain passwords in the database:).
337 +
338 +3.3 substitutions
339 +
340 +Substitutions are strings, which may appear in your query, and which
341 +have a special meaning. You can also call them substitution
342 +variables. If substitution variable is known for a clause context then
343 +it is parsed. If it isn't known the error is generated. In the default
344 +compilation of authmysql module any substitution variable is declared
345 +inside of two substrings - the first is a dollar sign concatenated
346 +with opening parenthesis, and the second is a closing parenthesis
347 +sign. First symbol identifies beginning of a substitution variable,
348 +and the second closes it. The string between the beginning and the
349 +closing symbol is called substitution variable's name.
350 +
351 +When, as I said before, the name is known to the parsing routine the
352 +substitution is made and the proper value appears in place of the
353 +substitution variable, while passing on the query for later
354 +processing.
355 +
356 +Allowed substitution variables:
357 +
358 +context: MYSQL_SELECT_CLAUSE, ON_PASS_FAIL_CLAUSE, ON_PASS_OK_CLAUSE,
359 +        ON_PASS_CHANGE_CLAUSE
360 +
361 +$(local_part)          will be replaced by currently verified user's username
362 +                       (without the domain part)
363 +
364 +$(domain)              will be replaced by currently verified user's domain
365 +                       name (if present, or if not present but the
366 +                       DEFAULT_DOMAIN was used) or by the empty,
367 +                       zero-length
368 +                       string if the domain cannot be obtained
369 +
370 +$(username)            will be replaced by currently verified user's username
371 +                       concatenated with the given domain name using
372 +                       @ symbol -- if the domiain name cannot be
373 +                       obtained (even looking up DEFAULT_DOMAIN) the
374 +                       separation sign will not appear and only the
375 +                       given username will be presented
376 +
377 +context: MYSQL_CHPASS_CLAUSE
378 +
379 +$(local_part)           will be replaced by currently verified user's username
380 +                        (without the domain part)
381 +
382 +$(domain)               will be replaced by currently verified user's domain
383 +                        name (if present, or if not present but the
384 +                        DEFAULT_DOMAIN was used) or by the empty, zero-length
385 +                        string if the domain cannot be obtained
386 +
387 +$(username)             will be replaced by currently verified user's username
388 +                        concatenated with the given domain name using
389 +                        @ symbol -- if the domiain name cannot be
390 +                        obtained (even by looking up DEFAULT_DOMAIN)
391 +                        the separation sign will not appear and only
392 +                        the given username will be presented
393 +
394 +$(newpass)             will be replaced by currently authenticated user's
395 +                       new password to set up (plaintext password)
396 +
397 +$(newpass_crypt)       will be replaced by currently authenticated user's
398 +                       new password to set up (MD5 form created from
399 +                       entered plain form)
400 +
401 +3.4 triggers
402 +
403 +Triggers are MySQL queries, which are performed depending on
404 +authentication state. Currently, there are three triggers which you
405 +may use. First is called ON_PASS_OK_CLAUSE and it is performed when
406 +the authentication succeedes.  The second is called
407 +ON_PASS_FAIL_CLAUSE and has the reverse meaning. The third, which name
408 +is ON_PASS_CHANGE_CLAUSE is performed whenever user has changed his
409 +password.
410 +
411 +You can declare triggers in the authmysqlrc configuration file. They
412 +can be used to arrange some logging facility in the database or just
413 +to keep last times of the successful/failed login tries. The typical
414 +trigger, which puts last login date into the users' table can look
415 +like this:
416 +
417 +ON_PASS_OK_CLAUSE      UPDATE users SET last_login=CURRENT_TIMESTAMP \
418 +                       WHERE username='$(username)';
419 +
420 +or, if you would like to know about last login failure for users you can try:
421 +
422 +ON_PASS_FAIL_CLAUSE    UPDATE users SET last_bad_login=CURRENT_TIMESTAMP \
423 +                       WHERE username='$(username)';
424 +
425 +and/or, if you want to know last password changes you can use:
426 +
427 +ON_PASS_CHANGE_CLAUSE  UPDATE users SET pw_change=CURRENT_TIMESTAMP \
428 +                       WHERE username='$(username)';
429 +
430 +Note, that YOU CAN use the triggers even if you aren't using
431 +MYSQL_SELECT_CLAUSE. Also note, that if the entered username
432 +doesn't match any real user ON_PASS_FAIL_CLAUSE will be simply
433 +discarded. To watch brute force attacs against known usernames
434 +you have to use log files. ;]
435 +
436 +3.5 empty default domain name
437 +
438 +Sometimes happens, that you want to allow user to log in without
439 +having a domain name entered and you expect it will be treated as an
440 +empty string, neither an error, nor default domain. In that case you
441 +should leave DEFAULT_DOMAIN option unset in authmysqlrc file and your
442 +database should have empty (not NULL) string fields for users without
443 +the domain name specified.
444 +
445 +3.6 whitespaces in queries
446 +
447 +In a few examples, here and in authmysqlrc file, I used to put many
448 +whitespaces and tabs to make the examples more clear for reader.
449 +However, it is recommended to not torture authdaemon's parser in
450 +that way and to remove unnecessary characters. ;]
451 +
452 +For example, the clause:
453 +
454 +MYSQL_CHPASS_CLAUSE UPDATE                                       \
455 + users                                                           \
456 + SET clearpw='$(newpass)',                                       \
457 +     cryptpw='$(newpass_crypt)'                                  \
458 + WHERE username='$(local_part)'                                  \
459 + AND   domain_name='$(domain)'
460 +
461 +can be safetly rewritten as:
462 +
463 +MYSQL_CHPASS_CLAUSE UPDATE users \
464 +SET clearpw='$(newpass)', cryptpw='$(newpass_crypt)' \
465 +WHERE username='$(local_part)' AND domain_name='$(domain)'
466 +
467 +
468 +
469 +
470 +
471 +
472 +                   *-----------------------
473 +                    4 Examples of usage
474 +                   *-----------------------
475 +
476 +The "ownquery" feature gives you possibility to adapt an
477 +authentication query to the database. So the first thing you have to
478 +do is to design the database structure you need, whithout being
479 +grieved at what structure authentication routines like. You have to
480 +take care about four essential things:
481 +
482 + o  The database
483
484 + o  The users' data in the database
485
486 + o  The proper directories for keeping virtual mailboxes and a system
487 +    user which can read and write them
488
489 + o  The proper MySQL queries in your authmysqlrc configuration file
490 +
491 +4.1 corporate mail system
492 +
493 +This example is concerned about a corporate mail system with a small
494 +ammount of served virtual domains. The database scheme was derived
495 +from tpop3d documentation and modified a bit.
496 +
497 +4.1.1 database structure
498 +
499 +Our goal here is to separate the data responsible for keeping mailbox
500 +credentials from the data describing domains.
501 +
502 +Let's create some tables for our example, filled up with an example
503 +data:
504 +
505 +table:          domains
506 +
507 +purpose:        associates virtual domain with domain name and informations
508 +               necessary to access mailboxes withing the domain
509 +
510 +fields:         domain_name         - fully qualified domain name
511 +                path_prefix         - absolute pathname which points to
512 +                                      a directory where domain's mailboxes
513 +                                      are located
514 +                quota               - default quota for each mailbox
515 +                uid                 - UID used to work on mailboxes
516 +                gid                 - GID used to work on mailboxes
517 +               
518 +        +----------------+-------------+-----+-----+----------+
519 +        | domain_name    | path_prefix | uid | gid | quota    |
520 +        +----------------+-------------+-----+-----+----------+
521 +        | exampledom.com | /var/mail/x | 555 | 555 | 10000000 |
522 +       | pld.org.pl     | /var/mail/p | 556 | 556 | 20000000 |
523 +       | pld.net.pl     | /var/mail/p | 556 | 556 | 20000000 |
524 +       +----------------+-------------+-----+-----+----------+
525 +
526 +table:          users
527 +
528 +purpose:        associates virtual mailbox with user and domain name,
529 +               and with informations necessary to access mailbox
530 +
531 +fields:         username            - user login name (mailbox name)
532 +               domain_name          - fully qualified domain name
533 +                mailbox_path         - relative pathname for mailbox
534 +                                       (will be appended to the path_prefix
535 +                                        from domain_auth table to specify
536 +                                       user's mailbox location)
537 +                cryptpw              - crypted password
538 +                plainpw              - plaintext password
539 +
540 +        +----------+----------------+--------------+------------+--------+
541 +       | username | domain_name    | mailbox_path | cryptpw   | plainpw |
542 +       +----------+----------------+--------------+-----------+---------+
543 +       | siefca   | pld.org.pl     | s/siefca     | $1$fs45.. | dupa.8  |
544 +       | siefca   | pld.net.pl     | s/siefca     | $1$fs45.. | dupa.8  |
545 +       | f00bar   | exampledom.com | foobar       | $1$g44w.. | secret  |
546 +       +----------+----------------+--------------+-----------+---------+ 
547 +
548 +Using MySQL monitor you can create these tables entering CREATE
549 +sequences.  Be sure to connect to the database using administrative
550 +MySQL account (usualy: mysql -u mysql -p).
551 +
552 +--------------------- cut here
553 +
554 +# Create the database called vmail.
555 +
556 +CREATE database vmail;
557 +
558 +# Create an example MySQL user, which can read, write and delete data
559 +# from vmail database. Username: vuser Password: secret_password
560 +
561 +GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.*
562 +             TO vuser@localhost
563 +             IDENTIFIED BY 'secret_password';
564 +
565 +FLUSH PRIVILEGES;
566 +
567 +# Create the tables.
568 +
569 +use vmail;
570 +
571 +CREATE TABLE domains (
572 +        domain_name           char(255) DEFAULT '',
573 +        path_prefix           char(255) DEFAULT '' NOT NULL,
574 +        uid                   int(10) unsigned DEFAULT '15000' NOT NULL,
575 +        gid                   int(10) unsigned DEFAULT '15000' NOT NULL,
576 +        quota                 char(255) DEFAULT '2000000' NOT NULL,
577 +        KEY domain_name (domain_name(255))
578 +        );
579 +
580 +CREATE TABLE users (
581 +        username              char(128) DEFAULT '' NOT NULL,
582 +        domain_name           char(255) DEFAULT '',
583 +        mailbox_path          char(255) DEFAULT '' NOT NULL,
584 +        cryptpw               char(128) DEFAULT '' NOT NULL,
585 +        clearpw               char(128) DEFAULT '' NOT NULL,
586 +        KEY username (username(128))
587 +        );
588 +
589 +# Create an example virtual domain entry
590 +# name  : exampledom.com
591 +# uid   : 555
592 +# gid   : 555
593 +# path  : /var/mail/x
594 +# quota : 10 Megs per mailbox
595 +
596 +INSERT INTO domains VALUES ('exampledom.com', '/var/mail/x', 555, 555,
597 +                            '10000000');
598 +
599 +# Create an example virtual user entry
600 +# username      : siefca
601 +# domain name   : exampledom.com
602 +# cryptpw       : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/
603 +# clearpw       : dupa.8
604 +# mailbox path  : s/siefca
605 +
606 +INSERT INTO users VALUES ('siefca', 'exampledom.com', 's/siefca',
607 +                         '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/',
608 +                         'dupa.8');
609 +
610 +--------------------- cut here
611 +
612 +Note: If you would like to have your passwords more safe, then just
613 +       omit the clearpw column and put '' into the config-query in
614 +       its place while doing SELECT on a database. But be ware -
615 +       you'll be unable to use authentication methods which needs it,
616 +       like MD5_CRAM.
617 +
618 +4.1.2 authdaemon configuration
619 +
620 +When our database is ready we can set up the configuration. :-) Go to
621 +authmysqlrc file and edit it.
622 +
623 +At the beginning we should take care about general informations, which
624 +are identifying our database:
625 +
626 +MYSQL_SERVER            localhost
627 +MYSQL_USERNAME          vuser
628 +MYSQL_PASSWORD          secret_password
629 +MYSQL_DATABASE          vmail
630 +
631 +Then we should add a clause responsible for authenticating user and
632 +fetching credentials:
633 +
634 +DEFAULT_DOMAIN          exampledom.com
635 +
636 +MYSQL_SELECT_CLAUSE SELECT                                      \
637 + users.username, users.cryptpw, users.clearpw,                  \
638 + domains.uid, domains.gid,                                      \
639 + CONCAT_WS('/',domains.path_prefix,users.mailbox_path),         \
640 + '', domains.quota, ''                                          \
641 + FROM users, domains                                            \
642 +  WHERE domains.domain_name='$(domain)'                         \
643 +    AND users.username='$(local_part)'                          \
644 +    AND domains.domain_name=users.domain_name
645 +
646 +
647 +Note the '' in the place of field which tells where user's INBOX
648 +resides and in place of realname field. You should use '' if you want
649 +to put an empty value as a query result for some field.
650 +
651 +We also should add some configuration for changing user's password:
652 +
653 +MYSQL_CHPASS_CLAUSE UPDATE                                       \
654 + users                                                           \
655 + SET clearpw='$(newpass)',                                       \
656 +     cryptpw='$(newpass_crypt)'                                  \
657 + WHERE username='$(local_part)'                                  \
658 + AND   domain_name='$(domain)'
659 +
660 +And finally...
661 +Create a system user/group and a proper directory structure. In our example:
662 +
663 +groupadd -g 555 xdomain
664 +useradd -u 555 -g 555 xdomain
665 +mkdir -p /var/mail/x/s/siefca
666 +chmod -R 0770 /var/mail/x
667 +maildirmake /var/mail/x/s/siefca/Maildir
668 +chown -R xdomain.xdomain /var/mail/x
669 +
670 +Now, restart the authdaemon and see if it works. Try: telnet 0 pop3
671 +
672 +and type:
673 +
674 +USER siefca [ENTER]
675 +PASS dupa.8 [ENTER]
676 +
677 +You should get Ok response. ;)
678 +
679 +4.2 virtual mail domains provider
680 +
681 +Let's consider more complicated database scheme, where there is a need
682 +to associate a lot of information with the domain name, including
683 +registrant information, owner, etc. That implies data separation
684 +between domain name, user and domain additional informations (which
685 +are unwanted when authentication process takes place). By proper data
686 +separation I mean avoiding unwanted redundancy in the database.
687 +
688 +Currently applied example doesn't care about the update password
689 +problem.  This is due to current abilities of MySQL and authdaemon
690 +(authmysql).  MySQL doesn't support subsequent SELECTs on UPDATE
691 +operation, and authmysql doesn't supports batched queries at the
692 +moment.
693 +
694 +4.2.1 database structure
695 +
696 +table:         domain_names
697 +
698 +purpose:       associates domain_id with domain name
699 +
700 +fields:                domain_name             - fully qualified domain name
701 +               domain_id               - domain identifier
702 +
703 +       +----------------+-----------+
704 +       | domain_name    | domain_id |
705 +       +----------------+-----------+
706 +       | exampledom.com |         1 |
707 +       | pld.org.pl     |         2 |
708 +       | pld.net.pl     |         2 |
709 +       | foobare.net.uk |         3 |
710 +       +----------------+-----------+
711 +
712 +Note, that for pld.org.pl and pld.net.pl the domain identifiers are
713 +the same.  We can create a domain aliases in such a way. :)
714 +
715 +table:         domain_auth
716 +
717 +purpose:       associates domain_id with authentication credentials
718 +               which are common for all users in the virtual domain
719 +
720 +fields:                domain_id               - domain identifier
721 +               path_prefix             - absolute pathname which points to
722 +                                         a directory where domain's mailboxes
723 +                                         are located
724 +               quota                   - default quota for each mailbox
725 +               uid                     - UID used to work on mailboxes
726 +               gid                     - GID used to work on mailboxes
727 +               
728 +       +------------+---------------+--------+-------+-------+
729 +       | domain_id  | path_prefix   | quota  | uid   | gid   |
730 +       +------------+---------------+--------+-------+-------+
731 +       |          1 | /var/mail/ex  | 100000 | 15000 | 15000 | 
732 +       |          2 | /var/mail/pld | 555500 | 15001 | 15000 |
733 +       |          3 | /home/f0/mail |   8000 | 15002 | 15000 |
734 +       +------------+---------------+--------+-------+-------+
735 +
736 +table:         domain_info
737 +
738 +purpose:       associates domain_id with additional informations
739 +
740 +fields:                domain_id               - domain identifier
741 +               registrant_id           - registrant identifier
742 +               nic_handle              - NIC handle
743 +               owner_id                - domain's owner identifier
744 +               expires                 - domain's expiration date
745 +
746 +       +------------+---------------+------------+----------+---------+
747 +       | domain_id  | registrant_id | nic_handle | owner_id | expires |
748 +       +------------+---------------+------------+----------+---------+
749 +
750 +       (we don't need to say anything more about this table indeed)
751 +
752 +table:         users
753 +
754 +purpose:       associates users' identifiers with domains' identifiers
755 +               and infers the credentials for various virtual mailboxes
756 +
757 +fields:                username                - user's login name
758 +               domain_id               - domain identifier
759 +               cryptpw                 - crypted password
760 +               plainpw                 - plaintext password
761 +               quota                   - user's mailbox quota
762 +                                         (will override quota value set for
763 +                                         the whole virtual domain)
764 +               path                    - relative pathname for mailbox
765 +                                         (will be appended to the path_prefix
766 +                                         from domain_auth table to specify
767 +                                         user's mailbox location)
768 +
769 +       +------------+-----------+----------+-----------+-------+------------+
770 +       | username   | domain_id | cryptpw  | plainpw   | quota | path       |
771 +       +------------+-----------+----------+-----------+-------+------------+
772 +       | foobar     |         1 | $1$hlIeE | dupa.8    | NULL  | f/o/foobar |
773 +       | breeder    |         2 | $1$TWsdf | ziarno128 | 77777 | brd        |
774 +       +------------+-----------+----------+-----------+-------+------------+
775 +
776 +    (you can add a realname column here, it doesn't fit to my terminal window:)
777 +
778 +--------------------- cut here
779 +
780 +# Create the database called vmail.
781 +
782 +CREATE database vmail;
783 +
784 +# Create an example MySQL user, which can read, write and delete data
785 +# from vmail database. Username: vuser Password: secret_password
786 +
787 +GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.*
788 +            TO vuser@localhost
789 +            IDENTIFIED BY 'secret_password';
790 +
791 +FLUSH PRIVILEGES;
792 +
793 +# Create the tables.
794 +
795 +use vmail;
796 +
797 +CREATE TABLE domain_names (
798 +       domain_id             int(10) unsigned NOT NULL,
799 +       domain_name           char(255) DEFAULT '' NOT NULL,
800 +       KEY domain_name (domain_name(255))
801 +       );
802 +
803 +CREATE TABLE domain_auth (
804 +       domain_id             int(10) unsigned DEFAULT 1 NOT NULL,
805 +       uid                   int(10) unsigned DEFAULT '15000' NOT NULL,
806 +       gid                   int(10) unsigned DEFAULT '15000' NOT NULL,
807 +       path_prefix           char(255) DEFAULT '' NOT NULL,
808 +       quota                 char(255) DEFAULT '20000000' NOT NULL,
809 +       KEY domain_id (domain_id)
810 +       );
811 +
812 +CREATE TABLE users (
813 +        username             char(128) DEFAULT '' NOT NULL,
814 +       domain_id             int(10) unsigned DEFAULT 1 NOT NULL,
815 +        cryptpw               char(128) DEFAULT '' NOT NULL,
816 +       plainpw               char(128) DEFAULT '' NOT NULL,
817 +        name                  char(128) DEFAULT '' NOT NULL,
818 +        quota                 char(255),
819 +       path                  char(255) DEFAULT '' NOT NULL,
820 +        KEY username (username(128))
821 +       );
822 +       
823 +# Create an example virtual domain entry
824 +# id   : 1
825 +# name : exampledom.com
826 +# uid  : 15000
827 +# gid  : 15000
828 +# path : /var/mail/example
829 +# quota        : 20 Megs per mailbox
830 +
831 +INSERT INTO domain_names VALUES (1, 'exampledom.com');
832 +INSERT INTO domain_auth VALUES (1, '15000', '15000', '/var/mail/example',
833 +                               '20000000');
834 +
835 +# Create an example virtual user entry
836 +# username     : siefca
837 +# domain id    : 1 (points to exampledom.com)
838 +# cryptpw      : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/
839 +# clearpw      : dupa.8
840 +# name         : Pawel Wilk
841 +# quota                : NULL (we want it to be fetched from domain_auth table)
842 +# mailbox path : s/i/siefca
843 +
844 +INSERT INTO users VALUES ('siefca', 1, '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/',
845 +                         'dupa.8', 'Pawel Wilk', NULL, 's/i/siefca');
846 +
847 +--------------------- cut here
848 +
849 +Ok, we've done what we need. Don't forget to create system user with
850 +UID and GID set to 15000, and a directory containing mailboxes (in
851 +this case: /var/mail/example) owned by system user I've mentioned
852 +above.  There is also necessary to create Maildir folder structure for
853 +our user inside the virtual domain directory - you can configure your
854 +MTA agent to do such thing when first message arrive or use
855 +maildirmake tool, which comes with Courier-IMAP.
856 +
857 +
858 +4.2.2 authdaemon configuration
859 +
860 +DEFAULT_DOMAIN         exampledom.com
861 +
862 +MYSQL_SELECT_CLAUSE SELECT                                     \
863 + users.username,                                               \
864 + users.cryptpw,                                                        \
865 + users.plainpw,                                                        \
866 + domain_auth.uid,                                              \
867 + domain_auth.gid,                                              \
868 + CONCAT_WS('/',domain_auth.path_prefix,users.path),            \
869 + '',                                                           \
870 + IFNULL(users.quota, domain_auth.quota),                       \
871 + users.name                                                    \
872 + FROM users, domain_names, domain_auth                                 \
873 + WHERE domain_names.domain_name='$(domain)'                    \
874 +  AND users.username='$(local_part)'                           \
875 +  AND domain_names.domain_id=users.domain_id                   \
876 +  AND domain_names.domain_id=domain_auth.domain_id
877 +
878 +
879 +.
880 +.
881 +.
882 +.
883 +.
884 +.
885 +
886 +/////////////////////////// PART II - Developer Notes /////////////////////////
887 +
888                     *-----------------------
889                      1 Modifications overview
890                     *-----------------------
891  
892 -Modified files:        authmysqllib.c authmysqlrc
893 +Modified files:        authmysqllib.c authmysql.c authmysql.h authmysqlrc
894  
895  Each modified set of instructions is marked by my e-mail address:
896  siefca@pld.org.pl
897  
898 -Changes in the current source code are related to:
899 +Changes in the source code are related to:
900  
901 -- sections where the queries are constructed
902 +- sections where the queries are constructed [authmysqllib.c]
903    (including memory allocation for the buffers)
904  
905         when MYSQL_SELECT_CLAUSE or MYSQL_CHPASS_CLAUSE is
906 @@ -95,17 +868,29 @@
907         passing over current memory allocation and query construction
908         subroutines
909           
910 -- section where the configuration file is read
911 +- section where the configuration file is read [authmysqllib.c]
912  
913         i've had to modify read_env() function to allow line breaks
914 -       - now each sequence of the backslash as a first character and
915 +       -- now each sequence of the backslash as a first character and
916         newline as the second is replaced by two whitespaces while
917         putting into the buffer
918  
919 -- sections where the query is constructed
920 +       i've also added USER_DOMAIN_SEPARATORS configuration option --
921 +       it is used by get_localpart(), get_domain() and get_username()
922 +       functions, which are described below
923 +
924 +- sections where the query is constructed [authmysqllib.c]
925  
926         selection is made, depending on configuration variables which
927 -       are set or not - if own query is used 
928 +       are set or not -- if own query is used 
929 +
930 +- sections where the user is authenticated against the authinfo [authmysql.c]
931 +
932 +       i've detached a part of code responsible for authentication
933 +       against crypted and plain password -- now it is in stub
934 +       function called auth_mysql_checkpassword() -- due to obtain
935 +       more clean code in auth_mysql_login() and
936 +       auth_mysql_changepw() around trigger calling functions
937  
938  
939  
940 @@ -123,14 +908,20 @@
941  
942  These definitions allows to change substitution marks in an easy way.
943  SV_BEGIN_MARK refers to sequence of characters treated as a prefix of
944 -each substitution variable and SV_END_MARK refers to string which is
945 -a closing suffix. If the expected substitution variable is called
946 +each substitution variable and SV_END_MARK refers to string which is a
947 +closing suffix. If the expected substitution variable is called
948  'local_part' (without apostrophes) then '$(local_part)' is a valid
949 -string representation for SV_BEGIN_MARK set to "$(" and SV_END_MARK to ")".
950 -MAX_SUBSTITUTION_LEN defines maximal length of a substitution variable's
951 -identifier (name).
952 +string representation for SV_BEGIN_MARK set to "$(" and SV_END_MARK to
953 +")".  MAX_SUBSTITUTION_LEN defines maximal length of a substitution
954 +variable's identifier (name).
955 +
956 +The last two definitions (SV_BEGIN_LEN and SV_END_LEN) are just for
957 +code simplification.
958 +
959 +#define         DEF_SEPARATORS_SET      "@%"
960  
961 -The last two definitions are just for code simplification.
962 +The DEF_SEPARATORS_SET directive defines the set of characters, which
963 +are treated as separators when splitting local part from the domain.
964  
965  
966  
967 @@ -152,10 +943,10 @@
968         size_t value_length;
969         } ;
970  
971 -This structure holds information needed by parsing routines.
972 -Using var_data array you may specify a set of string substitutions
973 -which should be done while parsing a query. Last element in array
974 -should have all fields set to zero (null). 
975 +This structure holds information needed by parsing routines.  Using
976 +var_data array you may specify a set of string substitutions which
977 +should be done while parsing a query. Last element in array should
978 +have all fields set to zero (null).
979  
980  name field     - should contain substituted variable name
981  value          - should contain string which replaces it
982 @@ -164,9 +955,9 @@
983  
984  
985  explanation: size is used to increase speed of calculation proccess
986 -            value_length is used to cache length of a value during the
987 -            parsing subroutines - it helps when substitution variable
988 -            occures more than once within the query
989 +            value_length is used to cache length of a value during
990 +            the parsing subroutines - it helps when substitution
991 +            variable occures more than once within the query
992   
993  Example:
994  
995 @@ -177,18 +968,19 @@
996  };
997  
998  In this example we've declared that $(some) in the query should be
999 -replaced by 'replacement' text, and replacement for $(anotha) will
1000 -be defined in the code before passing on the array pointer to
1001 -the paring function.
1002 +replaced by 'replacement' text, and replacement for $(anotha) will be
1003 +set later in the code, before passing on the array pointer to the
1004 +general parsing function.
1005  
1006  
1007  3.2 typedef size_t (*parsefunc)
1008  
1009  typedef int (*parsefunc)(const char *, size_t, void *);
1010  
1011 -This type definition refers to the function pointer, which is used
1012 -to pass plugin functions into the core parsing subroutine. This definition
1013 -is included to simplify the declaration of the parse_core() function.
1014 +This type definition refers to the function pointer, which is used to
1015 +pass plugin functions into the core parsing subroutine. This
1016 +definition is included to simplify the declaration of the parse_core()
1017 +function.
1018  
1019  
1020  
1021 @@ -230,6 +1022,10 @@
1022         structure of var_data type, which contains variable definition
1023         of a given name. It returns NULL on error or failure.
1024  
1025 +FILES
1026 +
1027 +       authlib/authmysqllib.c
1028 +
1029  
1030  4.2 parse_core
1031  
1032 @@ -285,6 +1081,11 @@
1033  
1034         This function returns -1 if an error has occured and 0 if
1035         everything went good.
1036 +
1037 +FILES
1038 +
1039 +       authlib/authmysqllib.c
1040 +
1041         
1042  4.3 ParsePlugin_counter
1043  
1044 @@ -314,6 +1115,11 @@
1045         This function returns the variable size or -1 if an error
1046         has occured, 0 if everything went good.
1047  
1048 +FILES
1049 +
1050 +       authlib/authmysqllib.c
1051 +
1052 +
1053  4.4 ParsePlugin_builder
1054  
1055  NAME
1056 @@ -333,7 +1139,7 @@
1057         type pointer and refers to the (char *) pointer variable.
1058         After each call it shifts the value of pointer variable (char *)
1059         incrementing it by len bytes. Be careful when using this function
1060 -       - its changes the given pointer value. Always operate on an
1061 +       - it changes the given pointer value. Always operate on an
1062         additional pointer type variable when passing it as the third
1063         argument.
1064  
1065 @@ -342,6 +1148,10 @@
1066         This function returns the variable size or -1 if an error
1067         has occured, 0 if everything went good.
1068  
1069 +FILES
1070 +
1071 +       authlib/authmysqllib.c
1072 +
1073  4.5 parse_string
1074  
1075  NAME
1076 @@ -353,7 +1163,7 @@
1077  
1078  DESCRIPTION
1079  
1080 -       This function parses the string pointed with source according to the
1081 +       This function parses the string pointed to by source according to the
1082         replacement instructions set in var_data array, which is passed with
1083         its pointer vdt. It produces changed string located in newly allocated
1084         memory area.
1085 @@ -377,6 +1187,10 @@
1086         Function returns pointer to the result buffer or NULL
1087         if an error has occured.
1088  
1089 +FILES
1090 +
1091 +       authlib/authmysqllib.c
1092 +
1093  WARNINGS
1094  
1095         This function allocates some amount of memory using standard
1096 @@ -405,6 +1219,10 @@
1097         It returns a pointer to the static buffer which contains
1098         validated password string or NULL if an error has occured.
1099  
1100 +FILES
1101 +
1102 +       authlib/authmysqllib.c
1103 +
1104  
1105  4.7 get_localpart
1106  
1107 @@ -414,20 +1232,28 @@
1108  
1109  SYNOPSIS
1110  
1111 -       static const char *get_localpart (const char *username);
1112 +       static const char *get_localpart (const char *username,
1113 +                                         const char *separators);
1114  
1115  DESCRIPTION
1116  
1117         This function detaches local part of an e-mail address
1118         from string pointed with username and puts it to the
1119         buffer of the fixed length. All necessary cleaning is
1120 -       made on the result string.
1121 +       made on the result string. String pointed with separators
1122 +       refers to a set of characters, which are treated as
1123 +       separation signs between local part and a domain.
1124  
1125  RETURN VALUE
1126  
1127         Pointer to the static buffer containing local part or
1128         NULL if there was some error.
1129  
1130 +FILES
1131 +
1132 +       authlib/authmysqllib.c
1133 +
1134 +
1135  
1136  4.8 get_domain
1137  
1138 @@ -438,24 +1264,67 @@
1139  SYNOPSIS
1140  
1141         static const char *get_domain (const char *username, 
1142 -                                      const char *defdomain);
1143 +                                      const char *defdomain,
1144 +                                      const char *separators);
1145  
1146  DESCRIPTION
1147  
1148          This function detaches domain part of an e-mail address
1149         from string pointed with username and puts it to the
1150         buffer of the fixed length. All necessary cleaning is
1151 -       made on the result string. If function cannot find domain
1152 -       part in the string the string pointed by defdomain is
1153 -       used instead.
1154 +       made on the result string. If the function cannot find a domain
1155 +       part in the string then the string pointed to by defdomain is
1156 +       used instead. If this function cannot find a domain part
1157 +       as well as it cannot obtain the default domain (it's empty string
1158 +       or the defdomain pointer is NULL) the returned result string is an
1159 +       empty string. The string pointed with separators refers to a set
1160 +       of characters, which are treated as separation signs between local
1161 +       part and a domain.
1162  
1163  RETURN VALUE
1164  
1165          Pointer to the static buffer containing domain name or
1166         NULL if there was some error.
1167  
1168 +FILES
1169 +
1170 +       authlib/authmysqllib.c
1171  
1172 -4.9 parse_select_clause
1173 +
1174 +4.9 get_username
1175 +
1176 +NAME
1177 +
1178 +       get_username
1179 +
1180 +SYNOPSIS
1181 +
1182 +       static const char *get_username (const char *username,
1183 +                                        const char *domainname);
1184 +
1185 +DESCRIPTION
1186 +
1187 +       This function concatenates the localpart with a domain name
1188 +       using the @ symbol. If the domain is empty or NULL the result
1189 +       comes without binding symbol.
1190 +
1191 +RETURN VALUE
1192 +
1193 +        Pointer to the static buffer containing output string or
1194 +        NULL if there was some error.
1195 +
1196 +FILES
1197 +
1198 +       authlib/authmysqllib.c
1199 +
1200 +WARNINGS
1201 +
1202 +       This function does not any string cleaning, nor default domain
1203 +       checking. It is designed to work on results of get_localpart() and
1204 +       get_domain().
1205 +
1206 +
1207 +4.10 parse_select_clause
1208  
1209  NAME
1210  
1211 @@ -465,23 +1334,34 @@
1212  
1213         static char *parse_select_clause (const char *clause,
1214                                           const char *username,
1215 -                                         const char *defdomain);
1216 +                                         const char *defdomain
1217 +                                         const char *separators_set);
1218  
1219  DESCRIPTION
1220  
1221         This function is a simple wrapper to the parse_string()
1222         function. It parses a query pointed by caluse. username
1223 -       and defdomain strings are used to replace corresponding
1224 -       substitution strings if present in the query: $(local_part)
1225 -       and $(domain).
1226 +       and defdomain strings are used to create corresponding
1227 +       substitution strings if present in the query: $(local_part),
1228 +       $(domain), and $(username). Note, that username parameter
1229 +       may contain 'user@domain' form here, so the call to
1230 +       get_localpart() and get_domain() function will split it
1231 +       into two parts, then calling get_username() function will join
1232 +       it again using the @ symbol. This trick is wanted as long as
1233 +       we'd like to have possibility to split the local part from the
1234 +       domain by using dynamic symbols set. The separators_set is
1235 +       passed to get_localpart() and get_domain() invocations.
1236         
1237 -
1238  RETURN VALUE
1239  
1240         Same as parse_string().
1241  
1242 +FILES
1243 +
1244 +       authlib/authmysqllib.c
1245  
1246 -4.10 parse_chpass_clause
1247 +
1248 +4.11 parse_chpass_clause
1249  
1250  NAME
1251  
1252 @@ -492,6 +1372,7 @@
1253          static char *parse_chpass_clause (const char *clause,
1254                                            const char *username,
1255                                            const char *defdomain,
1256 +                                         const char *separators_set,
1257                                           const char *newpass,
1258                                           const char *newpass_crypt);
1259                                                                     
1260 @@ -502,12 +1383,115 @@
1261         defdomain, newpass and newpass_crypt strings are used to
1262         replace corresponding substitution strings if present in
1263         the query: $(local_part), $(domain), $(newpass),
1264 -       $(newpass_crypt).
1265 +       $(newpass_crypt). The separators_set is passed to
1266 +       get_localpart() and get_domain() functions as described in the
1267 +       entry for parse_select_clause().
1268  
1269  RETURN VALUE
1270  
1271         Same as parse_string().
1272  
1273 +FILES
1274 +
1275 +       authlib/authmysqllib.c
1276 +
1277 +
1278 +4.12 auth_mysql_on_trigger
1279 +
1280 +NAME
1281 +
1282 +       auth_mysql_on_trigger
1283 +
1284 +SYNOPSIS
1285 +
1286 +       int auth_mysql_on_trigger (const char *clause_name,
1287 +                                  const char *username);
1288 +
1289 +DESCRIPTION
1290 +
1291 +       This function is responsible for calling out the MySQL queries
1292 +       depending on which authentication state was reached.
1293 +
1294 +       The clause_name should contain the name of a clause, which can
1295 +       be found in the configuration file, and the username is simply
1296 +       the string used as username (including the domain if entered).
1297 +
1298 +       This function reads DEFAULT_DOMAIN and USER_DOMAIN_SEPARATORS
1299 +       from the configuration file using read_env(), then it uses
1300 +       parse_select_clause() to parse the query obtained using
1301 +       read_env(clause_name), and then it calls querying subroutines
1302 +       to perform the action.
1303 +
1304 +RETURN VALUE
1305 +
1306 +       This function returns 0 on success and -1 on failure. The
1307 +       query results are simply discarded. If a trigger's clause is
1308 +       not defined in the configuration file the 1 is returned and
1309 +       function silently ends its work.
1310 +
1311 +FILES
1312 +
1313 +       authlib/authmysqllib.c
1314 +
1315 +
1316 +4.13 auth_mysql_on_pass
1317 +
1318 +NAME
1319 +
1320 +       auth_mysql_on_pass
1321 +
1322 +SYNOPSIS
1323 +
1324 +       static int auth_mysql_on_pass(const char *clause,
1325 +                                     struct authmysqluserinfo *authinfo);
1326 +
1327 +DESCRIPTION
1328 +
1329 +       This function is responsible for invoking trigger MySQL
1330 +       clauses whenever user is authenticated or not.
1331 +       This is a stub function, which calls auth_mysql_on_trigger().
1332 +       Firstly, it does a simple checks in authinfo structure --
1333 +       it looks for a valid username field. If username is not set
1334 +       or it's empty the fuction does nothing. This behavior follows
1335 +       the need, that if there wasn't any valid username then we
1336 +       shouldn't touch the database.
1337 +
1338 +RETURN VALUE
1339 +
1340 +       It returns 0 in case everything went fine, -1 if there was some
1341 +       error.
1342 +
1343 +FILES
1344 +
1345 +       authlib/authmysql.c
1346 +
1347 +
1348 +4.14 auth_mysql_checkpassword
1349 +
1350 +NAME
1351 +       auth_mysql_checkpassword
1352 +
1353 +SYNOPSIS
1354 +
1355 +       static int auth_mysql_checkpassword(struct authmysqluserinfo *authinfo,                                                                                                          const char *pass);
1356 +       
1357 +DESCRIPTION
1358 +
1359 +       This function is a wrapper, which checks user's entered
1360 +       password against one found in a database. Function tries to
1361 +       authenticate user against his crypted password and if it's
1362 +       impossible it tries the plain form -- by impossible we mean
1363 +       the authinfo->cryptpw set to NULL.
1364 +
1365 +RETURN VALUE
1366 +
1367 +       Function returns 0 if the password was correct, -1 if user
1368 +       applied bad password of the username wasn't found.
1369 +
1370 +FILES
1371 +
1372 +       authlib/authmysql.c
1373 +
1374  
1375  
1376  
1377 @@ -516,15 +1500,10 @@
1378                      5 Ideas and TODO
1379                     *------------------------
1380  
1381 -- solve problem with fixed buffer length of local part and the domain part
1382 -  strings after split                                          (problem?)
1383  - allow admin to set a group name instead of numerical group id
1384  - allow admin to set a username instead of numerical user id
1385 -
1386 -- add clauses:
1387 -
1388 -  - MYSQL_PRESELECT_CLAUSE (query which comes before MYSQL_SELECT_CLAUSE)
1389 -  - MYSQL_POSTSELECT_CLAUSE (query which comes after MYSQL_SELECT_CLAUSE)
1390 +- put the parsing routines into separate files to make possible of sharing it
1391 +  by more authentication modules
1392  
1393  
1394  
1395 @@ -534,10 +1513,20 @@
1396                      6 Thanks
1397                     *------------------------
1398  
1399 -At the beginning this patch was messy indeed. :> I would like to thank
1400 -Sam Varshavchik for pointing me a lot how to make it more fast and solid.
1401 -I would also thank Philip Hazel, Chris Lightfoot and Mike Bremford which
1402 -by their software capabilities inspired me to write it.
1403 +At the beginning the patch was messy indeed. :> I would like to thank:
1404  
1405 ----------------------------------------------------------------------------
1406 +Sam Varshavchik
1407 +  for pointing me a lot, how to make it more fast and solid
1408 +           
1409 +Philip Hazel, Chris Lightfoot, Mike Bremford
1410 +  which by their software's capabilities inspired me to write it
1411 +               
1412 +Oliver Oblasnik
1413 +  which remainded me to make the documentation more friendly for
1414 +  those who are not programmers and just want to use it
1415 +  
1416 +Jacek Surazski
1417 +  for reviewing this document just before it was published
1418  
1419 +---------------------------------------------------------------------------
1420 +                                 Any comments and suggestions are welcome.
1421 diff -ur courier-imap-1.5.3-orig/authlib/authmysql.c courier-imap-1.5.3/authlib/authmysql.c
1422 --- courier-imap-1.5.3-orig/authlib/authmysql.c Sun Jun 24 01:42:05 2001
1423 +++ courier-imap-1.5.3/authlib/authmysql.c      Sun Oct 13 23:12:06 2002
1424 @@ -19,7 +19,47 @@
1425  #include       "authmysql.h"
1426  #include       "authstaticlist.h"
1427  
1428 -static const char rcsid[]="$Id$";
1429 +static const char rcsid[]="$Id$";
1430 +
1431 +/* siefca@pld.org.pl */
1432 +static int auth_mysql_on_pass(const char *clause, struct authmysqluserinfo *authinfo)
1433 +{
1434 +       if (authinfo->username && *(authinfo->username)!='\0') /* do it if user was found */
1435 +       {
1436 +               if (auth_mysql_on_trigger(clause, authinfo->username))
1437 +               {
1438 +                       return (-1); /* MySQL error or something critical.. */
1439 +               }
1440 +       }
1441 +  
1442 +       return (0);
1443 +}
1444 +
1445 +/* siefca@pld.org.pl */
1446 +static int auth_mysql_checkpassword(struct authmysqluserinfo *authinfo,
1447 +                                   const char *pass)
1448 +{
1449 +       if (authinfo->cryptpw)
1450 +       {
1451 +               if (authcheckpassword(pass,authinfo->cryptpw))
1452 +               {
1453 +                       return (-1); /* User/Password not found. */
1454 +               }
1455 +       }
1456 +       else if (authinfo->clearpw)
1457 +       {
1458 +               if (strcmp(pass, authinfo->clearpw))
1459 +               {
1460 +                       return (-1);
1461 +               }
1462 +       }
1463 +       else
1464 +       {
1465 +               return (-1);
1466 +       }
1467 +    
1468 +       return (0);
1469 +}
1470  
1471  static char *auth_mysql_login(const char *service, char *authdata,
1472         int issession,
1473 @@ -46,26 +86,23 @@
1474                 return (0);
1475         }
1476  
1477 -       if (authinfo->cryptpw)
1478 +       /* siefca@pld.org.pl */
1479 +       if (auth_mysql_checkpassword(authinfo,pass))
1480         {
1481 -               if (authcheckpassword(pass,authinfo->cryptpw))
1482 -               {
1483 +               if (auth_mysql_on_pass("ON_PASS_FAIL_CLAUSE", authinfo))
1484 +                       errno=EACCES;
1485 +               else
1486                         errno=EPERM;
1487 -                       return (0);     /* User/Password not found. */
1488 -               }
1489 -       }
1490 -       else if (authinfo->clearpw)
1491 -       {
1492 -               if (strcmp(pass, authinfo->clearpw))
1493 -               {
1494 -                       errno=EPERM;
1495 -                       return (0);
1496 -               }
1497 +           
1498 +               return(0);
1499         }
1500         else
1501         {
1502 -               errno=EPERM;
1503 -               return (0);             /* Username not found */
1504 +               if (auth_mysql_on_pass("ON_PASS_OK_CLAUSE", authinfo))
1505 +               {
1506 +                       errno=EACCES;
1507 +                       return(0);
1508 +               }
1509         }
1510  
1511         if (callback_func == 0)
1512 @@ -149,26 +186,23 @@
1513                 return (-1);
1514         }
1515  
1516 -       if (authinfo->cryptpw)
1517 +       /* siefca@pld.org.pl */
1518 +       if (auth_mysql_checkpassword(authinfo, pass))
1519         {
1520 -               if (authcheckpassword(pass,authinfo->cryptpw))
1521 -               {
1522 -                       errno=EPERM;
1523 -                       return (-1);    /* User/Password not found. */
1524 -               }
1525 -       }
1526 -       else if (authinfo->clearpw)
1527 -       {
1528 -               if (strcmp(pass, authinfo->clearpw))
1529 -               {
1530 +               if (auth_mysql_on_pass("ON_PASS_FAIL_CLAUSE", authinfo))
1531 +                       errno=EACCES;
1532 +               else
1533                         errno=EPERM;
1534 -                       return (-1);
1535 -               }
1536 +
1537 +               return(-1);
1538         }
1539         else
1540         {
1541 -               errno=EPERM;
1542 -               return (-1);
1543 +               if (auth_mysql_on_pass("ON_PASS_OK_CLAUSE", authinfo))
1544 +               {
1545 +                       errno=EACCES;
1546 +                       return(-1);
1547 +               }
1548         }
1549  
1550         if (auth_mysql_setpass(user, newpass))
1551 @@ -176,6 +210,14 @@
1552                 errno=EPERM;
1553                 return (-1);
1554         }
1555 +       
1556 +       /* siefca@pld.org.pl */
1557 +       if (auth_mysql_on_pass("ON_PASS_CHANGE_CLAUSE", authinfo))
1558 +       {
1559 +           errno=EACCES;
1560 +           return (-1);
1561 +       }
1562 +
1563         return (0);
1564  }
1565  
1566 @@ -314,7 +356,7 @@
1567  #endif
1568  
1569  char *auth_mysql(const char *service, const char *authtype, char *authdata,
1570 -               int issession,
1571 +                int issession,
1572         void (*callback_func)(struct authinfo *, void *), void *callback_arg)
1573  {
1574         if (strcmp(authtype, AUTHTYPE_LOGIN) == 0)
1575 diff -ur courier-imap-1.5.3-orig/authlib/authmysql.h courier-imap-1.5.3/authlib/authmysql.h
1576 --- courier-imap-1.5.3-orig/authlib/authmysql.h Mon Aug  6 05:12:39 2001
1577 +++ courier-imap-1.5.3/authlib/authmysql.h      Sat Sep 28 00:01:07 2002
1578 @@ -21,6 +21,7 @@
1579         } ;
1580  
1581  extern struct authmysqluserinfo *auth_mysql_getuserinfo(const char *);
1582 +extern int auth_mysql_on_trigger (const char *clause_name, const char *username);
1583  extern void auth_mysql_cleanup();
1584  
1585  extern int auth_mysql_setpass(const char *, const char *);
1586 diff -ur courier-imap-1.5.3-orig/authlib/authmysqllib.c courier-imap-1.5.3/authlib/authmysqllib.c
1587 --- courier-imap-1.5.3-orig/authlib/authmysqllib.c      Wed May 29 19:24:03 2002
1588 +++ courier-imap-1.5.3/authlib/authmysqllib.c   Sun Oct 13 22:58:09 2002
1589 @@ -23,6 +23,8 @@
1590  #define                SV_END_MARK             ")"
1591  #define                SV_BEGIN_LEN            ((sizeof(SV_BEGIN_MARK))-1)
1592  #define                SV_END_LEN              ((sizeof(SV_END_MARK))-1)
1593 +#define                DEF_SEPARATORS_SET      "@%"
1594 +
1595  
1596  static const char rcsid[]="$Id$";
1597  
1598 @@ -268,7 +270,7 @@
1599                          SV_BEGIN_MARK
1600                          "%.*s"
1601                          SV_END_MARK
1602 -                        "\n", len, begin);
1603 +                        "\n", (int) len, begin);
1604         
1605         return NULL;
1606  }
1607 @@ -364,14 +366,14 @@
1608                 t_size  = t_end-t_begin+1;/* text field length              */
1609  
1610                 /* work on text */
1611 -               if ( (outfn (t_begin, t_size, result)) == -1 )
1612 +               if ( (outfn (t_begin, t_size, result)))
1613                         return -1;
1614                 
1615                 /* work on variable */
1616                 v_ptr = get_variable (v_begin, v_size, vdt);
1617                 if (!v_ptr) return -1;
1618                 
1619 -               if ( (outfn (v_ptr->value, v_ptr->value_length, result)) == -1 )
1620 +               if ( (outfn (v_ptr->value, v_ptr->value_length, result)))
1621                         return -1;
1622                 
1623                 q = e + 1;
1624 @@ -379,7 +381,7 @@
1625  
1626         /* work on last part of text if any */
1627         if (*q != '\0')
1628 -               if ( (outfn (q, strlen(q), result)) == -1 )
1629 +               if ( (outfn (q, strlen(q), result)))
1630                         return -1;
1631  
1632         return 0;
1633 @@ -426,21 +428,45 @@
1634                 return NULL;
1635         }       
1636         *pass_buf = '\0';
1637 -       
1638 +
1639         return output_buf;
1640  }
1641  
1642  /* siefca@pld.org.pl */
1643 -static const char *get_localpart (const char *username)
1644 +static const char *get_username (const char *username, const char *domainname)
1645 +{
1646 +size_t u_len;
1647 +char *p;
1648 +static char    username_buf[400];
1649 +
1650 +       if (!username || *username == '\0') return NULL;
1651 +       u_len=strlen(username);
1652 +       if (( u_len + (domainname ? strlen(domainname) : 0)
1653 +           ) > 397) return NULL;
1654 +
1655 +       strcpy (username_buf, username);
1656 +       if (domainname && *domainname != '\0')
1657 +         {
1658 +           p = username_buf + u_len;
1659 +           *p='@'; p++;
1660 +           strcpy(p, domainname);
1661 +         }
1662 +       
1663 +       return (username_buf);
1664 +}
1665 +
1666 +/* siefca@pld.org.pl */
1667 +static const char *get_localpart (const char *username, const char *separators)
1668  {
1669  size_t         lbuf    = 0;
1670  const char     *l_end, *p;
1671  char           *q;
1672  static char    localpart_buf[130];
1673         
1674 -       if (!username || *username == '\0')     return NULL;
1675 +       if (!username || *username == '\0' ||
1676 +           !separators || *separators == '\0') return NULL;
1677         
1678 -       p = strchr(username,'@');
1679 +       p = strpbrk (username, separators);
1680         if (p)
1681         {
1682                 if ((p-username) > 128)
1683 @@ -469,21 +495,27 @@
1684  }
1685  
1686  /* siefca@pld.org.pl */
1687 -static const char *get_domain (const char *username, const char *defdomain)
1688 +static const char *get_domain (const char *username, const char *defdomain,
1689 +                              const char *separators)
1690  {
1691  static char    domain_buf[260];
1692  const char     *p;
1693  char           *q;
1694         
1695 -       if (!username || *username == '\0')     return NULL;
1696 -       p = strchr(username,'@');
1697 +       if (!username || *username == '\0' ||
1698 +           !separators || *separators == '\0') return NULL;
1699 +       
1700 +       p = strpbrk (username, separators);
1701         
1702         if (!p || *(p+1) == '\0')
1703         {
1704 -               if (defdomain && *defdomain)
1705 +               if (defdomain && *defdomain != '\0')
1706                         return defdomain;
1707                 else
1708 -                       return NULL;
1709 +                 {
1710 +                   *domain_buf = '\0';
1711 +                   return domain_buf;
1712 +                 }
1713         }
1714  
1715         p++;
1716 @@ -531,20 +563,25 @@
1717  
1718  /* siefca@pld.org.pl */
1719  static char *parse_select_clause (const char *clause, const char *username,
1720 -                                 const char *defdomain)
1721 +                                 const char *defdomain,
1722 +                                 const char *separators_set)
1723  {
1724  static struct var_data vd[]={
1725             {"local_part",      NULL,   sizeof("local_part"),   0},
1726             {"domain",          NULL,   sizeof("domain"),       0},
1727 +           {"username",        NULL,   sizeof("username"),     0},
1728             {NULL,              NULL,   0,                      0}};
1729  
1730 -       if (clause == NULL || *clause == '\0' ||
1731 -           !username || *username == '\0')
1732 +       if (!clause || !username || !separators_set ||
1733 +           *clause == '\0' || *username == '\0' ||
1734 +           *separators_set == '\0')
1735                 return NULL;
1736 -       
1737 -       vd[0].value     = get_localpart (username);
1738 -       vd[1].value     = get_domain (username, defdomain);
1739 -       if (!vd[0].value || !vd[1].value)
1740 +
1741 +       vd[0].value     = get_localpart (username, separators_set);
1742 +       vd[1].value     = get_domain (username, defdomain, separators_set);
1743 +       vd[2].value     = get_username (vd[0].value, vd[1].value);
1744 +
1745 +       if (!vd[0].value || !vd[1].value || !vd[2].value)
1746                 return NULL;
1747         
1748         return (parse_string (clause, vd));
1749 @@ -552,12 +589,15 @@
1750  
1751  /* siefca@pld.org.pl */
1752  static char *parse_chpass_clause (const char *clause, const char *username,
1753 -                                 const char *defdomain, const char *newpass,
1754 +                                 const char *defdomain,
1755 +                                 const char *separators_set,
1756 +                                 const char *newpass,
1757                                   const char *newpass_crypt)
1758  {
1759  static struct var_data vd[]={
1760             {"local_part",      NULL,   sizeof("local_part"),           0},
1761             {"domain",          NULL,   sizeof("domain"),               0},
1762 +           {"username",        NULL,   sizeof("username"),             0},
1763             {"newpass",         NULL,   sizeof("newpass"),              0},
1764             {"newpass_crypt",   NULL,   sizeof("newpass_crypt"),        0},
1765             {NULL,              NULL,   0,                              0}};
1766 @@ -565,19 +605,81 @@
1767         if (clause == NULL || *clause == '\0'           ||
1768             !username || *username == '\0'              ||
1769             !newpass || *newpass == '\0'                ||
1770 +           !separators_set || *separators_set == '\0'  ||
1771             !newpass_crypt || *newpass_crypt == '\0')   return NULL;
1772  
1773 -       vd[0].value     = get_localpart (username);
1774 -       vd[1].value     = get_domain (username, defdomain);
1775 -       vd[2].value     = validate_password (newpass);
1776 -       vd[3].value     = validate_password (newpass_crypt);
1777 +       vd[0].value     = get_localpart (username, separators_set);
1778 +       vd[1].value     = get_domain (username, defdomain, separators_set);
1779 +       vd[3].value     = get_username (vd[0].value, vd[1].value);
1780 +       vd[4].value     = validate_password (newpass);
1781 +       vd[5].value     = validate_password (newpass_crypt);
1782         
1783         if (!vd[0].value || !vd[1].value ||
1784 -           !vd[2].value || !vd[3].value)       return NULL;
1785 +           !vd[2].value || !vd[3].value ||
1786 +           !vd[4].value || !vd[5].value)       return NULL;
1787  
1788         return (parse_string (clause, vd));
1789  }
1790  
1791 +/* siefca@pld.org.pl */
1792 +int auth_mysql_on_trigger (const char *clause_name, const char *username)
1793 +{
1794 +char           *querybuf       =NULL;
1795 +const char     *separators_set =NULL,
1796 +               *defdomain      =NULL,
1797 +               *on_clause      =NULL;
1798 +MYSQL_RES      *result;
1799 +
1800 +       if (!clause_name || *clause_name == '\0')
1801 +         return (-1);
1802 +       
1803 +       on_clause = read_env (clause_name);
1804 +       if (!on_clause || *on_clause == '\0')
1805 +         return (0); /* ok! not in use */
1806 +
1807 +       defdomain = read_env ("DEFAULT_DOMAIN");        
1808 +       separators_set = read_env ("USER_DOMAIN_SEPARATORS");
1809 +       if (!defdomain) defdomain = "";
1810 +       if (!separators_set || *separators_set == '\0')
1811 +               separators_set = DEF_SEPARATORS_SET;
1812 +       
1813 +       querybuf = parse_select_clause (on_clause,
1814 +                                       username,
1815 +                                       defdomain,
1816 +                                       separators_set);
1817 +       
1818 +       if (!querybuf)  return (-1);
1819 +
1820 +       if (mysql_query (mysql, querybuf))
1821 +       {
1822 +               /* <o.blasnik@nextra.de> */
1823 +
1824 +               auth_mysql_cleanup();
1825 +
1826 +               if (do_connect())
1827 +               {
1828 +                       free(querybuf);
1829 +                       return (-1);
1830 +               }
1831 +
1832 +               if (mysql_query (mysql, querybuf))
1833 +               {
1834 +                       free(querybuf);
1835 +                       auth_mysql_cleanup();
1836 +                       /* Server went down, that's OK,
1837 +                       ** try again next time.
1838 +                       */
1839 +                       return (-1);
1840 +               }
1841 +       }
1842 +       free(querybuf);
1843 +       result = mysql_store_result(mysql);
1844 +       if (result) mysql_free_result(result);
1845 +       
1846 +       return (0);
1847 +}
1848 +
1849 +
1850  struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username)
1851  {
1852  const char *user_table =NULL;
1853 @@ -596,6 +698,7 @@
1854             *gid_field          =NULL,
1855             *quota_field        =NULL,
1856             *where_clause       =NULL,
1857 +           *separators_set     =NULL,
1858             *select_clause      =NULL; /* siefca@pld.org.pl */
1859  
1860  static const char query[]=
1861 @@ -704,7 +807,15 @@
1862         else
1863         {
1864                 /* siefca@pld.org.pl */
1865 -               querybuf=parse_select_clause (select_clause, username, defdomain);
1866 +               separators_set = read_env ("USER_DOMAIN_SEPARATORS");
1867 +
1868 +               if (!separators_set || *separators_set == '\0')
1869 +                       separators_set = DEF_SEPARATORS_SET;
1870 +
1871 +               querybuf = parse_select_clause (select_clause,
1872 +                                               username,
1873 +                                               defdomain,
1874 +                                               separators_set);
1875                 if (!querybuf) return 0;
1876         }
1877  
1878 @@ -788,6 +899,7 @@
1879                     *where_clause       =NULL,
1880                     *user_table         =NULL,
1881                     *login_field        =NULL,
1882 +                   *separators_set     =NULL,  
1883                     *chpass_clause      =NULL; /* siefca@pld.org.pl */
1884  
1885         if (!mysql)
1886 @@ -837,13 +949,18 @@
1887         }
1888         else
1889         {
1890 +                separators_set = read_env ("USER_DOMAIN_SEPARATORS");
1891 +               
1892 +               if (!separators_set || *separators_set == '\0')
1893 +                       separators_set = DEF_SEPARATORS_SET;
1894 +               
1895                 sql_buf=parse_chpass_clause(chpass_clause,
1896                                             user,
1897                                             defdomain,
1898 +                                           separators_set,
1899                                             pass,
1900                                             newpass_crypt_ptr);
1901         }
1902 -       
1903  
1904         if (!sql_buf)
1905         {
1906 diff -ur courier-imap-1.5.3-orig/authlib/authmysqlrc courier-imap-1.5.3/authlib/authmysqlrc
1907 --- courier-imap-1.5.3-orig/authlib/authmysqlrc Thu Apr  4 06:36:29 2002
1908 +++ courier-imap-1.5.3/authlib/authmysqlrc      Mon Oct 14 00:02:59 2002
1909 @@ -1,4 +1,4 @@
1910 -##VERSION: $Id$
1911 +##VERSION: $Id$
1912  #
1913  # Copyright 2000 Double Precision, Inc.  See COPYING for
1914  # distribution information.
1915 @@ -141,65 +141,99 @@
1916  #
1917  # MYSQL_WHERE_CLAUSE   server='mailhost.example.com'
1918  
1919 -##NAME: MYSQL_SELECT_CLAUSE:0
1920 -#
1921 -# (EXPERIMENTAL)
1922 -# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database,
1923 -# which is structuraly different from proposed. The fixed string will
1924 -# be used to do a SELECT operation on database, which should return fields
1925 -# in order specified bellow:
1926 +##NAME: USER_DOMAIN_SEPARATORS:0
1927  #
1928 -# username, cryptpw, uid, gid, clearpw, home, maildir, quota, fullname
1929 +# This is optional. Using this option you can set the set of characters
1930 +# which are treated as separators when splitting entered username into the
1931 +# local part and the domain name. If it's not set the defaults @% are used,
1932 +# so the user can authenticate using user@domain or user%domain form.
1933 +# See README.authmysql.myownquery for more information
1934  #
1935 -# Enabling this option causes ignorance of any other field-related
1936 -# options, excluding default domain.
1937 +# USER_DOMAIN_SEPARATORS       @%+
1938 +
1939 +##NAME: MYSQL_SELECT_CLAUSE:0
1940  #
1941 -# There are two variables, which you can use. Substitution will be made
1942 -# for them, so you can put entered username (local part) and domain name
1943 -# in the right place of your query. These variables are:
1944 -#              $(local_part) and $(domain)
1945 +# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database,
1946 +# which is structuraly different from proposed. You can type here your MySQL
1947 +# query, which will be used to fetch user's credentials, and which should
1948 +# return fields in order specified bellow:
1949 +#
1950 +# username, cryptpw, clearpw, uid, gid, home, maildir, quota, fullname
1951 +#
1952 +# Enabling this option causes ignorance of any other field-related options.
1953 +#
1954 +# There also are variables, which you can use. Substitution will be made
1955 +# for them, so you can pass currently entered username and a domain name
1956 +# up to the right place within your query. These variables are:
1957 +# $(local_part) , $(domain) , $(username)
1958  #
1959  # If a $(domain) is empty (not given by the remote user) the default domain
1960 -# name is used in its place.
1961 -#
1962 -# This example is a little bit modified adaptation of vmail-sql
1963 -# database scheme:
1964 -#
1965 -# MYSQL_SELECT_CLAUSE  SELECT popbox.local_part,                       \
1966 -#                      CONCAT('{MD5}', popbox.password_hash),          \
1967 -#                      popbox.clearpw,                                 \
1968 -#                      domain.uid,                                     \
1969 -#                      domain.gid,                                     \
1970 -#                      CONCAT(domain.path, '/', popbox.mbox_name),     \
1971 -#                      '',                                             \
1972 -#                      domain.quota,                                   \
1973 -#                      '',                                             \
1974 -#                      FROM popbox, domain                             \
1975 -#                      WHERE popbox.local_part = '$(local_part)'       \
1976 -#                      AND popbox.domain_name = '$(domain)'            \
1977 -#                      AND popbox.domain_name = domain.domain_name
1978 -#
1979 +# name is used in its place. $(username) is a local part concatenated with
1980 +# domain name using symbol defined in USER_DOMAIN_CONCAT or '@' if this option
1981 +# is not set.
1982 +# See README.authmysql.myownquery for more information
1983 +#
1984 +# MYSQL_SELECT_CLAUSE SELECT                                   \
1985 +# users.username, users.cryptpw, users.clearpw,                        \
1986 +# domains.uid, domains.gid,                                    \
1987 +# CONCAT_WS('/',domains.path_prefix,users.mailbox_path),       \
1988 +# '', domains.quota, ''                                                \
1989 +# FROM users, domains                                          \
1990 +#  WHERE domains.domain_name='$(domain)'                       \
1991 +#    AND users.username='$(local_part)'                                \
1992 +#    AND domains.domain_name=users.domain_name
1993 +
1994  ##NAME: MYSQL_CHPASS_CLAUSE:0
1995  #
1996 -# (EXPERIMENTAL)
1997  # This is optional, MYSQL_CHPASS_CLAUSE can be set when you have a database,
1998 -# which is structuraly different from proposed. The fixed string will
1999 -# be used to do an UPDATE operation on database. In other words, it is
2000 -# used, when changing password.
2001 +# which is structuraly different from proposed. You can use it to set up
2002 +# a MySQL query used to change user's password.
2003  #
2004  # There are four variables, which you can use. Substitution will be made
2005 -# for them, so you can put entered username (local part) and domain name
2006 -# in the right place of your query. There variables are:
2007 -#      $(local_part) , $(domain) , $(newpass) , $(newpass_crypt)
2008 +# for them, so you can put the currently entered username and the domain name
2009 +# in the right place of your query. These variables are:
2010 +# $(local_part) , $(domain) , $(username) , $(newpass) , $(newpass_crypt)
2011  #
2012  # If a $(domain) is empty (not given by the remote user) the default domain
2013 -# name is used in its place.
2014 -# $(newpass) contains plain password
2015 -# $(newpass_crypt) contains its crypted form
2016 -#
2017 -# MYSQL_CHPASS_CLAUSE  UPDATE  popbox                                  \
2018 -#                      SET     clearpw='$(newpass)',                   \
2019 -#                              password_hash='$(newpass_crypt)'        \
2020 -#                      WHERE   local_part='$(local_part)'              \
2021 -#                      AND     domain_name='$(domain)'
2022 -#
2023 +# name is used in its place. $(newpass) contains plain password and
2024 +# $(newpass_crypt) contains its crypted form.
2025 +# See README.authmysql.myownquery for more information
2026 +#
2027 +# MYSQL_CHPASS_CLAUSE UPDATE users             \
2028 +# SET clearpw='$(newpass)',                    \
2029 +#     cryptpw='$(newpass_crypt)'               \
2030 +# WHERE username='$(local_part)'               \
2031 +# AND domain_name='$(domain)'
2032 +
2033 +##NAME: ON_PASS_OK_CLAUSE:0
2034 +#
2035 +# This is optional, ON_PASS_OK_CLAUSE is a trigger -- the query
2036 +# is performed each time user has successfuly logged in.
2037 +# See README.authmysql.myownquery for more information
2038 +#
2039 +# ON_PASS_OK_CLAUSE            UPDATE users                            \
2040 +#                              SET last_ok=CURRENT_TIMESTAMP           \
2041 +#                              WHERE username='$(local_part)'          \
2042 +#                              AND domain_name='$(domain)'
2043 +
2044 +##NAME: ON_PASS_FAIL_CLAUSE:0
2045 +#
2046 +# This is optional, ON_PASS_FAIL_CLAUSE is a trigger -- the query
2047 +# is performed each time user has NOT logged in, cause of bad password.
2048 +# See README.authmysql.myownquery for more information
2049 +#
2050 +# ON_PASS_FAIL_CLAUSE          UPDATE users                            \
2051 +#                              SET last_fail=CURRENT_TIMESTAMP         \
2052 +#                              WHERE username='$(local_part)'          \
2053 +#                              AND domain_name='$(domain)'
2054 +
2055 +##NAME: ON_PASS_CHANGE_CLAUSE:0
2056 +#
2057 +# This is optional, ON_PASS_CHANGE_CLAUSE is a trigger -- the query
2058 +# is performed each time user has successfuly changed his password.
2059 +# See README.authmysql.myownquery for more information
2060 +#
2061 +# ON_PASS_CHANGE_CLAUSE                UPDATE users                            \
2062 +#                              SET pw_change=CURRENT_TIMESTAMP         \
2063 +#                              WHERE username='$(local_part)'          \
2064 +#                              AND domain_name='$(domain)'
This page took 0.431609 seconds and 2 git commands to generate.