From: siefca Date: Sat, 28 Sep 2002 15:35:57 +0000 (+0000) Subject: - preparing for new courier-imap version (current snapshot 1.5.3.20020921) X-Git-Tag: courier-imap-1_5_3-3~1 X-Git-Url: http://git.pld-linux.org/?a=commitdiff_plain;ds=sidebyside;h=e781919ff3216ddcb3d099048585cd7f22771cb5;p=packages%2Fcourier-imap.git - preparing for new courier-imap version (current snapshot 1.5.3.20020921) - myownquery feature updated - new release, adapted for new sources - added ChangeLog to documentation - init.d scripts changes - logger is now courierlogger - restart of authdaemon causes each related service to be restarted Changed files: courier-imap-1.5.3.20020921-myownquery.patch -> 1.1 courier-imap-authdaemon.init -> 1.3 courier-imap-pop3.init -> 1.5 courier-imap.init -> 1.9 courier-imap.spec -> 1.71 --- diff --git a/courier-imap-1.5.3.20020921-myownquery.patch b/courier-imap-1.5.3.20020921-myownquery.patch new file mode 100644 index 0000000..afcd292 --- /dev/null +++ b/courier-imap-1.5.3.20020921-myownquery.patch @@ -0,0 +1,1773 @@ +diff -ur courier-imap-1.5.3.20020921.orig/authlib/README.authmysql.myownquery courier-imap-1.5.3.20020921/authlib/README.authmysql.myownquery +--- courier-imap-1.5.3.20020921.orig/authlib/README.authmysql.myownquery Tue Jan 8 06:01:22 2002 ++++ courier-imap-1.5.3.20020921/authlib/README.authmysql.myownquery Sat Sep 28 01:19:55 2002 +@@ -2,13 +2,18 @@ + + + +- Developer Notes for courier-imap-myownquery.patch ++ Developer Notes and Usage Instructions ++ ++ of ++ ++ courier-imap-authmysql-myownquery + + + ++ document version: 1.30 ++ patch for version: 1.5.3.20020921 ++ author: Pawel Wilk + +- document version: 1.03 +- author: Pawel Wilk + + + +@@ -21,71 +26,749 @@ + + + ++PART I - Usage Instructions + ++ 1 What's that? ++ ++ 2 When will I need it? ++ ++ 3 How does it work? ++ 3.1 configuration variables ++ 3.2 queries ++ 3.3 substitutions ++ 3.4 triggers ++ ++ 4 Examples of usage ++ 4.1 corporate mail system ++ 4.1.1 database structure ++ 4.1.2 authdaemon configuration ++ 4.2 virtual mail domains provider ++ 4.2.1 database structure ++ 4.2.2 authdaemon configuration ++ ++PART II - Developer Notes ++ ++ 1 Modifications overview ++ ++ 2 Definitions ++ ++ 3 New data types ++ 3.1 struct var_data ++ 3.2 typedef size_t (*parsefunc) ++ ++ 4 New functions ++ 4.1 get_variable ++ 4.2 parse_core ++ 4.3 ParsePlugin_counter ++ 4.4 ParsePlugin_builder ++ 4.5 parse_string ++ 4.6 validate_password ++ 4.7 get_localpart ++ 4.8 get_domain ++ 4.9 get_username ++ 4.10 parse_select_clause ++ 4.11 parse_chpass_clause ++ 4.12 auth_mysql_on_trigger + ++ 5 Ideas and TODO + ++ 6 Thanks + +-0 What's that? + +-1 Modifications overview + +-2 Definitions + +-3 New data types +- 3.1 struct var_data +- 3.2 typedef size_t (*parsefunc) +- +-4 New functions +- 4.1 get_variable +- 4.2 parse_core +- 4.3 ParsePlugin_counter +- 4.4 ParsePlugin_builder +- 4.5 parse_string +- 4.6 validate_password +- 4.7 get_localpart +- 4.8 get_domain +- 4.9 parse_select_clause +- 4.10 parse_chpass_clause +- +-5 Ideas and TODO ++//////////////////////////////// PART I - Usage /////////////////////////////// ++ ++ *----------------------- ++ 1 What's that? ++ *----------------------- ++ ++Courier-imap-myownquery's features allow the administrator to set his own MySQL ++queries used by authdaemon to authenticate a user (including fetchig his ++credentials) and to change the user's password. It allows one to write a ++SELECT and UPDATE clause in the configuration file (authmysqlrc) using ++the new configuration options. It may be useful in mail environments where ++there is a need to have a different database structure and/or tables ++scheme than expected by authmysql module. + +-6 Thanks ++It also implements a small parsing engine for substitution of variables which ++may appear in the SQL clauses, such as a username or a domain. + + + + ++ ++ *----------------------- ++ 2 When will I need it? + *----------------------- +- 0 What's that? ++ ++ o When you already have some MySQL database filled up with the data ++ and there is no chance to change the whole structure to make it ++ work with a standard authmysql table. Typical situation is when all the ++ data required to authenticate a user is arranged in more than one table. ++ ++ o When you have some great idea how to make the database structure ++ more efficient due to your needs and your requirements. ++ ++ o When doing something 'by-myself' is in your style and you just want ++ to create your own database, just to feel the pleasure of doing ++ something original. :) ++ ++ ++ ++ ++ + *----------------------- ++ 3 How does it work? ++ *----------------------- ++ ++There are three things which the feature concerns: ++ ++- fetching clauses from the configuration file ++- doing substitution replacements inside of SQL clauses ++- passing prepared query on to the mysql interface funtions ++ ++3.1 configuration options ++ ++You can apply your own MySQL queries using a set of the configuration options. ++The options you'll need to make the authmysql your slave are: ++ ++MYSQL_SERVER (required) ++MYSQL_USERNAME (required) ++MYSQL_PASSWORD (required) ++ ++ The server name, userid, and password used to log in. ++ ++MYSQL_DATABASE (required) ++ ++ The name of the MySQL database we will open. ++ ++DEFAULT_DOMAIN (optional) ++ ++ If DEFAULT_DOMAIN is defined, and someone tries to log in as ++ 'user', we will look up 'user@DEFAULT_DOMAIN' instead. ++ ++USER_DOMAIN_CONCAT (optional) ++ ++ The USER_DOMAIN_CONCAT defines a character(s) used to ++ concatenate a local part and a domain while parsing ++ the $(username) substitution variable (see section 3.3 ++ for more info). If it's not defined the @ sign is assumed. ++ ++USER_DOMAIN_SEPARATORS (optional) ++ ++ This may contain the set of characters used by parsing ++ routines to split local part of the virtual mailbox name ++ from the part which describes the domain name. If it's not ++ defined the set containing @% is assumed, so the user can ++ enter either: user@domain or user%domain when he wants to be ++ authenticated. ++ ++MYSQL_SELECT_CLAUSE (required) ++MYSQL_CHPASS_CLAUSE (required under some circumstances) ++ ++ These are the major options you should use. See 3.2 section ++ for more info. ++ ++MYSQL_ONSUCCESS_CLAUSE (optional) ++MYSQL_ONFAIL_CLAUSE (optional) ++ ++ These are used to do a MySQL query whether user has passed ++ the authentication verification (MYSQL_ONSUCCESS_CLAUSE) ++ or there was the authentication failure (MYSQL_ONFAIL_CLAUSE). ++ Query results have no meaning. You can use the same ++ substitution variables in your query as with ++ MYSQL_SELECT_CLAUSE. See 3.4 section for more info. ++ ++The options which have no effect, and may be safetly left blank are: ++ ++MYSQL_USER_TABLE ++MYSQL_CRYPT_PWFIELD ++MYSQL_CLEAR_PWFIELD ++MYSQL_UID_FIELD ++MYSQL_GID_FIELD ++MYSQL_LOGIN_FIELD ++MYSQL_HOME_FIELD ++MYSQL_NAME_FIELD ++MYSQL_MAILDIR_FIELD ++MYSQL_QUOTA_FIELD ++MYSQL_WHERE_CLAUSE ++ ++3.2 queries ++ ++The feature adds two configuration options (clauses), which are parsed first, ++and then applied as MySQL queries to MySQL interface routines. These options ++are: MYSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE. After each option a number of ++spaces and/or tabs is allowed, and then MySQL query is expected. For better ++look, your queries can have line breaks. Each line break should be preceded by ++the backslash sign. Look into examples chapter (4) to see how it should look ++like. First clause is used to authenticate a user, and the second to change his ++password. ++ ++You should note that a query identified by MYSQL_SELECT_CLAUSE should return ++fixed number (9) of fields and each field should match the variable expected ++by authentication routines. These fields are: ++ ++* username - which is the currently logged user's username (or the ++ username with domain if you want it) ++ ++* cryptpw - which is the user's crypted password ++ ++* clearpw - which is the user's plaintext password ++ ++* uid - which is a numerical UID value used as a process's UID when ++ accessing the mailbox directory ++ ++* gid - as above, but refers to GID ++ ++* home - which contains full path to the user's home directory ++ ++maildir - which contains the directory name inside the user's home ++ which is treated as INBOX folder when accessing mailbox ++ - if it's empty then the 'Maildir' string is used ++ ++quota - which describes a quota size for the mailbox ++ ++fullname - which may contain the user's fullname ++ ++(The fields marked by the asterix sign are required and cannot have an ++ empty results) ++ ++So, the typical query clause may start with: ++ ++MYSQL_SELECT_CLAUSE SELECT \ ++ users.username, \ ++ users.cryptpw, \ ++ users.clearpw, \ ++ domains.uid, \ ++ domains.gid, \ ++ users.mailbox_path) \ ++ '' \ ++ domains.quota, \ ++ '' \ ++... ++ ++Note that in this short example we're assuming that we have two tables ++(users and domains) and INBOX path is always called 'Maildir' and ++we're not using the fullname field (the query will always return an empty ++string in its place). ++ ++Also note that you may discard one of the password fields if you don't want ++to use an authentication mechanism, which needs it. For example, if you don't ++want to use MD5-CRAM you may put '' into the place of clearpw (because, for ++example you're in paranoid mode and you don't even want to keep plain passwords ++in the database:). ++ ++3.3 substitutions ++ ++Substitutions are strings, which may appear in your query, and which have a ++special meaning. You can also call them substitution variables. If substitution ++variable is known for a clause context then it is parsed. If it isn't known the ++error is generated. In the default compilation of authmysql module any ++substitution variable is declared inside of two substrings - the first is a ++dollar sign concatenated with opening parenthesis, and the second is a closing ++parenthesis sign. First symbol identifies beginning of a substitution variable, ++and the second closes it. The string between the beginning and the closing ++symbol is called substitution variable's name. ++ ++When, as I said before, the name is known to the parsing routine the ++substitution is made and the proper value appears in place of the substitution ++variable, while passing on the query for later processing. ++ ++Allowed substitution variables: ++ ++context: MYSQL_SELECT_CLAUSE, MYSQL_ONFAIL_CLAUSE, MYSQL_ONSUCCESS_CLAUSE ++ ++$(local_part) will be replaced by currently verified user's username ++ (without the domain part) ++ ++$(domain) will be replaced by currently verified user's domain ++ name (if present, or if not present but the ++ DEFAULT_DOMAIN was used) or by the empty, zero-length ++ string if the domain cannot be obtained ++ ++$(username) will be replaced by currently verified user's username ++ concatenated with the given domain name using symbol ++ defined by USER_DOMAIN_CONCAT - if the domiain name ++ cannot be obtained (even by looking up DEFAULT_DOMAIN) ++ the separation sign will not appear and only the given ++ username will be presented ++ ++context: MYSQL_CHPASS_CLAUSE ++ ++$(local_part) will be replaced by currently verified user's username ++ (without the domain part) ++ ++$(domain) will be replaced by currently verified user's domain ++ name (if present, or if not present but the ++ DEFAULT_DOMAIN was used) or by the empty, zero-length ++ string if the domain cannot be obtained ++ ++$(username) will be replaced by currently verified user's username ++ concatenated with the given domain name using symbol ++ defined by USER_DOMAIN_CONCAT - if the domiain name ++ cannot be obtained (even by looking up DEFAULT_DOMAIN) ++ the separation sign will not appear and only the given ++ username will be presented ++ ++$(newpass) will be replaced by currently authenticated user's ++ new password to set up (plaintext password) ++ ++$(newpass_crypt) will be replaced by currently authenticated user's ++ new password to set up (MD5 form created from entered ++ plain form) ++ ++3.4 triggers ++ ++Triggers are MySQL queries, which are performed depending on authentication ++state. Currently, there are two triggers which you may use. First is called ++MYSQL_ONSUCCESS_CLAUSE and it is performed when the authentication succeedes. ++The second is called MYSQL_ONFAIL_CLAUSE and has the reverse meaning. You can ++declare triggers in the authmysqlrc configuration file. They can be used to ++arrange some logging facility in the database or just to keep last times ++of the successful/failed login tries. The typical trigger, which puts last ++login date into the users' table can look like this: ++ ++MYSQL_ONSUCCESS_CLAUSE UPDATE users SET last_login=CURRENT_TIMESTAMP \ ++ WHERE username='$(username)'; ++ ++or, if you would like to know about last login failure for users you can try: ++ ++MYSQL_ONFAIL_CLAUSE UPDATE users SET last_bad_login=CURRENT_TIMESTAMP \ ++ WHERE username='$(username)'; + +-Courier-imap-myownquery.patch allows administrator to set own MySQL queries +-used by authdaemon to authenticate user (including fetchig credentials) and to +-change user's password. It allows to construct SELECT or UPDATE clause in the +-configuration file (authmysqlrc) by adding two new configuration variables: +-MYSQL_SELECT_CLAUSE and MYSQL_CHPASS_CLAUSE. It may be useful in the mail +-environments where there is such a need to have different database structure +-and/or tables scheme than expected by authmysql module. ++Note, that YOU CAN use the triggers even if you aren't using ++MYSQL_SELECT_CLAUSE. Also note, that there is such a possibility that ONFAIL ++trigger may be performed without a proper username. Take it into consideration ++when creating queries to avoid messy data on INSERT operations. + +-It also implements a small parsing engine for substitution variables which +-may appear in the clauses and are used to put informations like username +-or domain into the right place of a query. + +-This patch was created using `diff -Nur` on courier-imap-1.3.12 source. + + + + ++ *----------------------- ++ 4 Examples of usage ++ *----------------------- ++ ++The "ownquery" feature gives you possibility to adapt an authentication query ++to the database. So the first thing you have to do is to design the database ++structure you need, whithout being grieved at what structure authentication ++routines like. You have to take care about four essential things: ++ ++ o The database ++ ++ o The users' data in the database ++ ++ o The proper directories for keeping virtual mailboxes and a system user ++ which can read and write them ++ ++ o The proper MySQL queries in your authmysqlrc configuration file ++ ++4.1 corporate mail system ++ ++This example is concerned about a corporate mail system with a small ++ammount of served virtual domains. The database scheme was derived from tpop3d ++documentation and modified a bit. ++ ++4.1.1 database structure ++ ++Our goal here is to separate the data responsible for keeping mailbox ++credentials from the data, which describes a domain. ++ ++Let's create some tables for our example, filled up with an example data: ++ ++table: domains ++ ++purpose: associates virtual domain with domain name and informations ++ necessary to access mailboxes withing the domain ++ ++fields: domain_name - fully qualified domain name ++ path_prefix - absolute pathname which points to ++ a directory where domain's mailboxes ++ are located ++ quota - default quota for each mailbox ++ uid - UID used to work on mailboxes ++ gid - GID used to work on mailboxes ++ ++ +----------------+-------------+-----+-----+----------+ ++ | domain_name | path_prefix | uid | gid | quota | ++ +----------------+-------------+-----+-----+----------+ ++ | exampledom.com | /var/mail/x | 555 | 555 | 10000000 | ++ | pld.org.pl | /var/mail/p | 556 | 556 | 20000000 | ++ | pld.net.pl | /var/mail/p | 556 | 556 | 20000000 | ++ +----------------+-------------+-----+-----+----------+ ++ ++table: users ++ ++purpose: associates virtual mailbox with user and domain name, ++ and with informations necessary to access mailbox ++ ++fields: username - user login name (mailbox name) ++ domain_name - fully qualified domain name ++ mailbox_path - relative pathname for mailbox ++ (will be appended to the path_prefix ++ from domain_auth table to specify ++ user's mailbox location) ++ cryptpw - crypted password ++ plainpw - plaintext password ++ ++ +----------+----------------+--------------+------------+--------+ ++ | username | domain_name | mailbox_path | cryptpw | plainpw | ++ +----------+----------------+--------------+-----------+---------+ ++ | siefca | pld.org.pl | s/siefca | $1$fs45.. | dupa.8 | ++ | siefca | pld.net.pl | s/siefca | $1$fs45.. | dupa.8 | ++ | f00bar | exampledom.com | foobar | $1$g44w.. | secret | ++ +----------+----------------+--------------+-----------+---------+ ++ ++Using MySQL monitor you can create these tables entering CREATE sequences. ++Be sure to connect to the database using administrative MySQL account ++(usualy: mysql -u mysql -p). ++ ++--------------------- cut here ++ ++# Create the database called vmail. ++ ++CREATE database vmail; ++ ++# Create an example MySQL user, which can read, write and delete data from ++# vmail database. Username: vuser Password: secret_password ++ ++GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.* ++ TO vuser@localhost ++ IDENTIFIED BY 'secret_password'; ++ ++FLUSH PRIVILEGES; ++ ++# Create the tables. ++ ++use vmail; ++ ++CREATE TABLE domains ( ++ domain_name char(255) DEFAULT '', ++ path_prefix char(255) DEFAULT '' NOT NULL, ++ uid int(10) unsigned DEFAULT '15000' NOT NULL, ++ gid int(10) unsigned DEFAULT '15000' NOT NULL, ++ quota char(255) DEFAULT '2000000' NOT NULL, ++ KEY domain_name (domain_name(255)) ++ ); ++ ++CREATE TABLE users ( ++ username char(128) DEFAULT '' NOT NULL, ++ domain_name char(255) DEFAULT '', ++ mailbox_path char(255) DEFAULT '' NOT NULL, ++ cryptpw char(128) DEFAULT '' NOT NULL, ++ clearpw char(128) DEFAULT '' NOT NULL, ++ KEY username (username(128)) ++ ); ++ ++# Create an example virtual domain entry ++# name : exampledom.com ++# uid : 555 ++# gid : 555 ++# path : /var/mail/x ++# quota : 10 Megs per mailbox ++ ++INSERT INTO domains VALUES ('exampledom.com', '/var/mail/x', 555, 555, ++ '10000000'); ++ ++# Create an example virtual user entry ++# username : siefca ++# domain name : exampledom.com ++# cryptpw : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/ ++# clearpw : dupa.8 ++# mailbox path : s/siefca ++ ++INSERT INTO users VALUES ('siefca', 'exampledom.com', 's/siefca', ++ '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/', ++ 'dupa.8'); ++ ++--------------------- cut here ++ ++Note: If you would like to have your passwords more safe then just omit the ++ clearpw column and put '' into the config-query in its place while ++ doing SELECT on a database. But be ware - you'll be unable to use ++ authentication methods which needs it, like MD5_CRAM. ++ ++4.1.2 authdaemon configuration ++ ++When our database is ready we can set up the configuration. :-) Go to ++authmysqlrc file and edit it. ++ ++At the beginning we should take care about general informations, which ++are identifying our database: ++ ++MYSQL_SERVER localhost ++MYSQL_USERNAME vuser ++MYSQL_PASSWORD secret_password ++MYSQL_DATABASE vmail ++ ++Then we should add a clause responsible for authenticating user and ++fetching credentials: ++ ++DEFAULT_DOMAIN exampledom.com ++ ++MYSQL_SELECT_CLAUSE SELECT \ ++ users.username, users.cryptpw, users.clearpw, \ ++ domains.uid, domains.gid, \ ++ CONCAT_WS('/',domains.path_prefix,users.mailbox_path), \ ++ '', domains.quota, '' \ ++ FROM users, domains \ ++ WHERE domains.domain_name='$(domain)' \ ++ AND users.username='$(local_part)' \ ++ AND domains.domain_name=users.domain_name ++ ++ ++Note the '' in the place of field which tells where user's INBOX resides ++and in place of realname field. You should use '' if you want to put an empty ++value as a query result for some field. ++ ++We also should add some configuration for changing user's password: ++ ++MYSQL_CHPASS_CLAUSE UPDATE \ ++ users \ ++ SET clearpw='$(newpass)', \ ++ cryptpw='$(newpass_crypt)' \ ++ WHERE username='$(local_part)' \ ++ AND domain_name='$(domain)' ++ ++And finally... ++Create a system user/group and a proper directory structure. In our example: ++ ++groupadd -g 555 xdomain ++useradd -u 555 -g 555 xdomain ++mkdir -p /var/mail/x/s/siefca ++chmod -R 0770 /var/mail/x ++maildirmake /var/mail/x/s/siefca/Maildir ++chown -R xdomain.xdomain /var/mail/x ++ ++Now, restart the authdaemon and see if it works. Try: telnet 0 pop3 ++ ++and type: ++ ++USER siefca [ENTER] ++PASS dupa.8 [ENTER] ++ ++You should get Ok response. ;) ++ ++4.2 virtual mail domains provider ++ ++Let's consider more complicated database scheme, where there is a need to ++associate a lot of information with the domain name, including registrant ++information, owner, etc. That implies data separation between domain name, ++user and domain additional informations (which are unwanted when ++authentication process takes place). By proper data separation I mean ++avoiding unwanted redundancy in the database. ++ ++Currently applied example doesn't care about the update password problem. ++This is due to current abilities of MySQL and authdaemon (authmysql). ++MySQL doesn't support subsequent SELECTs on UPDATE operation, and authmysql ++doesn't supports batched queries at the moment. ++ ++4.2.1 database structure ++ ++table: domain_names ++ ++purpose: associates domain_id with domain name ++ ++fields: domain_name - fully qualified domain name ++ domain_id - domain identifier ++ ++ +----------------+-----------+ ++ | domain_name | domain_id | ++ +----------------+-----------+ ++ | exampledom.com | 1 | ++ | pld.org.pl | 2 | ++ | pld.net.pl | 2 | ++ | foobare.net.uk | 3 | ++ +----------------+-----------+ ++ ++Note, that for pld.org.pl and pld.net.pl the domain identifiers are the same. ++We can create a domain aliases in such a way. :) ++ ++table: domain_auth ++ ++purpose: associates domain_id with authentication credentials ++ which are common for all users in the virtual domain ++ ++fields: domain_id - domain identifier ++ path_prefix - absolute pathname which points to ++ a directory where domain's mailboxes ++ are located ++ quota - default quota for each mailbox ++ uid - UID used to work on mailboxes ++ gid - GID used to work on mailboxes ++ ++ +------------+---------------+--------+-------+-------+ ++ | domain_id | path_prefix | quota | uid | gid | ++ +------------+---------------+--------+-------+-------+ ++ | 1 | /var/mail/ex | 100000 | 15000 | 15000 | ++ | 2 | /var/mail/pld | 555500 | 15001 | 15000 | ++ | 3 | /home/f0/mail | 8000 | 15002 | 15000 | ++ +------------+---------------+--------+-------+-------+ ++ ++table: domain_info ++ ++purpose: associates domain_id with additional informations ++ ++fields: domain_id - domain identifier ++ registrant_id - registrant identifier ++ nic_handle - NIC handle ++ owner_id - domain's owner identifier ++ expires - domain's expiration date ++ ++ +------------+---------------+------------+----------+---------+ ++ | domain_id | registrant_id | nic_handle | owner_id | expires | ++ +------------+---------------+------------+----------+---------+ ++ ++ (we don't need to say anything more about this table indeed) ++ ++table: users ++ ++purpose: associates users' identifiers with domains' identifiers ++ and infers the credentials for various virtual mailboxes ++ ++fields: username - user's login name ++ domain_id - domain identifier ++ cryptpw - crypted password ++ plainpw - plaintext password ++ quota - user's mailbox quota ++ (will override quota value set for ++ the whole virtual domain) ++ path - relative pathname for mailbox ++ (will be appended to the path_prefix ++ from domain_auth table to specify ++ user's mailbox location) ++ ++ +------------+-----------+----------+-----------+-------+------------+ ++ | username | domain_id | cryptpw | plainpw | quota | path | ++ +------------+-----------+----------+-----------+-------+------------+ ++ | foobar | 1 | $1$hlIeE | dupa.8 | NULL | f/o/foobar | ++ | breeder | 2 | $1$TWsdf | ziarno128 | 77777 | brd | ++ +------------+-----------+----------+-----------+-------+------------+ ++ ++ (you can add a realname column here, it doesn't fit to my terminal window:) ++ ++--------------------- cut here ++ ++# Create the database called vmail. ++ ++CREATE database vmail; ++ ++# Create an example MySQL user, which can read, write and delete data from ++# vmail database. Username: vuser Password: secret_password ++ ++GRANT SELECT,INSERT,UPDATE,DELETE ON vmail.* ++ TO vuser@localhost ++ IDENTIFIED BY 'secret_password'; ++ ++FLUSH PRIVILEGES; ++ ++# Create the tables. ++ ++use vmail; ++ ++CREATE TABLE domain_names ( ++ domain_id int(10) unsigned NOT NULL, ++ domain_name char(255) DEFAULT '' NOT NULL, ++ KEY domain_name (domain_name(255)) ++ ); ++ ++CREATE TABLE domain_auth ( ++ domain_id int(10) unsigned DEFAULT 1 NOT NULL, ++ uid int(10) unsigned DEFAULT '15000' NOT NULL, ++ gid int(10) unsigned DEFAULT '15000' NOT NULL, ++ path_prefix char(255) DEFAULT '' NOT NULL, ++ quota char(255) DEFAULT '20000000' NOT NULL, ++ KEY domain_id (domain_id) ++ ); ++ ++CREATE TABLE users ( ++ username char(128) DEFAULT '' NOT NULL, ++ domain_id int(10) unsigned DEFAULT 1 NOT NULL, ++ cryptpw char(128) DEFAULT '' NOT NULL, ++ plainpw char(128) DEFAULT '' NOT NULL, ++ name char(128) DEFAULT '' NOT NULL, ++ quota char(255), ++ path char(255) DEFAULT '' NOT NULL, ++ KEY username (username(128)) ++ ); ++ ++# Create an example virtual domain entry ++# id : 1 ++# name : exampledom.com ++# uid : 15000 ++# gid : 15000 ++# path : /var/mail/example ++# quota : 20 Megs per mailbox ++ ++INSERT INTO domain_names VALUES (1, 'exampledom.com'); ++INSERT INTO domain_auth VALUES (1, '15000', '15000', '/var/mail/example', ++ '20000000'); ++ ++# Create an example virtual user entry ++# username : siefca ++# domain id : 1 (points to exampledom.com) ++# cryptpw : $1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/ ++# clearpw : dupa.8 ++# name : Pawel Wilk ++# quota : NULL (we want it to be fetched from domain_auth table) ++# mailbox path : s/i/siefca ++ ++INSERT INTO users VALUES ('siefca', 1, '$1$wIfVZ8uK$qhagYAcIoZpQM83Et7c1e/', ++ 'dupa.8', 'Pawel Wilk', NULL, 's/i/siefca'); ++ ++--------------------- cut here ++ ++Ok, we've done what we need. Don't forget to create system user with UID and ++GID set to 15000, and a directory containing mailboxes (in this case: ++/var/mail/example) owned by system user I've mentioned above. ++There is also necessary to create Maildir folder structure for our user ++inside the virtual domain directory - you can configure your MTA agent to do ++such thing when first message arrive or use maildirmake tool, which comes ++with Courier-IMAP. ++ ++ ++4.2.2 authdaemon configuration ++ ++DEFAULT_DOMAIN exampledom.com ++ ++MYSQL_SELECT_CLAUSE SELECT \ ++ users.username, \ ++ users.cryptpw, \ ++ users.plainpw, \ ++ domain_auth.uid, \ ++ domain_auth.gid, \ ++ CONCAT_WS('/',domain_auth.path_prefix,users.path), \ ++ '', \ ++ IFNULL(users.quota, domain_auth.quota), \ ++ users.name \ ++ FROM users, domain_names, domain_auth \ ++ WHERE domain_names.domain_name='$(domain)' \ ++ AND users.username='$(local_part)' \ ++ AND domain_names.domain_id=users.domain_id \ ++ AND domain_names.domain_id=domain_auth.domain_id ++ ++ ++. ++. ++. ++. ++. ++. ++ ++/////////////////////////// PART II - Developer Notes ///////////////////////// + + *----------------------- + 1 Modifications overview + *----------------------- + +-Modified files: authmysqllib.c authmysqlrc ++Modified files: authmysqllib.c authmysql.c authmysql.h authmysqlrc + + Each modified set of instructions is marked by my e-mail address: + siefca@pld.org.pl + +-Changes in the current source code are related to: ++Changes in the source code are related to: + + - sections where the queries are constructed + (including memory allocation for the buffers) +@@ -102,6 +785,10 @@ + newline as the second is replaced by two whitespaces while + putting into the buffer + ++ i've also added USER_DOMAIN_CONCAT and USER_DOMAIN_SEPARATORS ++ configuration options - they're used by get_localpart(), get_domain() ++ and get_username() functions, which are described below ++ + - sections where the query is constructed + + selection is made, depending on configuration variables which +@@ -130,7 +817,16 @@ + MAX_SUBSTITUTION_LEN defines maximal length of a substitution variable's + identifier (name). + +-The last two definitions are just for code simplification. ++The last two definitions (SV_BEGIN_LEN and SV_END_LEN) are just for code ++simplification. ++ ++#define DEF_CONCAT_STRING "@" ++#define DEF_SEPARATORS_SET "@%" ++ ++The first (DEF_CONCAT_STRING) is used to set the defaults for a ++concatenation string, used when parsing $(username) substitution variable. ++The second (DEF_SEPARATORS_SET) is the set of characters, which are treated as ++separators when splitting local part from the domain. + + + +@@ -179,7 +875,7 @@ + In this example we've declared that $(some) in the query should be + replaced by 'replacement' text, and replacement for $(anotha) will + be defined in the code before passing on the array pointer to +-the paring function. ++the general parsing function. + + + 3.2 typedef size_t (*parsefunc) +@@ -230,6 +926,10 @@ + structure of var_data type, which contains variable definition + of a given name. It returns NULL on error or failure. + ++FILES ++ ++ authlib/authmysqllib.c ++ + + 4.2 parse_core + +@@ -285,6 +985,11 @@ + + This function returns -1 if an error has occured and 0 if + everything went good. ++ ++FILES ++ ++ authlib/authmysqllib.c ++ + + 4.3 ParsePlugin_counter + +@@ -314,6 +1019,11 @@ + This function returns the variable size or -1 if an error + has occured, 0 if everything went good. + ++FILES ++ ++ authlib/authmysqllib.c ++ ++ + 4.4 ParsePlugin_builder + + NAME +@@ -333,7 +1043,7 @@ + type pointer and refers to the (char *) pointer variable. + After each call it shifts the value of pointer variable (char *) + incrementing it by len bytes. Be careful when using this function +- - its changes the given pointer value. Always operate on an ++ - it changes the given pointer value. Always operate on an + additional pointer type variable when passing it as the third + argument. + +@@ -342,6 +1052,10 @@ + This function returns the variable size or -1 if an error + has occured, 0 if everything went good. + ++FILES ++ ++ authlib/authmysqllib.c ++ + 4.5 parse_string + + NAME +@@ -353,7 +1067,7 @@ + + DESCRIPTION + +- This function parses the string pointed with source according to the ++ This function parses the string pointed to by source according to the + replacement instructions set in var_data array, which is passed with + its pointer vdt. It produces changed string located in newly allocated + memory area. +@@ -377,6 +1091,10 @@ + Function returns pointer to the result buffer or NULL + if an error has occured. + ++FILES ++ ++ authlib/authmysqllib.c ++ + WARNINGS + + This function allocates some amount of memory using standard +@@ -405,6 +1123,10 @@ + It returns a pointer to the static buffer which contains + validated password string or NULL if an error has occured. + ++FILES ++ ++ authlib/authmysqllib.c ++ + + 4.7 get_localpart + +@@ -414,20 +1136,28 @@ + + SYNOPSIS + +- static const char *get_localpart (const char *username); ++ static const char *get_localpart (const char *username, ++ const char *separators); + + DESCRIPTION + + This function detaches local part of an e-mail address + from string pointed with username and puts it to the + buffer of the fixed length. All necessary cleaning is +- made on the result string. ++ made on the result string. String pointed with separators ++ refers to a set of characters, which are treated as ++ separation signs between local part and a domain. + + RETURN VALUE + + Pointer to the static buffer containing local part or + NULL if there was some error. + ++FILES ++ ++ authlib/authmysqllib.c ++ ++ + + 4.8 get_domain + +@@ -438,24 +1168,68 @@ + SYNOPSIS + + static const char *get_domain (const char *username, +- const char *defdomain); ++ const char *defdomain, ++ const char *separators); + + DESCRIPTION + + This function detaches domain part of an e-mail address + from string pointed with username and puts it to the + buffer of the fixed length. All necessary cleaning is +- made on the result string. If function cannot find domain +- part in the string the string pointed by defdomain is +- used instead. ++ made on the result string. If the function cannot find a domain ++ part in the string then the string pointed to by defdomain is ++ used instead. If this function cannot find a domain part ++ as well as it cannot obtain the default domain (it's empty string ++ or the defdomain pointer is NULL) the returned result string is an ++ empty string. The string pointed with separators refers to a set ++ of characters, which are treated as separation signs between local ++ part and a domain. + + RETURN VALUE + + Pointer to the static buffer containing domain name or + NULL if there was some error. + ++FILES ++ ++ authlib/authmysqllib.c ++ + +-4.9 parse_select_clause ++4.9 get_username ++ ++NAME ++ ++ get_username ++ ++SYNOPSIS ++ ++ static const char *get_username (const char *username, ++ const char *domainname, ++ const char *concat_str); ++ ++DESCRIPTION ++ ++ This function concatenates the localpart with a domain name ++ using the string pointed with concat_str. If the domain is ++ empty or NULL the result comes without binding string. ++ ++RETURN VALUE ++ ++ Pointer to the static buffer containing output string or ++ NULL if there was some error. ++ ++FILES ++ ++ authlib/authmysqllib.c ++ ++WARNINGS ++ ++ This function does not any string cleaning, nor default domain ++ checking. It is designed to work on results of get_localpart() and ++ get_domain(). ++ ++ ++4.10 parse_select_clause + + NAME + +@@ -465,7 +1239,9 @@ + + static char *parse_select_clause (const char *clause, + const char *username, +- const char *defdomain); ++ const char *defdomain ++ const char *concat_str, ++ const char *separators_set); + + DESCRIPTION + +@@ -473,15 +1249,21 @@ + function. It parses a query pointed by caluse. username + and defdomain strings are used to replace corresponding + substitution strings if present in the query: $(local_part) +- and $(domain). ++ and $(domain). The separators_set is passed to get_username() ++ and get_domain() invocations, and the concat_str is passed ++ to get_username() function, which is responsible for replacing ++ $(username) substitution variable. + +- + RETURN VALUE + + Same as parse_string(). + ++FILES ++ ++ authlib/authmysqllib.c + +-4.10 parse_chpass_clause ++ ++4.11 parse_chpass_clause + + NAME + +@@ -492,6 +1274,8 @@ + static char *parse_chpass_clause (const char *clause, + const char *username, + const char *defdomain, ++ const char *separators_set, ++ const char *concat_str, + const char *newpass, + const char *newpass_crypt); + +@@ -502,12 +1286,56 @@ + defdomain, newpass and newpass_crypt strings are used to + replace corresponding substitution strings if present in + the query: $(local_part), $(domain), $(newpass), +- $(newpass_crypt). ++ $(newpass_crypt). The separators_set and the concat_str ++ are passed to get_localpart(), get_domain(), and get_username() ++ functions as described in the entry for parse_select_clause(). + + RETURN VALUE + + Same as parse_string(). + ++FILES ++ ++ authlib/authmysqllib.c ++ ++ ++4.12 auth_mysql_on_trigger ++ ++NAME ++ ++ auth_mysql_on_trigger ++ ++SYNOPSIS ++ ++ int auth_mysql_on_trigger (const char *clause_name, ++ const char *username); ++ ++DESCRIPTION ++ ++ This function is responsible for calling out the MySQL queries ++ depending on which authentication state was reached. ++ ++ The clause_name should contain the name of a clause, which can be found ++ in the configuration file, and the username is simply the string used ++ as username (including the domain if entered). ++ ++ This function reads DEFAULT_DOMAIN, USER_DOMAIN_CONCAT and ++ USER_DOMAIN_SEPARATORS from the configuration file using read_env(), ++ then it uses parse_select_clause() to parse the query obtained using ++ read_env(clause_name), and then it calls querying subroutines to ++ perform the action. ++ ++RETURN VALUE ++ ++ This function returns 1 on success and 0 on failure. The query results ++ are simply discarded. If a trigger's clause is not defined in the ++ configuration file the 1 is returned and function silently ends its ++ work. ++ ++FILES ++ ++ authlib/authmysql.h ++ authlib/authmysql.c + + + +@@ -520,11 +1348,9 @@ + strings after split (problem?) + - allow admin to set a group name instead of numerical group id + - allow admin to set a username instead of numerical user id +- +-- add clauses: +- +- - MYSQL_PRESELECT_CLAUSE (query which comes before MYSQL_SELECT_CLAUSE) +- - MYSQL_POSTSELECT_CLAUSE (query which comes after MYSQL_SELECT_CLAUSE) ++- allow batched queries and register variables for keeping results ++- put the parsing routines into separate files to make possible of sharing it ++ by more authentication modules + + + +@@ -534,10 +1360,20 @@ + 6 Thanks + *------------------------ + +-At the beginning this patch was messy indeed. :> I would like to thank +-Sam Varshavchik for pointing me a lot how to make it more fast and solid. +-I would also thank Philip Hazel, Chris Lightfoot and Mike Bremford which +-by their software capabilities inspired me to write it. ++At the beginning the patch was messy indeed. :> I would like to thank: ++ ++Sam Varshavchik ++ for pointing me a lot, how to make it more fast and solid ++ ++Philip Hazel, Chris Lightfoot, Mike Bremford ++ which by their software's capabilities inspired me to write it ++ ++Oliver Oblasnik ++ which remainded me to make the documentation more friendly for ++ those who are not programmers and just want to use it ++ ++Jacek Surazski ++ for reviewing this document just before it was published + + --------------------------------------------------------------------------- + +diff -ur courier-imap-1.5.3.20020921.orig/authlib/authmysql.c courier-imap-1.5.3.20020921/authlib/authmysql.c +--- courier-imap-1.5.3.20020921.orig/authlib/authmysql.c Mon Aug 19 17:52:28 2002 ++++ courier-imap-1.5.3.20020921/authlib/authmysql.c Sat Sep 28 00:01:07 2002 +@@ -31,7 +31,11 @@ + if ((user=strtok(authdata, "\n")) == 0 || + (pass=strtok(0, "\n")) == 0) + { +- errno=EPERM; ++ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user)) ++ errno=EACCES; ++ else ++ errno=EPERM; ++ + return (0); + } + +@@ -50,7 +54,11 @@ + { + if (authcheckpassword(pass,authinfo->cryptpw)) + { +- errno=EPERM; ++ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user)) ++ errno=EACCES; ++ else ++ errno=EPERM; ++ + return (0); /* User/Password not found. */ + } + } +@@ -58,13 +66,21 @@ + { + if (strcmp(pass, authinfo->clearpw)) + { +- errno=EPERM; ++ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user)) ++ errno=EACCES; ++ else ++ errno=EPERM; ++ + return (0); + } + } + else + { +- errno=EPERM; ++ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user)) ++ errno=EACCES; ++ else ++ errno=EPERM; ++ + return (0); /* Username not found */ + } + +@@ -132,6 +148,12 @@ + (*callback_func)(&aa, callback_arg); + } + ++ if (!auth_mysql_on_trigger("MYSQL_ONSUCCESS_CLAUSE", user)) ++ { ++ errno=EACCES; ++ return (0); ++ } ++ + return (strdup(authinfo->username)); + } + +@@ -153,7 +175,11 @@ + { + if (authcheckpassword(pass,authinfo->cryptpw)) + { +- errno=EPERM; ++ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user)) ++ errno=EACCES; ++ else ++ errno=EPERM; ++ + return (-1); /* User/Password not found. */ + } + } +@@ -161,13 +187,21 @@ + { + if (strcmp(pass, authinfo->clearpw)) + { +- errno=EPERM; ++ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user)) ++ errno=EACCES; ++ else ++ errno=EPERM; ++ + return (-1); + } + } + else + { +- errno=EPERM; ++ if (!auth_mysql_on_trigger("MYSQL_ONFAIL_CLAUSE", user)) ++ errno=EACCES; ++ else ++ errno=EPERM; ++ + return (-1); + } + +@@ -176,6 +210,13 @@ + errno=EPERM; + return (-1); + } ++ ++ if (!auth_mysql_on_trigger("MYSQL_ONSUCCESS_CLAUSE", user)) ++ { ++ errno=EACCES; ++ return (-1); ++ } ++ + return (0); + } + +diff -ur courier-imap-1.5.3.20020921.orig/authlib/authmysql.h courier-imap-1.5.3.20020921/authlib/authmysql.h +--- courier-imap-1.5.3.20020921.orig/authlib/authmysql.h Mon Aug 6 05:12:39 2001 ++++ courier-imap-1.5.3.20020921/authlib/authmysql.h Sat Sep 28 00:01:07 2002 +@@ -21,6 +21,7 @@ + } ; + + extern struct authmysqluserinfo *auth_mysql_getuserinfo(const char *); ++extern int auth_mysql_on_trigger (const char *clause_name, const char *username); + extern void auth_mysql_cleanup(); + + extern int auth_mysql_setpass(const char *, const char *); +diff -ur courier-imap-1.5.3.20020921.orig/authlib/authmysqllib.c courier-imap-1.5.3.20020921/authlib/authmysqllib.c +--- courier-imap-1.5.3.20020921.orig/authlib/authmysqllib.c Sun Aug 11 22:01:25 2002 ++++ courier-imap-1.5.3.20020921/authlib/authmysqllib.c Sat Sep 28 06:06:41 2002 +@@ -24,6 +24,9 @@ + #define SV_BEGIN_LEN ((sizeof(SV_BEGIN_MARK))-1) + #define SV_END_LEN ((sizeof(SV_END_MARK))-1) + ++#define DEF_CONCAT_STRING "@" ++#define DEF_SEPARATORS_SET "@%" ++ + static const char rcsid[]="$Id$"; + + /* siefca@pld.org.pl */ +@@ -268,7 +271,7 @@ + SV_BEGIN_MARK + "%.*s" + SV_END_MARK +- "\n", len, begin); ++ "\n", (int) len, begin); + + return NULL; + } +@@ -426,21 +429,43 @@ + return NULL; + } + *pass_buf = '\0'; +- ++ + return output_buf; + } + + /* siefca@pld.org.pl */ +-static const char *get_localpart (const char *username) ++static const char *get_username (const char *username, const char *domainname, ++ const char *concat_str) ++{ ++static char username_buf[400]; ++ ++ if (!username || !domainname || !concat_str || ++ *username == '\0' || *concat_str == '\0') return NULL; ++ if (( strlen(username) + ++ strlen(concat_str) + ++ strlen(domainname)) > 397) return NULL; ++ ++ if (*domainname == '\0') ++ strcpy (username_buf, username); ++ else ++ sprintf (username_buf, "%s%s%s", username, concat_str, ++ domainname); ++ ++ return (username_buf); ++} ++ ++/* siefca@pld.org.pl */ ++static const char *get_localpart (const char *username, const char *separators) + { + size_t lbuf = 0; + const char *l_end, *p; + char *q; + static char localpart_buf[130]; + +- if (!username || *username == '\0') return NULL; ++ if (!username || *username == '\0' || ++ !separators || *separators == '\0') return NULL; + +- p = strchr(username,'@'); ++ p = strpbrk (username, separators); + if (p) + { + if ((p-username) > 128) +@@ -469,21 +494,27 @@ + } + + /* siefca@pld.org.pl */ +-static const char *get_domain (const char *username, const char *defdomain) ++static const char *get_domain (const char *username, const char *defdomain, ++ const char *separators) + { + static char domain_buf[260]; + const char *p; + char *q; + +- if (!username || *username == '\0') return NULL; +- p = strchr(username,'@'); ++ if (!username || *username == '\0' || ++ !separators || *separators == '\0') return NULL; ++ ++ p = strpbrk (username, separators); + + if (!p || *(p+1) == '\0') + { +- if (defdomain && *defdomain) ++ if (defdomain && *defdomain != '\0') + return defdomain; + else +- return NULL; ++ { ++ *domain_buf = '\0'; ++ return domain_buf; ++ } + } + + p++; +@@ -536,20 +567,27 @@ + + /* siefca@pld.org.pl */ + static char *parse_select_clause (const char *clause, const char *username, +- const char *defdomain) ++ const char *defdomain, ++ const char *concat_str, ++ const char *separators_set) + { + static struct var_data vd[]={ + {"local_part", NULL, sizeof("local_part"), 0}, + {"domain", NULL, sizeof("domain"), 0}, ++ {"username", NULL, sizeof("username"), 0}, + {NULL, NULL, 0, 0}}; + + if (clause == NULL || *clause == '\0' || +- !username || *username == '\0') ++ !username || *username == '\0' || ++ !concat_str || *concat_str == '\0' || ++ !separators_set || *separators_set == '\0') + return NULL; + +- vd[0].value = get_localpart (username); +- vd[1].value = get_domain (username, defdomain); +- if (!vd[0].value || !vd[1].value) ++ vd[0].value = get_localpart (username, separators_set); ++ vd[1].value = get_domain (username, defdomain, separators_set); ++ vd[2].value = get_username (vd[0].value, vd[1].value, concat_str); ++ ++ if (!vd[0].value || !vd[1].value || !vd[2].value) + return NULL; + + return (parse_string (clause, vd)); +@@ -557,12 +595,16 @@ + + /* siefca@pld.org.pl */ + static char *parse_chpass_clause (const char *clause, const char *username, +- const char *defdomain, const char *newpass, ++ const char *defdomain, ++ const char *separators_set, ++ const char *concat_str, ++ const char *newpass, + const char *newpass_crypt) + { + static struct var_data vd[]={ + {"local_part", NULL, sizeof("local_part"), 0}, + {"domain", NULL, sizeof("domain"), 0}, ++ {"username", NULL, sizeof("username"), 0}, + {"newpass", NULL, sizeof("newpass"), 0}, + {"newpass_crypt", NULL, sizeof("newpass_crypt"), 0}, + {NULL, NULL, 0, 0}}; +@@ -570,19 +612,83 @@ + if (clause == NULL || *clause == '\0' || + !username || *username == '\0' || + !newpass || *newpass == '\0' || ++ !separators_set || *separators_set == '\0' || + !newpass_crypt || *newpass_crypt == '\0') return NULL; + +- vd[0].value = get_localpart (username); +- vd[1].value = get_domain (username, defdomain); +- vd[2].value = validate_password (newpass); +- vd[3].value = validate_password (newpass_crypt); ++ vd[0].value = get_localpart (username, separators_set); ++ vd[1].value = get_domain (username, defdomain, separators_set); ++ vd[3].value = get_username (vd[0].value, vd[1].value, concat_str); ++ vd[4].value = validate_password (newpass); ++ vd[5].value = validate_password (newpass_crypt); + + if (!vd[0].value || !vd[1].value || +- !vd[2].value || !vd[3].value) return NULL; ++ !vd[2].value || !vd[3].value || ++ !vd[4].value || !vd[5].value) return NULL; + + return (parse_string (clause, vd)); + } + ++/* siefca@pld.org.pl */ ++int auth_mysql_on_trigger (const char *clause_name, const char *username) ++{ ++char *querybuf =NULL; ++const char *concat_str =NULL, ++ *separators_set =NULL, ++ *defdomain =NULL, ++ *on_clause =NULL; ++MYSQL_RES *result; ++ ++ if (!clause_name || *clause_name == '\0') return (0); ++ on_clause = read_env (clause_name); ++ if (!on_clause || *on_clause == '\0') return (1); ++ ++ defdomain = read_env ("DEFAULT_DOMAIN"); ++ concat_str = read_env ("USER_DOMAIN_CONCAT"); ++ separators_set = read_env ("USER_DOMAIN_SEPARATORS"); ++ if (!defdomain) defdomain = ""; ++ if (!concat_str || *concat_str == '\0') ++ concat_str = DEF_CONCAT_STRING; ++ if (!separators_set || *separators_set == '\0') ++ separators_set = DEF_SEPARATORS_SET; ++ ++ querybuf = parse_select_clause (on_clause, ++ username, ++ defdomain, ++ concat_str, ++ separators_set); ++ ++ if (!querybuf) return (0); ++ ++ if (mysql_query (mysql, querybuf)) ++ { ++ /* */ ++ ++ auth_mysql_cleanup(); ++ ++ if (do_connect()) ++ { ++ free(querybuf); ++ return (1); ++ } ++ ++ if (mysql_query (mysql, querybuf)) ++ { ++ free(querybuf); ++ auth_mysql_cleanup(); ++ /* Server went down, that's OK, ++ ** try again next time. ++ */ ++ return (1); ++ } ++ } ++ free(querybuf); ++ result = mysql_store_result(mysql); ++ if (result) mysql_free_result(result); ++ ++ return (1); ++} ++ ++ + struct authmysqluserinfo *auth_mysql_getuserinfo(const char *username) + { + const char *user_table =NULL; +@@ -601,6 +707,8 @@ + *gid_field =NULL, + *quota_field =NULL, + *where_clause =NULL, ++ *concat_str =NULL, ++ *separators_set =NULL, + *select_clause =NULL; /* siefca@pld.org.pl */ + + static const char query[]= +@@ -709,7 +817,19 @@ + else + { + /* siefca@pld.org.pl */ +- querybuf=parse_select_clause (select_clause, username, defdomain); ++ concat_str = read_env ("USER_DOMAIN_CONCAT"); ++ separators_set = read_env ("USER_DOMAIN_SEPARATORS"); ++ ++ if (!concat_str || *concat_str == '\0') ++ concat_str = DEF_CONCAT_STRING; ++ if (!separators_set || *separators_set == '\0') ++ separators_set = DEF_SEPARATORS_SET; ++ ++ querybuf = parse_select_clause (select_clause, ++ username, ++ defdomain, ++ concat_str, ++ separators_set); + if (!querybuf) return 0; + } + +@@ -793,6 +913,8 @@ + *where_clause =NULL, + *user_table =NULL, + *login_field =NULL, ++ *concat_str =NULL, ++ *separators_set =NULL, + *chpass_clause =NULL; /* siefca@pld.org.pl */ + + if (!mysql) +@@ -842,13 +964,22 @@ + } + else + { ++ concat_str = read_env ("USER_DOMAIN_CONCAT"); ++ separators_set = read_env ("USER_DOMAIN_SEPARATORS"); ++ ++ if (!concat_str || *concat_str == '\0') ++ concat_str = DEF_CONCAT_STRING; ++ if (!separators_set || *separators_set == '\0') ++ separators_set = DEF_SEPARATORS_SET; ++ + sql_buf=parse_chpass_clause(chpass_clause, + user, + defdomain, ++ concat_str, ++ separators_set, + pass, + newpass_crypt_ptr); + } +- + + if (!sql_buf) + { +diff -ur courier-imap-1.5.3.20020921.orig/authlib/authmysqlrc courier-imap-1.5.3.20020921/authlib/authmysqlrc +--- courier-imap-1.5.3.20020921.orig/authlib/authmysqlrc Thu Apr 4 06:36:29 2002 ++++ courier-imap-1.5.3.20020921/authlib/authmysqlrc Sat Sep 28 02:46:41 2002 +@@ -1,4 +1,4 @@ +-##VERSION: $Id$ ++##VERSION: $Id$ + # + # Copyright 2000 Double Precision, Inc. See COPYING for + # distribution information. +@@ -141,65 +141,99 @@ + # + # MYSQL_WHERE_CLAUSE server='mailhost.example.com' + +-##NAME: MYSQL_SELECT_CLAUSE:0 +-# +-# (EXPERIMENTAL) +-# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database, +-# which is structuraly different from proposed. The fixed string will +-# be used to do a SELECT operation on database, which should return fields +-# in order specified bellow: +-# +-# username, cryptpw, uid, gid, clearpw, home, maildir, quota, fullname ++##NAME: USER_DOMAIN_CONCAT:0 + # +-# Enabling this option causes ignorance of any other field-related +-# options, excluding default domain. ++# This is optional. Here, you can write the string used to concatenate ++# username with domain part while expanding the $(username) substitution ++# variable. If it's not set the '@' character is used. ++# See README.authmysql.myownquery for more information + # +-# There are two variables, which you can use. Substitution will be made +-# for them, so you can put entered username (local part) and domain name +-# in the right place of your query. These variables are: +-# $(local_part) and $(domain) ++# USER_DOMAIN_CONCAT @ ++ ++##NAME: USER_DOMAIN_SEPARATORS:0 + # +-# If a $(domain) is empty (not given by the remote user) the default domain +-# name is used in its place. ++# This is optional. Using this option you can set the set of characters ++# which are treated as separators when splitting entered username into the ++# local part and the domain name. If it's not set the defaults @% are used, ++# so the user can authenticate using user@domain or user%domain form. ++# See README.authmysql.myownquery for more information + # +-# This example is a little bit modified adaptation of vmail-sql +-# database scheme: ++# USER_DOMAIN_SEPARATORS @%+ ++ ++##NAME: MYSQL_SELECT_CLAUSE:0 + # +-# MYSQL_SELECT_CLAUSE SELECT popbox.local_part, \ +-# CONCAT('{MD5}', popbox.password_hash), \ +-# popbox.clearpw, \ +-# domain.uid, \ +-# domain.gid, \ +-# CONCAT(domain.path, '/', popbox.mbox_name), \ +-# '', \ +-# domain.quota, \ +-# '', \ +-# FROM popbox, domain \ +-# WHERE popbox.local_part = '$(local_part)' \ +-# AND popbox.domain_name = '$(domain)' \ +-# AND popbox.domain_name = domain.domain_name ++# This is optional, MYSQL_SELECT_CLAUSE can be set when you have a database, ++# which is structuraly different from proposed. You can type here your MySQL ++# query, which will be used to fetch user's credentials, and which should ++# return fields in order specified bellow: ++# ++# username, cryptpw, clearpw, uid, gid, home, maildir, quota, fullname ++# ++# Enabling this option causes ignorance of any other field-related options. ++# ++# There also are variables, which you can use. Substitution will be made ++# for them, so you can pass currently entered username and a domain name ++# up to the right place within your query. These variables are: ++# $(local_part) , $(domain) , $(username) + # ++# If a $(domain) is empty (not given by the remote user) the default domain ++# name is used in its place. $(username) is a local part concatenated with ++# domain name using symbol defined in USER_DOMAIN_CONCAT or '@' if this option ++# is not set. ++# See README.authmysql.myownquery for more information ++# ++# MYSQL_SELECT_CLAUSE SELECT \ ++# users.username, users.cryptpw, users.clearpw, \ ++# domains.uid, domains.gid, \ ++# CONCAT_WS('/',domains.path_prefix,users.mailbox_path), \ ++# '', domains.quota, '' \ ++# FROM users, domains \ ++# WHERE domains.domain_name='$(domain)' \ ++# AND users.username='$(local_part)' \ ++# AND domains.domain_name=users.domain_name ++ + ##NAME: MYSQL_CHPASS_CLAUSE:0 + # +-# (EXPERIMENTAL) + # This is optional, MYSQL_CHPASS_CLAUSE can be set when you have a database, +-# which is structuraly different from proposed. The fixed string will +-# be used to do an UPDATE operation on database. In other words, it is +-# used, when changing password. ++# which is structuraly different from proposed. You can use it to set up ++# a MySQL query used to change user's password. + # + # There are four variables, which you can use. Substitution will be made +-# for them, so you can put entered username (local part) and domain name +-# in the right place of your query. There variables are: +-# $(local_part) , $(domain) , $(newpass) , $(newpass_crypt) ++# for them, so you can put the currently entered username and the domain name ++# in the right place of your query. These variables are: ++# $(local_part) , $(domain) , $(username) , $(newpass) , $(newpass_crypt) + # + # If a $(domain) is empty (not given by the remote user) the default domain +-# name is used in its place. +-# $(newpass) contains plain password +-# $(newpass_crypt) contains its crypted form +-# +-# MYSQL_CHPASS_CLAUSE UPDATE popbox \ +-# SET clearpw='$(newpass)', \ +-# password_hash='$(newpass_crypt)' \ +-# WHERE local_part='$(local_part)' \ +-# AND domain_name='$(domain)' ++# name is used in its place. $(newpass) contains plain password and ++# $(newpass_crypt) contains its crypted form. ++# See README.authmysql.myownquery for more information ++# ++# MYSQL_CHPASS_CLAUSE UPDATE users \ ++# SET clearpw='$(newpass)', \ ++# cryptpw='$(newpass_crypt)' \ ++# WHERE username='$(local_part)' \ ++# AND domain_name='$(domain)' ++ ++##NAME: MYSQL_ONSUCCESS_CLAUSE:0 ++# (EXPERIMENTAL) ++# ++# This is optional, MYSQL_ONSUCCESS_CLAUSE is a trigger - the query is performed ++# each time user has successfuly logged in. ++# See README.authmysql.myownquery for more information ++# ++# MYSQL_ONSUCCESS_CLAUSE UPDATE users \ ++# SET last_ok=CURRENT_TIMESTAMP \ ++# WHERE username='$(local_part)' \ ++# AND domain_name='$(domain)' ++ ++##NAME: MYSQL_ONFAIL_CLAUSE:0 ++# (EXPERIMENTAL) + # ++# This is optional, MYSQL_ONFAIL_CLAUSE is a trigger - the query is performed ++# each time user has successfuly logged in. ++# See README.authmysql.myownquery for more information ++# ++# MYSQL_ONFAIL_CLAUSE UPDATE users \ ++# SET last_fail=CURRENT_TIMESTAMP \ ++# WHERE username='$(local_part)' \ ++# AND domain_name='$(domain)' diff --git a/courier-imap-authdaemon.init b/courier-imap-authdaemon.init index 12fab09..522e473 100644 --- a/courier-imap-authdaemon.init +++ b/courier-imap-authdaemon.init @@ -46,8 +46,30 @@ case "$1" in fi ;; restart|reload) + # restart related services, if running.. + if [ -f /var/lock/subsys/courier-mta -a \ + -x /etc/rc.d/init.d/courier-mta ]; then + /etc/rc.d/init.d/courier-mta stop + crun=1 + fi + if [ -f /var/lock/subsys/courier-imap -a \ + -x /etc/rc.d/init.d/courier-imap ]; then + /etc/rc.d/init.d/courier-imap stop + imaprun=1 + fi + if [ -f /var/lock/subsys/courier-imap-pop3 -a \ + -x /etc/rc.d/init.d/courier-imap-pop3 ]; then + /etc/rc.d/init.d/courier-imap-pop3 stop + poprun=1 + fi $0 stop $0 start + [[ $imaprun -eq 1 ]] && /etc/rc.d/init.d/courier-imap start + [[ $poprun -eq 1 ]] && /etc/rc.d/init.d/courier-imap-pop3 start + [[ $crun -eq 1 ]] && /etc/rc.d/init.d/courier-mta start + unset crun + unset poprun + unset imaprun ;; status) status authdaemond.$METHOD diff --git a/courier-imap-pop3.init b/courier-imap-pop3.init index 380c4e6..70440d6 100644 --- a/courier-imap-pop3.init +++ b/courier-imap-pop3.init @@ -37,7 +37,7 @@ case "$1" in `sed -n '/^#/d;/=/p' $RPM_BUILD_ROOT%{_mandir}/man8/authcram.8 @@ -335,7 +336,7 @@ fi %files common %defattr(644,root,root,755) -%doc README* imap/BUGS AUTHORS +%doc README* imap/BUGS ChangeLog AUTHORS %attr(640,root,root) %config(noreplace) %verify(not size mtime md5) /etc/sysconfig/authdaemon %attr(754,root,root) /etc/rc.d/init.d/authdaemon %attr(700,root,root) /var/lib/authdaemon @@ -348,7 +349,7 @@ fi %attr(755,root,root) %{_libexecdir}/authlib/authdaemon %attr(755,root,root) %{_libexecdir}/authlib/authdaemond.plain %attr(755,root,root) %{_libexecdir}/couriertcpd -%attr(755,root,root) %{_libexecdir}/logger +%attr(755,root,root) %{_libexecdir}/courierlogger %attr(755,root,root) %{_libexecdir}/makedatprog %{_mandir}/man8/auth[cdsuv]* %{_mandir}/man8/authp[aw]* @@ -369,7 +370,7 @@ fi %files deliverquota %defattr(644,root,root,755) -%attr(755,root,root) %{_libexecdir}/deliverquota +%attr(755,root,root) %{_bindir}/deliverquota %{_mandir}/man8/deliverquota* %files maildirmake