]> git.pld-linux.org Git - packages/courier-imap.git/blame - courier-imap-myownquery-fix.patch
- add ssl init script
[packages/courier-imap.git] / courier-imap-myownquery-fix.patch
CommitLineData
0d6c173a 1diff -ur courier-imap-1.4.2.orig/authlib/README.authmysql.myownquery courier-imap-1.4.2/authlib/README.authmysql.myownquery
2--- courier-imap-1.4.2.orig/authlib/README.authmysql.myownquery Tue Jan 8 06:01:22 2002
3+++ courier-imap-1.4.2/authlib/README.authmysql.myownquery Thu Feb 14 00:14:33 2002
4@@ -2,12 +2,13 @@
5
6
7
8- Developer Notes for courier-imap-myownquery.patch
9+ Developer Notes and Usage Instructions
10+ of courier-imap-authmysql-myownquery
11
12
13
14
15- document version: 1.03
16+ document version: 1.20
17 author: Pawel Wilk
18
19
20@@ -24,57 +25,739 @@
21
22
23
24+PART I - Usage Instructions
25
26-0 What's that?
27+ 1 What's that?
28+
29+ 2 When I'll need it?
30+
31+ 3 How does it work?
32+ 3.1 configuration variables
33+ 3.2 queries
34+ 3.3 substitutions
35+ 3.4 triggers
36+
37+ 4 Examples of usage
38+ 4.1 corporate mail system
39+ 4.1.1 database structure
40+ 4.1.2 authdaemon configuration
41+ 4.2 virtual mail domains provider
42+ 4.2.1 database structure
43+ 4.2.2 authdaemon configuration
44+
45+PART II - Developer Notes
46+
47+ 1 Modifications overview
48+
49+ 2 Definitions
50+
51+ 3 New data types
52+ 3.1 struct var_data
53+ 3.2 typedef size_t (*parsefunc)
54+
55+ 4 New functions
56+ 4.1 get_variable
57+ 4.2 parse_core
58+ 4.3 ParsePlugin_counter
59+ 4.4 ParsePlugin_builder
60+ 4.5 parse_string
61+ 4.6 validate_password
62+ 4.7 get_localpart
63+ 4.8 get_domain
64+ 4.9 get_username
65+ 4.10 parse_select_clause
66+ 4.11 parse_chpass_clause
67+ 4.12 auth_mysql_on_trigger
68
69-1 Modifications overview
70+ 5 Ideas and TODO
71
72-2 Definitions
73+ 6 Thanks
74
75-3 New data types
76- 3.1 struct var_data
77- 3.2 typedef size_t (*parsefunc)
78-
79-4 New functions
80- 4.1 get_variable
81- 4.2 parse_core
82- 4.3 ParsePlugin_counter
83- 4.4 ParsePlugin_builder
84- 4.5 parse_string
85- 4.6 validate_password
86- 4.7 get_localpart
87- 4.8 get_domain
88- 4.9 parse_select_clause
89- 4.10 parse_chpass_clause
90-
91-5 Ideas and TODO
92-
93-6 Thanks
94
95
96
97+//////////////////////////////// PART I - Usage ///////////////////////////////
98
99 *-----------------------
100- 0 What's that?
101+ 1 What's that?
102 *-----------------------
103
104-Courier-imap-myownquery.patch allows administrator to set own MySQL queries
105-used by authdaemon to authenticate user (including fetchig credentials) and to
106-change user's password. It allows to construct SELECT or UPDATE clause in the
107-configuration file (authmysqlrc) by adding two new configuration variables:
108-MYSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE. It may be useful in the mail
109-environments where there is such a need to have different database structure
110-and/or tables scheme than expected by authmysql module.
111+Courier-imap-myownquery feature allows administrator to set his own MySQL
112+queries used by authdaemon to authenticate a user (including fetchig his
113+credentials) and to change the user's password. It allows to construct
114+SELECT and UPDATE clause in the configuration file (authmysqlrc) using
115+new configuration variables. It may be useful in the mail environments where
116+there is such a need to have different database structure and/or tables
117+scheme than expected by authmysql module.
118
119 It also implements a small parsing engine for substitution variables which
120-may appear in the clauses and are used to put informations like username
121-or domain into the right place of a query.
122+may appear in the clauses and are used to put information like a username
123+or a domain into the right place within the query.
124
125-This patch was created using `diff -Nur` on courier-imap-1.3.12 source.
126
127
128
129
130+ *-----------------------
131+ 2 When I'll need it?
132+ *-----------------------
133+
134+ o When you already have some MySQL database filled up with the data
135+ and there is no chance to change the whole structure to make it
136+ working with standard authmysql table. Typical situation is when all the
137+ data required to authenticate a user is arranged in more than one table.
138+
139+ o When you have some great idea how to make the database structure
140+ more efficient due to your needs and your requirements.
141+
142+ o When doing something 'by-myself' is in your style and you just want
143+ to create your own database, just to feel the pleasure of doing
144+ something original. :)
145+
146+
147+
148+
149+
150+ *-----------------------
151+ 3 How does it work?
152+ *-----------------------
153+
154+There are three things which the feature concerns:
155+
156+- fetching clauses from the configuration file
157+- doing substitution replacements inside of clauses
158+- passing prepared query on to the mysql interface funtions
159+
160+3.1 configuration options
161+
162+You can apply your own MySQL queries using a set of the configuration options.
163+The options you'll need to make the authmysql your slave are:
164+
165+MYSQL_SERVER (required)
166+MYSQL_USERNAME (required)
167+MYSQL_PASSWORD (required)
168+
169+ The server name, userid, and password used to log in.
170+
171+MYSQL_DATABASE (required)
172+
173+ The name of the MySQL database we will open.
174+
175+DEFAULT_DOMAIN (optional)
176+
177+ If DEFAULT_DOMAIN is defined, and someone tries to log in as
178+ 'user', we will look up 'user@DEFAULT_DOMAIN' instead.
179+
180+USER_DOMAIN_CONCAT (optional)
181+
182+ The USER_DOMAIN_CONCAT defines a character(s) used to
183+ concatenate a local part and a domain while parsing
184+ the $(username) substitution variable (see section 3.3
185+ for more info). If it's not defined the @ sign is assumed.
186+
187+USER_DOMAIN_SEPARATORS (optional)
188+
189+ This may contain the set of characters used by parsing
190+ routines to split local part of the virtual mailbox name
191+ from the part which describes the domain name. If it's not
192+ defined the set containing @% is assumed, so the user can
193+ enter either: user@domain or user%domain when he wants to be
194+ authenticated.
195+
196+MYSQL_SELECT_CLAUSE (required)
197+MYSQL_CHPASS_CLAUSE (required under some circumstances)
198+
199+ These are the major options you should use. See 3.2 section
200+ for more info.
201+
202+MYSQL_ONSUCCESS_CLAUSE (optional)
203+MYSQL_ONFAIL_CLAUSE (optional)
204+
205+ These are used to do a MySQL query whether user has passed
206+ the authentication verification (MYSQL_ONSUCCESS_CLAUSE)
207+ or there was the authentication failure (MYSQL_ONFAIL_CLAUSE).
208+ Query results have no meaning. You can use the same
209+ substitution variables in your query as with
210+ MYSQL_SELECT_CLAUSE. See 3.4 section for more info.
211+
212+The options which have no effect, and may be safetly left blank are:
213+
214+MYSQL_USER_TABLE
215+MYSQL_CRYPT_PWFIELD
216+MYSQL_CLEAR_PWFIELD
217+MYSQL_UID_FIELD
218+MYSQL_GID_FIELD
219+MYSQL_LOGIN_FIELD
220+MYSQL_HOME_FIELD
221+MYSQL_NAME_FIELD
222+MYSQL_MAILDIR_FIELD
223+MYSQL_QUOTA_FIELD
224+MYSQL_WHERE_CLAUSE
225+
226+3.2 queries
227+
228+The feature adds two configuration options (clauses), which are parsed first,
229+and then applied as MySQL queries to MySQL interface routines. These options
230+are: MYSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE. After each option a number of
231+spaces and/or tabs is allowed, and then MySQL query is expected. For better
232+look, your queries can have line breaks. Each line break should be preceded by
233+the backslash sign. Look into examples chapter (4) to see how it should look
234+like. First clause is used to authenticate a user, and the second to change his
235+password.
236+
237+You should note that a query identified by MYSQL_SELECT_CLAUSE should return
238+fixed number (9) of fields and each field should match the variable expected
239+by authentication routines. These fields are:
240+
241+* username - which is the currently logged user's username (or the
242+ username with domain if you want it)
243+
244+* cryptpw - which is the user's crypted password
245+
246+* clearpw - which is the user's plaintext password
247+
248+* uid - which is a numerical UID value used as a process's UID when
249+ accessing the mailbox directory
250+
251+* gid - as above, but refers to GID
252+
253+* home - which contains full path to the user's home directory
254+
255+maildir - which contains the directory name inside the user's home
256+ which is treated as INBOX folder when accessing mailbox
257+ - if it's empty then the 'Maildir' string is used
258+
259+quota - which describes a quota size for the mailbox
260+
261+fullname - which may contain the user's fullname
262+
263+(The fields marked by the asterix sign are required and cannot have an
264+ empty results)
265+
266+So, the typical query clause may start with:
267+
268+MYSQL_SELECT_CLAUSE SELECT \
269+ users.username, \
270+ users.cryptpw, \
271+ users.clearpw, \
272+ domains.uid, \
273+ domains.gid, \
274+ users.mailbox_path) \
275+ '' \
276+ domains.quota, \
277+ '' \
278+...
279+
280+Note that in this short example we're assuming that we have two tables
281+(users and domains) and INBOX path is always called 'Maildir' and
282+we're not using the fullname field (the query will always return an empty
283+string in its place).
284+
285+Also note that you may discard one of the password fields if you don't want
286+to use an authentication mechanism, which needs it. For example, if you don't
287+want to use MD5-CRAM you may put '' into the place of clearpw (because, for
288+example you're in paranoid mode and you don't even want to keep plain passwords
289+in the database:).
290+
291+3.3 substitutions
292+
293+Substitutions are strings, which may appear in your query, and which have a
294+special meaning. You can also call them substitution variables. If substitution
295+variable is known for a clause context then it is parsed. If it isn't known the
296+error is generated. In the default compilation of authmysql module any
297+substitution variable is declared inside of two substrings - the first is a
298+dollar sign concatenated with opening parenthesis, and the second is a closing
299+parenthesis sign. First symbol identifies beginning of a substitution variable,
300+and the second closes it. The string between the beginning and the closing
301+symbol is called substitution variable's name.
302+
303+When, as I said before, the name is known to the parsing routine the
304+substitution is made and the proper value appears in place of the substitution
305+variable, while passing on the query for later processing.
306+
307+Allowed substitution variables:
308+
309+context: MYSQL_SELECT_CLAUSE, MYSQL_ONFAIL_CLAUSE, MYSQL_ONSUCCESS_CLAUSE
310+
311+$(local_part) will be replaced by currently verified user's username
312+ (without the domain part)
313+
314+$(domain) will be replaced by currently verified user's domain
315+ name (if present, or if not present but the
316+ DEFAULT_DOMAIN was used) or by the empty, zero-length
317+ string if the domain cannot be obtained
318+
319+$(username) will be replaced by currently verified user's username
320+ concatenated with the given domain name using symbol
321+ defined by USER_DOMAIN_CONCAT - if the domiain name
322+ cannot be obtained (even by looking up DEFAULT_DOMAIN)
323+ the separation sign will not appear and only the given
324+ username will be presented
325+
326+context: MYSQL_CHPASS_CLAUSE
327+
328+$(local_part) will be replaced by currently verified user's username
329+ (without the domain part)
330+
331+$(domain) will be replaced by currently verified user's domain
332+ name (if present, or if not present but the
333+ DEFAULT_DOMAIN was used) or by the empty, zero-length
334+ string if the domain cannot be obtained
335+
336+$(username) will be replaced by currently verified user's username
337+ concatenated with the given domain name using symbol
338+ defined by USER_DOMAIN_CONCAT - if the domiain name
339+ cannot be obtained (even by looking up DEFAULT_DOMAIN)
340+ the separation sign will not appear and only the given
341+ username will be presented
342+
343+$(newpass) will be replaced by currently authenticated user's
344+ new password to set up (plaintext password)
345+
346+$(newpass_crypt) will be replaced by currently authenticated user's
347+ new password to set up (MD5 form created from entered
348+ plain form)
349+
350+3.4 triggers
351+
352+Triggers are MySQL queries, which are performed depending on authentication
353+state. Currently, there are two triggers which you may use. First is called
354+MYSQL_ONSUCCESS_CLAUSE and it is performed when the authentication succeedes.
355+The second is called MYSQL_ONFAIL_CLAUSE and has the reverse meaning. You can
356+declare triggers in the authmysqlrc configuration file. They can be used to
357+arrange some logging facility in the database or just to keep last times
358+of the successful/failed login tries. The typical trigger, which puts last
359+login date into the users' table can look like this:
360+
361+MYSQL_ONSUCCESS_CLAUSE UPDATE users SET last_login=CURRENT_TIMESTAMP \
362+ WHERE username='$(username)';
363+
364+or, if you would like to know about last login failure for users you can try:
365+
366+MYSQL_ONFAIL_CLAUSE UPDATE users SET last_bad_login=CURRENT_TIMESTAMP \
367+ WHERE username='$(username)';
368+
369+Note, that YOU CAN use the triggers even if you aren't using
370+MYSQL_SELECT_CLAUSE. Also note, that there is such a possibility that ONFAIL
371+trigger may be performed without a proper username. Take it into consideration
372+when creating queries to avoid messy data on INSERT operations.
373+
374+
375+
376+
377+
378+
379+ *-----------------------
380+ 4 Examples of usage
381+ *-----------------------
382+
383+The "ownquery" feature gives you possibility to adapt an authentication query
384+to the database. So the first thing you have to do is to design the database
385+structure you need, whithout being grieved at what structure authentication
386+routines like. You have to take care about four essential things:
387+
388+ o The database
389+
390+ o The users' data in the database
391+
392+ o The proper directories for keeping virtual mailboxes and a system user
393+ which can read and write them
394+
395+ o The proper MySQL queries in your authmysqlrc configuration file
396+
397+4.1 corporate mail system
398+
399+This example is concerned about a corporate mail system with a small
400+count of served virtual domains. The database scheme was derived from tpop3d
401+documentation and modified a bit.
402+
403+4.1.1 database structure
404+
405+Our goal here is to separate the data responsible for keeping mailbox
406+credentials from the data, which describes a domain.
407+
408+Let's create some tables for our example, filled up with an example data:
409+
410+table: domains
411+
412+purpose: associates virtual domain with domain name and informations
413+ necessary to access mailboxes withing the domain
414+
415+fields: domain_name - fully qualified domain name
416+ path_prefix - absolute pathname which points to
417+ a directory where domain's mailboxes
418+ are located
419+ quota - default quota for each mailbox
420+ uid - UID used to work on mailboxes
421+ gid - GID used to work on mailboxes
422+
423+ +----------------+-------------+-----+-----+----------+
424+ | domain_name | path_prefix | uid | gid | quota |
425+ +----------------+-------------+-----+-----+----------+
426+ | exampledom.com | /var/mail/x | 555 | 555 | 10000000 |
427+ | pld.org.pl | /var/mail/p | 556 | 556 | 20000000 |
428+ | pld.net.pl | /var/mail/p | 556 | 556 | 20000000 |
429+ +----------------+-------------+-----+-----+----------+
430+
431+table: users
432+
433+purpose: associates virtual mailbox with user and domain name,
434+ and with informations necessary to access mailbox
435+
436+fields: username - user login name (mailbox name)
437+ domain_name - fully qualified domain name
438+ mailbox_path - relative pathname for mailbox
439+ (will be appended to the path_prefix
440+ from domain_auth table to specify
441+ user's mailbox location)
442+ cryptpw - crypted password
443+ plainpw - plaintext password
444+
445+ +----------+----------------+--------------+------------+--------+
446+ | username | domain_name | mailbox_path | cryptpw | plainpw |
447+ +----------+----------------+--------------+-----------+---------+
448+ | siefca | pld.org.pl | s/siefca | $1$fs45.. | dupa.8 |
449+ | siefca | pld.net.pl | s/siefca | $1$fs45.. | dupa.8 |
450+ | f00bar | exampledom.com | foobar | $1$g44w.. | secret |
451+ +----------+----------------+--------------+-----------+---------+
452+
453+Using MySQL monitor you can create these tables entering CREATE sequences.
454+Be sure to connect to the database using administrative MySQL account
455+(usualy: mysql -u mysql -p).
456+
457+--------------------- cut here
458+
459+# Create the database called vmail.
460+
461+CREATE database vmail;
462+
463+# Create an example MySQL user, which can read, write and delete data from
464+# vmail database. Username: vuser Password: secret_password
465+
466+GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.*
467+ TO vuser@localhost
468+ IDENTIFIED BY 'secret_password';
469+
470+FLUSH PRIVILEGES;
471+
472+# Create the tables.
473+
474+use vmail;
475+
476+CREATE TABLE domains (
477+ domain_name char(255) DEFAULT '',
478+ path_prefix char(255) DEFAULT '' NOT NULL,
479+ uid int(10) unsigned DEFAULT '15000' NOT NULL,
480+ gid int(10) unsigned DEFAULT '15000' NOT NULL,
481+ quota char(255) DEFAULT '2000000' NOT NULL,
482+ KEY domain_name (domain_name(255))
483+ );
484+
485+CREATE TABLE users (
486+ username char(128) DEFAULT '' NOT NULL,
487+ domain_name char(255) DEFAULT '',
488+ mailbox_path char(255) DEFAULT '' NOT NULL,
489+ cryptpw char(128) DEFAULT '' NOT NULL,
490+ clearpw char(128) DEFAULT '' NOT NULL,
491+ KEY username (username(128))
492+ );
493+
494+# Create an example virtual domain entry
495+# name : exampledom.com
496+# uid : 555
497+# gid : 555
498+# path : /var/mail/x
499+# quota : 10 Megs per mailbox
500+
501+INSERT INTO domains VALUES ('exampledom.com', '/var/mail/x', 555, 555,
502+ '10000000');
503+
504+# Create an example virtual user entry
505+# username : siefca
506+# domain name : exampledom.com
507+# cryptpw : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/
508+# clearpw : dupa.8
509+# mailbox path : s/siefca
510+
511+INSERT INTO users VALUES ('siefca', 'exampledom.com', 's/siefca',
512+ '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/',
513+ 'dupa.8');
514+
515+--------------------- cut here
516+
517+Note: If you would like to have your passwords more safe then just omit the
518+ clearpw column and put '' into the config-query in its place while
519+ doing SELECT on a database. But be ware - you'll be unable to use
520+ authentication methods which needs it, like MD5_CRAM.
521+
522+4.1.2 authdaemon configuration
523+
524+When our database is ready we can set up the configuration. :-) Go to
525+authmysqlrc file and edit it.
526+
527+At the beginning we should take care about general informations, which
528+are identifying our database:
529+
530+MYSQL_SERVER localhost
531+MYSQL_USERNAME vuser
532+MYSQL_PASSWORD secret_password
533+MYSQL_DATABASE vmail
534+
535+Then we should add a clause responsible for authenticating user and
536+fetching credentials:
537+
538+DEFAULT_DOMAIN exampledom.com
539+
540+MYSQL_SELECT_CLAUSE SELECT \
541+ users.username, users.cryptpw, users.clearpw, \
542+ domains.uid, domains.gid, \
543+ CONCAT_WS('/',domains.path_prefix,users.mailbox_path), \
544+ '', domains.quota, '' \
545+ FROM users, domains \
546+ WHERE domains.domain_name='$(domain)' \
547+ AND users.username='$(local_part)' \
548+ AND domains.domain_name=users.domain_name
549+
550+
551+Note the '' in the place of field which tells where user's INBOX resides
552+and in place of realname field. You should use '' if you want to put an empty
553+value as a query result for some field.
554+
555+We also should add some configuration for changing user's password:
556+
557+MYSQL_CHPASS_CLAUSE UPDATE \
558+ users \
559+ SET clearpw='$(newpass)', \
560+ cryptpw='$(newpass_crypt)' \
561+ WHERE username='$(local_part)' \
562+ AND domain_name='$(domain)'
563+
564+Last things to have sure...
565+Create a system user/group and a proper directory structure. In our example:
566+
567+groupadd -g 555 xdomain
568+useradd -u 555 -g 555 xdomain
569+mkdir -p /var/mail/x/s/siefca
570+chmod -R 0770 /var/mail/x
571+maildirmake /var/mail/x/s/siefca/Maildir
572+chown -R xdomain.xdomain /var/mail/x
573+
574+Now, restart the authdaemon and see if it works. Try: telnet 0 pop3
575+
576+and type:
577+
578+USER siefca [ENTER]
579+PASS dupa.8 [ENTER]
580+
581+You should get Ok response. ;)
582+
583+4.2 virtual mail domains provider
584+
585+Let's consider more complicated database scheme, where is a need to
586+associate a lot of information with the domain name, including registrant
587+information, owner, etc. That implies data separation between domain name,
588+user and domain additional informations (which are unwanted when
589+authentication process takes place). By the proper data separation I mean
590+avoiding unwanted redundancy in the database.
591+
592+Currently applied example doesn't care about the update password problem.
593+This is due to current abilities of MySQL and authdaemon (authmysql).
594+MySQL doesn't support subsequent SELECTs on UPDATE operation, and authmysql
595+doesn't supports batched queries at the moment.
596+
597+4.2.1 database structure
598+
599+table: domain_names
600+
601+purpose: associates domain_id with domain name
602+
603+fields: domain_name - fully qualified domain name
604+ domain_id - domain identifier
605+
606+ +----------------+-----------+
607+ | domain_name | domain_id |
608+ +----------------+-----------+
609+ | exampledom.com | 1 |
610+ | pld.org.pl | 2 |
611+ | pld.net.pl | 2 |
612+ | foobare.net.uk | 3 |
613+ +----------------+-----------+
614+
615+Note, that for pld.org.pl and pld.net.pl the domain identifiers are the same.
616+We can create a domain aliases in such a way. :)
617+
618+table: domain_auth
619+
620+purpose: associates domain_id with authentication credentials
621+ which are common for all users in the virtual domain
622+
623+fields: domain_id - domain identifier
624+ path_prefix - absolute pathname which points to
625+ a directory where domain's mailboxes
626+ are located
627+ quota - default quota for each mailbox
628+ uid - UID used to work on mailboxes
629+ gid - GID used to work on mailboxes
630+
631+ +------------+---------------+--------+-------+-------+
632+ | domain_id | path_prefix | quota | uid | gid |
633+ +------------+---------------+--------+-------+-------+
634+ | 1 | /var/mail/ex | 100000 | 15000 | 15000 |
635+ | 2 | /var/mail/pld | 555500 | 15001 | 15000 |
636+ | 3 | /home/f0/mail | 8000 | 15002 | 15000 |
637+ +------------+---------------+--------+-------+-------+
638+
639+table: domain_info
640+
641+purpose: associates domain_id with additional informations
642+
643+fields: domain_id - domain identifier
644+ registrant_id - registrant identifier
645+ nic_handle - NIC handle
646+ owner_id - domain's owner identifier
647+ expires - domain's expiration date
648+
649+ +------------+---------------+------------+----------+---------+
650+ | domain_id | registrant_id | nic_handle | owner_id | expires |
651+ +------------+---------------+------------+----------+---------+
652+
653+ (we don't need to say anything more about this table indeed)
654+
655+table: users
656+
657+purpose: associates users' identifiers with domains' identifiers
658+ and infers the credentials for various virtual mailboxes
659+
660+fields: username - user's login name
661+ domain_id - domain identifier
662+ cryptpw - crypted password
663+ plainpw - plaintext password
664+ quota - user's mailbox quota
665+ (will override quota value set for
666+ the whole virtual domain)
667+ path - relative pathname for mailbox
668+ (will be appended to the path_prefix
669+ from domain_auth table to specify
670+ user's mailbox location)
671+
672+ +------------+-----------+----------+-----------+-------+------------+
673+ | username | domain_id | cryptpw | plainpw | quota | path |
674+ +------------+-----------+----------+-----------+-------+------------+
675+ | foobar | 1 | $1$hlIeE | dupa.8 | NULL | f/o/foobar |
676+ | breeder | 2 | $1$TWsdf | ziarno128 | 77777 | brd |
677+ +------------+-----------+----------+-----------+-------+------------+
678+
679+ (you can add a realname column here, it doesn't fit to my terminal window:)
680+
681+--------------------- cut here
682+
683+# Create the database called vmail.
684+
685+CREATE database vmail;
686+
687+# Create an example MySQL user, which can read, write and delete data from
688+# vmail database. Username: vuser Password: secret_password
689+
690+GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.*
691+ TO vuser@localhost
692+ IDENTIFIED BY 'secret_password';
693+
694+FLUSH PRIVILEGES;
695+
696+# Create the tables.
697+
698+use vmail;
699+
700+CREATE TABLE domain_names (
701+ domain_id int(10) unsigned NOT NULL,
702+ domain_name char(255) DEFAULT '' NOT NULL,
703+ KEY domain_name (domain_name(255))
704+ );
705+
706+CREATE TABLE domain_auth (
707+ domain_id int(10) unsigned DEFAULT 1 NOT NULL,
708+ uid int(10) unsigned DEFAULT '15000' NOT NULL,
709+ gid int(10) unsigned DEFAULT '15000' NOT NULL,
710+ path_prefix char(255) DEFAULT '' NOT NULL,
711+ quota char(255) DEFAULT '20000000' NOT NULL,
712+ KEY domain_id (domain_id)
713+ );
714+
715+CREATE TABLE users (
716+ username char(128) DEFAULT '' NOT NULL,
717+ domain_id int(10) unsigned DEFAULT 1 NOT NULL,
718+ cryptpw char(128) DEFAULT '' NOT NULL,
719+ plainpw char(128) DEFAULT '' NOT NULL,
720+ name char(128) DEFAULT '' NOT NULL,
721+ quota char(255),
722+ path char(255) DEFAULT '' NOT NULL,
723+ KEY username (username(128))
724+ );
725+
726+# Create an example virtual domain entry
727+# id : 1
728+# name : exampledom.com
729+# uid : 15000
730+# gid : 15000
731+# path : /var/mail/example
732+# quota : 20 Megs per mailbox
733+
734+INSERT INTO domain_names VALUES (1, 'exampledom.com');
735+INSERT INTO domain_auth VALUES (1, '15000', '15000', '/var/mail/example',
736+ '20000000');
737+
738+# Create an example virtual user entry
739+# username : siefca
740+# domain id : 1 (points to exampledom.com)
741+# cryptpw : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/
742+# clearpw : dupa.8
743+# name : Pawel Wilk
744+# quota : NULL (we want it to be fetched from domain_auth table)
745+# mailbox path : s/i/siefca
746+
747+INSERT INTO users VALUES ('siefca', 1, '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/',
748+ 'dupa.8', 'Pawel Wilk', NULL, 's/i/siefca');
749+
750+--------------------- cut here
751+
752+Ok, we've done what we need. Don't forget to create system user with UID and
753+GID set to 15000, and a directory containing mailboxes (in this case:
754+/var/mail/example) owned by system user I've mentioned above.
755+There is also necessary to create Maildir folder structure for our user
756+inside the virtual domain directory - you can configure your MTA agent to do
757+such thing when first message arrive or use maildirmake tool, which comes
758+with Courier-IMAP.
759+
760+
761+4.2.2 authdaemon configuration
762+
763+DEFAULT_DOMAIN exampledom.com
764+
765+MYSQL_SELECT_CLAUSE SELECT \
766+ users.username, \
767+ users.cryptpw, \
768+ users.plainpw, \
769+ domain_auth.uid, \
770+ domain_auth.gid, \
771+ CONCAT_WS('/',domain_auth.path_prefix,users.path), \
772+ '', \
773+ IFNULL(users.quota, domain_auth.quota), \
774+ users.name \
775+ FROM users, domain_names, domain_auth \
776+ WHERE domain_names.domain_name='$(domain)' \
777+ AND users.username='$(local_part)' \
778+ AND domain_names.domain_id=users.domain_id \
779+ AND domain_names.domain_id=domain_auth.domain_id
780+
781+
782+.
783+.
784+.
785+.
786+.
787+.
788+
789+/////////////////////////// PART II - Developer Notes /////////////////////////
790
791 *-----------------------
792 1 Modifications overview
793@@ -85,7 +768,7 @@
794 Each modified set of instructions is marked by my e-mail address:
795 siefca@pld.org.pl
796
797-Changes in the current source code are related to:
798+Changes in the source code are related to:
799
800 - sections where the queries are constructed
801 (including memory allocation for the buffers)
802@@ -102,6 +785,10 @@
803 newline as the second is replaced by two whitespaces while
804 putting into the buffer
805
806+ i've also added USER_DOMAIN_CONCAT and USER_DOMAIN_SEPARATORS
807+ configuration options - they're used by get_localpart(), get_domain()
808+ and get_username() functions, which are described below
809+
810 - sections where the query is constructed
811
812 selection is made, depending on configuration variables which
813@@ -130,7 +817,16 @@
814 MAX_SUBSTITUTION_LEN defines maximal length of a substitution variable's
815 identifier (name).
816
817-The last two definitions are just for code simplification.
818+The last two definitions (SV_BEGIN_LEN and SV_END_LEN) are just for code
819+simplification.
820+
821+#define DEF_CONCAT_STRING "@"
822+#define DEF_SEPARATORS_SET "@%"
823+
824+The first (DEF_CONCAT_STRING) is used to set the defaults for a
825+concatenation string, used when parsing $(username) substitution variable.
826+The second (DEF_SEPARATORS_SET) is the set of characters, which are treated as
827+separators when splitting local part from the domain.
828
829
830
831@@ -179,7 +875,7 @@
832 In this example we've declared that $(some) in the query should be
833 replaced by 'replacement' text, and replacement for $(anotha) will
834 be defined in the code before passing on the array pointer to
835-the paring function.
836+the general parsing function.
837
838
839 3.2 typedef size_t (*parsefunc)
840@@ -414,14 +1110,17 @@
841
842 SYNOPSIS
843
844- static const char *get_localpart (const char *username);
845+ static const char *get_localpart (const char *username,
846+ const char *separators);
847
848 DESCRIPTION
849
850 This function detaches local part of an e-mail address
851 from string pointed with username and puts it to the
852 buffer of the fixed length. All necessary cleaning is
853- made on the result string.
854+ made on the result string. String pointed with separators
855+ refers to a set of characters, which are treated as
856+ separation signs between local part and a domain.
857
858 RETURN VALUE
859
860@@ -438,16 +1137,22 @@
861 SYNOPSIS
862
863 static const char *get_domain (const char *username,
864- const char *defdomain);
865+ const char *defdomain,
866+ const char *separators);
867
868 DESCRIPTION
869
870 This function detaches domain part of an e-mail address
871 from string pointed with username and puts it to the
872 buffer of the fixed length. All necessary cleaning is
873- made on the result string. If function cannot find domain
874- part in the string the string pointed by defdomain is
875- used instead.
876+ made on the result string. If the function cannot find a domain
877+ part in the string then the string pointed to by defdomain is
878+ used instead. If this function cannot find a domain part
879+ as well as it cannot obtain the default domain (it's empty string
880+ or the defdomain pointer is NULL) the returned result string is an
881+ empty string. The string pointed with separators refers to a set
882+ of characters, which are treated as separation signs between local
883+ part and a domain.
884
885 RETURN VALUE
886
887@@ -455,7 +1160,36 @@
888 NULL if there was some error.
889
890
891-4.9 parse_select_clause
892+4.9 get_username
893+
894+NAME
895+
896+ get_username
897+
898+SYNOPSIS
899+
900+ static const char *get_username (const char *username,
901+ const char *domainname,
902+ const char *concat_str);
903+
904+DESCRIPTION
905+
906+ This function concatenates the localpart with a domain name
907+ using the string pointed with concat_str. If the domain is
908+ empty or NULL the result comes without binding string.
909+
910+RETURN VALUE
911+
912+ Pointer to the static buffer containing output string or
913+ NULL if there was some error.
914+
915+WARNINGS
916+
917+ This function does not any string cleaning, nor default domain
918+ checking. It is designed to work on results of get_localpart() and
919+ get_domain().
920+
921+4.10 parse_select_clause
922
923 NAME
924
925@@ -465,7 +1199,9 @@
926
927 static char *parse_select_clause (const char *clause,
928 const char *username,
929- const char *defdomain);
930+ const char *defdomain
931+ const char *concat_str,
932+ const char *separators_set);
933
934 DESCRIPTION
935
936@@ -473,15 +1209,17 @@
937 function. It parses a query pointed by caluse. username
938 and defdomain strings are used to replace corresponding
939 substitution strings if present in the query: $(local_part)
940- and $(domain).
941+ and $(domain). The separators_set is passed to get_username()
942+ and get_domain() invocations, and the concat_str is passed
943+ to get_username() function, which is responsible for replacing
944+ $(username) substitution variable.
945
946-
947 RETURN VALUE
948
949 Same as parse_string().
950
951
952-4.10 parse_chpass_clause
953+4.11 parse_chpass_clause
954
955 NAME
956
957@@ -492,6 +1230,8 @@
958 static char *parse_chpass_clause (const char *clause,
959 const char *username,
960 const char *defdomain,
961+ const char *separators_set,
962+ const char *concat_str,
963 const char *newpass,
964 const char *newpass_crypt);
965
966@@ -502,12 +1242,47 @@
967 defdomain, newpass and newpass_crypt strings are used to
968 replace corresponding substitution strings if present in
969 the query: $(local_part), $(domain), $(newpass),
970- $(newpass_crypt).
971+ $(newpass_crypt). The separators_set and the concat_str
972+ are passed to get_localpart(), get_domain(), and get_username()
973+ functions as described in the entry for parse_select_clause().
974
975 RETURN VALUE
976
977 Same as parse_string().
978
979+4.12 auth_mysql_on_trigger
980+
981+NAME
982+
983+ auth_mysql_on_trigger
984+
985+SYNOPSIS
986+
987+ int auth_mysql_on_trigger (const char *clause_name,
988+ const char *username);
989+
990+DESCRIPTION
991+
992+ This function is responsible for calling out the MySQL queries in
993+ depend which authentication state was reached.
994+
995+ The clause_name should contain the name of a clause, which can be found
996+ in the configuration file, and the username is simply the string used
997+ as username (including the domain if entered).
998+
999+ This function reads DEFAULT_DOMAIN, USER_DOMAIN_CONCAT and
1000+ USER_DOMAIN_SEPARATORS from the configuration file using read_env(),
1001+ then it uses parse_select_clause() to parse the query obtained using
1002+ read_env(clause_name), and then it calls querying subroutines to
1003+ perform the action.
1004+
1005+RETURN VALUE
1006+
1007+ This function returns 1 on success and 0 on failure. The query results
1008+ are simply discarded. If a trigger's clause is not defined in the
1009+ configuration file the 1 is returned and function silently ends its
1010+ work.
1011+
1012
1013
1014
1015@@ -520,11 +1295,9 @@
1016 strings after split (problem?)
1017 - allow admin to set a group name instead of numerical group id
1018 - allow admin to set a username instead of numerical user id
1019-
1020-- add clauses:
1021-
1022- - MYSQL_PRESELECT_CLAUSE (query which comes before MYSQL_SELECT_CLAUSE)
1023- - MYSQL_POSTSELECT_CLAUSE (query which comes after MYSQL_SELECT_CLAUSE)
1024+- allow batched queries and register variables for keeping results
1025+- put the parsing routines into separate files to make possible of sharing it
1026+ by more authentication modules
1027
1028
1029
1030@@ -534,10 +1307,12 @@
1031 6 Thanks
1032 *------------------------
1033
1034-At the beginning this patch was messy indeed. :> I would like to thank
1035+At the beginning the patch was messy indeed. :> I would like to thank
1036 Sam Varshavchik for pointing me a lot how to make it more fast and solid.
1037 I would also thank Philip Hazel, Chris Lightfoot and Mike Bremford which
1038-by their software capabilities inspired me to write it.
1039+by their software capabilities inspired me to write it. Oliver Oblasnik
1040+remainded me to make the documentation more friendly for those who are
1041+not programmers and just want to use it. :>
1042
1043 ---------------------------------------------------------------------------
1044
1045diff -ur courier-imap-1.4.2.orig/authlib/authmysql.c courier-imap-1.4.2/authlib/authmysql.c
1046--- courier-imap-1.4.2.orig/authlib/authmysql.c Sun Jun 24 01:42:05 2001
1047+++ courier-imap-1.4.2/authlib/authmysql.c Sun Feb 10 04:54:37 2002
1048@@ -31,7 +31,11 @@
1049 if ((user=strtok(authdata, "\n")) == 0 ||
1050 (pass=strtok(0, "\n")) == 0)
1051 {
1052- errno=EPERM;
1053+ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user))
1054+ errno=EACCES;
1055+ else
1056+ errno=EPERM;
1057+
1058 return (0);
1059 }
1060
1061@@ -50,7 +54,11 @@
1062 {
1063 if (authcheckpassword(pass,authinfo->cryptpw))
1064 {
1065- errno=EPERM;
1066+ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user))
1067+ errno=EACCES;
1068+ else
1069+ errno=EPERM;
1070+
1071 return (0); /* User/Password not found. */
1072 }
1073 }
1074@@ -58,13 +66,21 @@
1075 {
1076 if (strcmp(pass, authinfo->clearpw))
1077 {
1078- errno=EPERM;
1079+ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user))
1080+ errno=EACCES;
1081+ else
1082+ errno=EPERM;
1083+
1084 return (0);
1085 }
1086 }
1087 else
1088 {
1089- errno=EPERM;
1090+ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user))
1091+ errno=EACCES;
1092+ else
1093+ errno=EPERM;
1094+
1095 return (0); /* Username not found */
1096 }
1097
1098@@ -132,6 +148,12 @@
1099 (*callback_func)(&aa, callback_arg);
1100 }
1101
1102+ if (!auth_mysql_on_trigger("MYSQL_ONSUCCESS_CLAUSE", user))
1103+ {
1104+ errno=EACCES;
1105+ return (0);
1106+ }
1107+
1108 return (strdup(authinfo->username));
1109 }
1110
1111@@ -153,7 +175,11 @@
1112 {
1113 if (authcheckpassword(pass,authinfo->cryptpw))
1114 {
1115- errno=EPERM;
1116+ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user))
1117+ errno=EACCES;
1118+ else
1119+ errno=EPERM;
1120+
1121 return (-1); /* User/Password not found. */
1122 }
1123 }
1124@@ -161,13 +187,21 @@
1125 {
1126 if (strcmp(pass, authinfo->clearpw))
1127 {
1128- errno=EPERM;
1129+ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user))
1130+ errno=EACCES;
1131+ else
1132+ errno=EPERM;
1133+
1134 return (-1);
1135 }
1136 }
1137 else
1138 {
1139- errno=EPERM;
1140+ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user))
1141+ errno=EACCES;
1142+ else
1143+ errno=EPERM;
1144+
1145 return (-1);
1146 }
1147
1148@@ -176,6 +210,13 @@
1149 errno=EPERM;
1150 return (-1);
1151 }
1152+
1153+ if (!auth_mysql_on_trigger("MYSQL_ONSUCCESS_CLAUSE", user))
1154+ {
1155+ errno=EACCES;
1156+ return (-1);
1157+ }
1158+
1159 return (0);
1160 }
1161
1162diff -ur courier-imap-1.4.2.orig/authlib/authmysql.h courier-imap-1.4.2/authlib/authmysql.h
1163--- courier-imap-1.4.2.orig/authlib/authmysql.h Mon Aug 6 05:12:39 2001
1164+++ courier-imap-1.4.2/authlib/authmysql.h Sun Feb 10 04:56:30 2002
1165@@ -21,6 +21,7 @@
1166 } ;
1167
1168 extern struct authmysqluserinfo *auth_mysql_getuserinfo(const char *);
1169+extern int auth_mysql_on_trigger (const char *clause_name, const char *username);
1170 extern void auth_mysql_cleanup();
1171
1172 extern int auth_mysql_setpass(const char *, const char *);
1173diff -ur courier-imap-1.4.2.orig/authlib/authmysqllib.c courier-imap-1.4.2/authlib/authmysqllib.c
1174--- courier-imap-1.4.2.orig/authlib/authmysqllib.c Thu Jan 10 05:44:06 2002
1175+++ courier-imap-1.4.2/authlib/authmysqllib.c Sun Feb 10 14:35:10 2002
1176@@ -24,6 +24,9 @@
1177 #define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1)
1178 #define SV_END_LEN ((sizeof(SV_END_MARK))-1)
1179
1180+#define DEF_CONCAT_STRING "@"
1181+#define DEF_SEPARATORS_SET "@%"
1182+
1183 static const char rcsid[]="$Id$";
1184
1185 /* siefca@pld.org.pl */
1186@@ -426,21 +429,43 @@
1187 return NULL;
1188 }
1189 *pass_buf = '\0';
1190-
1191+
1192 return output_buf;
1193 }
1194
1195 /* siefca@pld.org.pl */
1196-static const char *get_localpart (const char *username)
1197+static const char *get_username (const char *username, const char *domainname,
1198+ const char *concat_str)
1199+{
1200+static char username_buf[400];
1201+
1202+ if (!username || !domainname || !concat_str ||
1203+ *username == '\0' || *concat_str == '\0') return NULL;
1204+ if (( strlen(username) +
1205+ strlen(concat_str) +
1206+ strlen(domainname)) > 397) return NULL;
1207+
1208+ if (*domainname == '\0')
1209+ strcpy (username_buf, username);
1210+ else
1211+ sprintf (username_buf, "%s%s%s", username, concat_str,
1212+ domainname);
1213+
1214+ return (username_buf);
1215+}
1216+
1217+/* siefca@pld.org.pl */
1218+static const char *get_localpart (const char *username, const char *separators)
1219 {
1220 size_t lbuf = 0;
1221 const char *l_end, *p;
1222 char *q;
1223 static char localpart_buf[130];
1224
1225- if (!username || *username == '\0') return NULL;
1226+ if (!username || *username == '\0' ||
1227+ !separators || *separators == '\0') return NULL;
1228
1229- p = strchr(username,'@');
1230+ p = strpbrk (username, separators);
1231 if (p)
1232 {
1233 if ((p-username) > 128)
1234@@ -469,21 +494,27 @@
1235 }
1236
1237 /* siefca@pld.org.pl */
1238-static const char *get_domain (const char *username, const char *defdomain)
1239+static const char *get_domain (const char *username, const char *defdomain,
1240+ const char *separators)
1241 {
1242 static char domain_buf[260];
1243 const char *p;
1244 char *q;
1245
1246- if (!username || *username == '\0') return NULL;
1247- p = strchr(username,'@');
1248+ if (!username || *username == '\0' ||
1249+ !separators || *separators == '\0') return NULL;
1250+
1251+ p = strpbrk (username, separators);
1252
1253 if (!p || *(p+1) == '\0')
1254 {
1255- if (defdomain && *defdomain)
1256+ if (defdomain && *defdomain != '\0')
1257 return defdomain;
1258 else
1259- return NULL;
1260+ {
1261+ *domain_buf = '\0';
1262+ return domain_buf;
1263+ }
1264 }
1265
1266 p++;
1267@@ -528,20 +559,27 @@
1268
1269 /* siefca@pld.org.pl */
1270 static char *parse_select_clause (const char *clause, const char *username,
1271- const char *defdomain)
1272+ const char *defdomain,
1273+ const char *concat_str,
1274+ const char *separators_set)
1275 {
1276 static struct var_data vd[]={
1277 {"local_part", NULL, sizeof("local_part"), 0},
1278 {"domain", NULL, sizeof("domain"), 0},
1279+ {"username", NULL, sizeof("username"), 0},
1280 {NULL, NULL, 0, 0}};
1281
1282 if (clause == NULL || *clause == '\0' ||
1283- !username || *username == '\0')
1284+ !username || *username == '\0' ||
1285+ !concat_str || *concat_str == '\0' ||
1286+ !separators_set || *separators_set == '\0')
1287 return NULL;
1288
1289- vd[0].value = get_localpart (username);
1290- vd[1].value = get_domain (username, defdomain);
1291- if (!vd[0].value || !vd[1].value)
1292+ vd[0].value = get_localpart (username, separators_set);
1293+ vd[1].value = get_domain (username, defdomain, separators_set);
1294+ vd[2].value = get_username (vd[0].value, vd[1].value, concat_str);
1295+
1296+ if (!vd[0].value || !vd[1].value || !vd[2].value)
1297 return NULL;
1298
1299 return (parse_string (clause, vd));
1300@@ -549,12 +587,16 @@
1301
1302 /* siefca@pld.org.pl */
1303 static char *parse_chpass_clause (const char *clause, const char *username,
1304- const char *defdomain, const char *newpass,
1305+ const char *defdomain,
1306+ const char *separators_set,
1307+ const char *concat_str,
1308+ const char *newpass,
1309 const char *newpass_crypt)
1310 {
1311 static struct var_data vd[]={
1312 {"local_part", NULL, sizeof("local_part"), 0},
1313 {"domain", NULL, sizeof("domain"), 0},
1314+ {"username", NULL, sizeof("username"), 0},
1315 {"newpass", NULL, sizeof("newpass"), 0},
1316 {"newpass_crypt", NULL, sizeof("newpass_crypt"), 0},
1317 {NULL, NULL, 0, 0}};
1318@@ -562,19 +604,83 @@
1319 if (clause == NULL || *clause == '\0' ||
1320 !username || *username == '\0' ||
1321 !newpass || *newpass == '\0' ||
1322+ !separators_set || *separators_set == '\0' ||
1323 !newpass_crypt || *newpass_crypt == '\0') return NULL;
1324
1325- vd[0].value = get_localpart (username);
1326- vd[1].value = get_domain (username, defdomain);
1327- vd[2].value = validate_password (newpass);
1328- vd[3].value = validate_password (newpass_crypt);
1329+ vd[0].value = get_localpart (username, separators_set);
1330+ vd[1].value = get_domain (username, defdomain, separators_set);
1331+ vd[3].value = get_username (vd[0].value, vd[1].value, concat_str);
1332+ vd[4].value = validate_password (newpass);
1333+ vd[5].value = validate_password (newpass_crypt);
1334
1335 if (!vd[0].value || !vd[1].value ||
1336- !vd[2].value || !vd[3].value) return NULL;
1337+ !vd[2].value || !vd[3].value ||
1338+ !vd[4].value || !vd[5].value) return NULL;
1339
1340 return (parse_string (clause, vd));
1341 }
1342
1343+/* siefca@pld.org.pl */
1344+int auth_mysql_on_trigger (const char *clause_name, const char *username)
1345+{
1346+char *querybuf =NULL;
1347+const char *concat_str =NULL,
1348+ *separators_set =NULL,
1349+ *defdomain =NULL,
1350+ *on_clause =NULL;
1351+MYSQL_RES *result;
1352+
1353+ if (!clause_name || *clause_name == '\0') return (0);
1354+ on_clause = read_env (clause_name);
1355+ if (!on_clause || *on_clause == '\0') return (1);
1356+
1357+ defdomain = read_env ("DEFAULT_DOMAIN");
1358+ concat_str = read_env ("USER_DOMAIN_CONCAT");
1359+ separators_set = read_env ("USER_DOMAIN_SEPARATORS");
1360+ if (!defdomain) defdomain = "";
1361+ if (!concat_str || *concat_str == '\0')
1362+ concat_str = DEF_CONCAT_STRING;
1363+ if (!separators_set || *separators_set == '\0')
1364+ separators_set = DEF_SEPARATORS_SET;
1365+
1366+ querybuf = parse_select_clause (on_clause,
1367+ username,
1368+ defdomain,
1369+ concat_str,
1370+ separators_set);
1371+
1372+ if (!querybuf) return (0);
1373+
1374+ if (mysql_query (mysql, querybuf))
1375+ {
1376+ /* <o.blasnik@nextra.de> */
1377+
1378+ auth_mysql_cleanup();
1379+
1380+ if (do_connect())
1381+ {
1382+ free(querybuf);
1383+ return (1);
1384+ }
1385+
1386+ if (mysql_query (mysql, querybuf))
1387+ {
1388+ free(querybuf);
1389+ auth_mysql_cleanup();
1390+ /* Server went down, that's OK,
1391+ ** try again next time.
1392+ */
1393+ return (1);
1394+ }
1395+ }
1396+ free(querybuf);
1397+ result = mysql_store_result(mysql);
1398+ if (result) mysql_free_result(result);
1399+
1400+ return (1);
1401+}
1402+
1403+
1404 struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username)
1405 {
1406 const char *user_table =NULL;
1407@@ -593,6 +699,8 @@
1408 *gid_field =NULL,
1409 *quota_field =NULL,
1410 *where_clause =NULL,
1411+ *concat_str =NULL,
1412+ *separators_set =NULL,
1413 *select_clause =NULL; /* siefca@pld.org.pl */
1414
1415 static const char query[]=
1416@@ -701,7 +809,19 @@
1417 else
1418 {
1419 /* siefca@pld.org.pl */
1420- querybuf=parse_select_clause (select_clause, username, defdomain);
1421+ concat_str = read_env ("USER_DOMAIN_CONCAT");
1422+ separators_set = read_env ("USER_DOMAIN_SEPARATORS");
1423+
1424+ if (!concat_str || *concat_str == '\0')
1425+ concat_str = DEF_CONCAT_STRING;
1426+ if (!separators_set || *separators_set == '\0')
1427+ separators_set = DEF_SEPARATORS_SET;
1428+
1429+ querybuf = parse_select_clause (select_clause,
1430+ username,
1431+ defdomain,
1432+ concat_str,
1433+ separators_set);
1434 if (!querybuf) return 0;
1435 }
1436
1437@@ -785,6 +905,8 @@
1438 *where_clause =NULL,
1439 *user_table =NULL,
1440 *login_field =NULL,
1441+ *concat_str =NULL,
1442+ *separators_set =NULL,
1443 *chpass_clause =NULL; /* siefca@pld.org.pl */
1444
1445 if (!mysql)
1446@@ -834,14 +956,23 @@
1447 }
1448 else
1449 {
1450+ concat_str = read_env ("USER_DOMAIN_CONCAT");
1451+ separators_set = read_env ("USER_DOMAIN_SEPARATORS");
1452+
1453+ if (!concat_str || *concat_str == '\0')
1454+ concat_str = DEF_CONCAT_STRING;
1455+ if (!separators_set || *separators_set == '\0')
1456+ separators_set = DEF_SEPARATORS_SET;
1457+
1458 sql_buf=parse_chpass_clause(chpass_clause,
1459 user,
1460 defdomain,
1461+ concat_str,
1462+ separators_set,
1463 pass,
1464 newpass_crypt);
1465 }
1466
1467-
1468 if (!sql_buf)
1469 {
1470 free(newpass_crypt);
1471diff -ur courier-imap-1.4.2.orig/authlib/authmysqlrc courier-imap-1.4.2/authlib/authmysqlrc
1472--- courier-imap-1.4.2.orig/authlib/authmysqlrc Tue Jan 8 06:20:46 2002
1473+++ courier-imap-1.4.2/authlib/authmysqlrc Thu Feb 14 00:19:43 2002
1474@@ -141,65 +141,91 @@
1475 #
1476 # MYSQL_WHERE_CLAUSE server='mailhost.example.com'
1477
1478-##NAME: MYSQL_SELECT_CLAUSE:0
1479+##NAME: USER_DOMAIN_CONCAT:0
1480 #
1481-# (EXPERIMENTAL)
1482-# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database,
1483-# which is structuraly different from proposed. The fixed string will
1484-# be used to do a SELECT operation on database, which should return fields
1485-# in order specified bellow:
1486-#
1487-# username, cryptpw, uid, gid, clearpw, home, maildir, quota, fullname
1488-#
1489-# Enabling this option causes ignorance of any other field-related
1490-# options, excluding default domain.
1491-#
1492-# There are two variables, which you can use. Substitution will be made
1493-# for them, so you can put entered username (local part) and domain name
1494-# in the right place of your query. These variables are:
1495-# $(local_part) and $(domain)
1496+# This is optional. Here you can set the string used to concatenate
1497+# usename with domain part while expanding the $(username) substitution
1498+# variable. If it's not set the '@' character is used.
1499 #
1500-# If a $(domain) is empty (not given by the remote user) the default domain
1501-# name is used in its place.
1502+# USER_DOMAIN_CONCAT @
1503+
1504+##NAME: USER_DOMAIN_SEPARATORS:0
1505 #
1506-# This example is a little bit modified adaptation of vmail-sql
1507-# database scheme:
1508+# This is optional. Using this option you can set the set of characters
1509+# which are treated as separators when splitting entered username into the
1510+# local part and the domain name. If it's not set the default set @% is used,
1511+# do the user can log on using user@domain or user%domain.
1512 #
1513-# MYSQL_SELECT_CLAUSE SELECT popbox.local_part, \
1514-# CONCAT('{MD5}', popbox.password_hash), \
1515-# domain.uid, \
1516-# domain.gid, \
1517-# popbox.clearpw, \
1518-# CONCAT(domain.path, '/', popbox.mbox_name), \
1519-# '', \
1520-# domain.quota, \
1521-# '', \
1522-# FROM popbox, domain \
1523-# WHERE popbox.local_part = '$(local_part)' \
1524-# AND popbox.domain_name = '$(domain)' \
1525-# AND popbox.domain_name = domain.domain_name
1526+# USER_DOMAIN_SEPARATORS @%+
1527+
1528+##NAME: MYSQL_SELECT_CLAUSE:0
1529+#
1530+# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database,
1531+# which is structuraly different from proposed. You can type here your MySQL
1532+# query, which will be used to fetch user's credentials, and which should
1533+# return fields in order specified bellow:
1534+#
1535+# username, cryptpw, clearpw, uid, gid, home, maildir, quota, fullname
1536+#
1537+# Enabling this option causes ignorance of any other field-related options.
1538+#
1539+# There also are variables, which you can use. Substitution will be made
1540+# for them, so you can pass currently entered username and a domain name
1541+# up to the right place within your query. These variables are:
1542+# $(local_part) , $(domain) , $(username)
1543 #
1544+# If a $(domain) is empty (not given by the remote user) the default domain
1545+# name is used in its place. $(username) is a local part concatenated with
1546+# domain name using symbol defined in USER_DOMAIN_CONCAT or '@' if this option
1547+# is not set.
1548+#
1549+# MYSQL_SELECT_CLAUSE SELECT \
1550+# users.username, users.cryptpw, users.clearpw, \
1551+# domains.uid, domains.gid, \
1552+# CONCAT_WS('/',domains.path_prefix,users.mailbox_path), \
1553+# '', domains.quota, '' \
1554+# FROM users, domains \
1555+# WHERE domains.domain_name='$(domain)' \
1556+# AND users.username='$(local_part)' \
1557+# AND domains.domain_name=users.domain_name
1558+
1559 ##NAME: MYSQL_CHPASS_CLAUSE:0
1560 #
1561-# (EXPERIMENTAL)
1562 # This is optional, MYSQL_CHPASS_CLAUSE can be set when you have a database,
1563-# which is structuraly different from proposed. The fixed string will
1564-# be used to do an UPDATE operation on database. In other words, it is
1565-# used, when changing password.
1566+# which is structuraly different from proposed. You can use it to set up
1567+# a MySQL query used to change user's password.
1568 #
1569 # There are four variables, which you can use. Substitution will be made
1570-# for them, so you can put entered username (local part) and domain name
1571+# for them, so you can put the currently entered username and the domain name
1572 # in the right place of your query. There variables are:
1573-# $(local_part) , $(domain) , $(newpass) , $(newpass_crypt)
1574+# $(local_part) , $(domain) , $(username) , $(newpass) , $(newpass_crypt)
1575 #
1576 # If a $(domain) is empty (not given by the remote user) the default domain
1577-# name is used in its place.
1578-# $(newpass) contains plain password
1579-# $(newpass_crypt) contains its crypted form
1580-#
1581-# MYSQL_CHPASS_CLAUSE UPDATE popbox \
1582-# SET clearpw='$(newpass)', \
1583-# password_hash='$(newpass_crypt)' \
1584-# WHERE local_part='$(local_part)' \
1585-# AND domain_name='$(domain)'
1586+# name is used in its place. $(newpass) contains plain password and
1587+# $(newpass_crypt) contains its crypted form.
1588+#
1589+# MYSQL_CHPASS_CLAUSE UPDATE users \
1590+# SET clearpw='$(newpass)', \
1591+# cryptpw='$(newpass_crypt)' \
1592+# WHERE username='$(local_part)' \
1593+# AND domain_name='$(domain)'
1594+
1595+##NAME: MYSQL_ONSUCCESS_CLAUSE:0
1596+#
1597+# This is optional, MYSQL_ONSUCCESS_CLAUSE is a trigger - the query is performed
1598+# each time user has successfuly logged in. [experimental]
1599+#
1600+# MYSQL_ONSUCCESS_CLAUSE UPDATE users \
1601+# SET last_ok=CURRENT_TIMESTAMP \
1602+# WHERE username='$(local_part)' \
1603+# AND domain_name='$(domain)'
1604+
1605+##NAME: MYSQL_ONFAIL_CLAUSE:0
1606+#
1607+# This is optional, MYSQL_ONFAIL_CLAUSE is a trigger - the query is performed
1608+# each time user has successfuly logged in. [experimental]
1609 #
1610+# MYSQL_ONFAIL_CLAUSE UPDATE users \
1611+# SET last_fail=CURRENT_TIMESTAMP \
1612+# WHERE username='$(local_part)' \
1613+# AND domain_name='$(domain)'
This page took 0.39281 seconds and 4 git commands to generate.