--- ../qmail-1.03/Makefile Mon Jun 15 18:53:16 1998 +++ Makefile Fri May 11 15:18:17 2001 @@ -1,9 +1,61 @@ # Don't edit Makefile! Use conf-* for configuration. +# uncomment -lz if you get compilation errors about compress +# uncomment -lsocket -lnsl if you get compilation errors about sockets +MYSQL_LIBS=/opt/mysql/lib/mysql/libmysqlclient.a -lm -lz #-lsocket -lnsl +MYSQL_INCLUDE=-I/opt/mysql/include +LOGGING=-DO_NOT_LOG_CONNECTS #-DEBUG_DEBUG_DEBUG #-DO_NOT_LOG +# read README.queries before uncommenting the next two lines +ALIAS_QUERY_STYLE=#-DFUNKY_ALIAS_QUERIES -DOIL_AND_WATER -DALIAS_HAS_PRECEDENCE +VIRTUAL_QUERY_STYLE=#-DFUNKY_VIRTUAL_QUERIES -DHOST_HAS_PRECEDENCE +QUERY_STYLE=$(ALIAS_QUERY_STYLE) $(VIRTUAL_QUERY_STYLE) +OS_SPECIFIC=#-DSOLARIS_STUPIDITY +# MAGIC_FORWARDING is not recommended: read the changelog online +MAGIC=#-DMAGIC_FORWARDING -DMAGIC_FORWARD_USER="\"forward\"" -DMAGIC_LOGGING SHELL=/bin/sh default: it +connect_mysql.o: compile connect_mysql.c + ./compile connect_mysql.c $(MYSQL_INCLUDE) $(LOGGING) + +vdoms_mysql.o: compile vdoms_mysql.c mysql_queries.h + ./compile vdoms_mysql.c $(MYSQL_INCLUDE) $(LOGGING) $(QUERY_STYLE) $(MAGIC) + +rcpthosts_mysql.o: compile rcpthosts_mysql.c mysql_queries.h + ./compile rcpthosts_mysql.c $(MYSQL_INCLUDE) $(LOGGING) + +checkuser_mysql.o: compile checkuser_mysql.c + ./compile checkuser_mysql.c $(MYSQL_INCLUDE) + +getpw_mysql.o: compile getpw_mysql.c mysql_queries.h + ./compile getpw_mysql.c $(MYSQL_INCLUDE) + +dotqmail_mysql.o: compile dotqmail_mysql.c mysql_queries.h + ./compile dotqmail_mysql.c $(MYSQL_INCLUDE) $(LOGGING) $(QUERY_STYLE) + +do_query.o: compile do_query.c + ./compile do_query.c $(MYSQL_INCLUDE) $(LOGGING) + +make_query.o: compile make_query.c mysql_queries.h + ./compile make_query.c $(MYSQL_INCLUDE) + +addrexp.o: \ +compile addrexp.c + ./compile addrexp.c + +addrexp: \ +load addrexp.o connect_mysql.o make_query.o do_query.o \ +checkuser_mysql.o getpw_mysql.o vdoms_mysql.o \ +qsutil.o auto_break.o auto_qmail.o fmt_ulong.o fmt_uint.o fmt_uint0.o \ +open_read.o stralloc.a str.a getln.a case.a substdio.a error.a alloc.a \ +constmap.o control.o scan_ulong.o mysql_queries.h + ./load addrexp constmap.o control.o scan_ulong.o \ + connect_mysql.o qsutil.o make_query.o do_query.o checkuser_mysql.o \ + getpw_mysql.o vdoms_mysql.o auto_break.o auto_qmail.o fmt_ulong.o \ + fmt_uint.o fmt_uint0.o open_read.o getln.a substdio.a stralloc.a \ + str.a error.a alloc.a case.a $(MYSQL_LIBS) + addresses.0: \ addresses.5 nroff -man addresses.5 > addresses.0 @@ -805,7 +857,7 @@ qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \ qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \ dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \ -forward preline condredirect bouncesaying except maildirmake \ +forward preline condredirect bouncesaying except maildirmake addrexp \ maildir2mbox maildirwatch qail elq pinq idedit install-big install \ instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \ binm3 binm3+df @@ -1109,9 +1161,14 @@ qmail-getpw: \ load qmail-getpw.o case.a substdio.a error.a str.a fs.a auto_break.o \ -auto_usera.o - ./load qmail-getpw case.a substdio.a error.a str.a fs.a \ - auto_break.o auto_usera.o +qsutil.o auto_usera.o auto_qmail.o connect_mysql.o getpw_mysql.o \ +make_query.o do_query.o scan_ulong.o open_read.o \ +getln.a stralloc.a alloc.a substdio.a env.a + ./load qmail-getpw \ + qsutil.o auto_break.o auto_usera.o auto_qmail.o connect_mysql.o \ + getpw_mysql.o make_query.o do_query.o scan_ulong.o open_read.o \ + case.a substdio.a error.a str.a fs.a \ + getln.a stralloc.a alloc.a substdio.a env.a str.a $(MYSQL_LIBS) qmail-getpw.0: \ qmail-getpw.8 @@ -1129,7 +1186,7 @@ compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \ error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \ qlx.h - ./compile qmail-getpw.c + ./compile qmail-getpw.c $(OS_SPECIFIC) qmail-header.0: \ qmail-header.5 @@ -1172,14 +1229,16 @@ qmail-local: \ load qmail-local.o qmail.o quote.o now.o gfrom.o myctime.o \ -slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ +qsutil.o slurpclose.o connect_mysql.o do_query.o \ +dotqmail_mysql.o case.a getln.a getopt.a sig.a open.a seek.a lock.a fd.a \ wait.a env.a stralloc.a alloc.a strerr.a substdio.a error.a str.a \ fs.a datetime.a auto_qmail.o auto_patrn.o socket.lib ./load qmail-local qmail.o quote.o now.o gfrom.o myctime.o \ - slurpclose.o case.a getln.a getopt.a sig.a open.a seek.a \ + qsutil.o slurpclose.o connect_mysql.o do_query.o \ + dotqmail_mysql.o case.a getln.a getopt.a sig.a open.a seek.a \ lock.a fd.a wait.a env.a stralloc.a alloc.a strerr.a \ substdio.a error.a str.a fs.a datetime.a auto_qmail.o \ - auto_patrn.o `cat socket.lib` + auto_patrn.o `cat socket.lib` $(MYSQL_LIBS) qmail-local.0: \ qmail-local.8 @@ -1480,15 +1539,18 @@ qmail-send: \ load qmail-send.o qsutil.o control.o constmap.o newfield.o prioq.o \ -trigger.o fmtqfn.o quote.o now.o readsubdir.o qmail.o date822fmt.o \ +trigger.o fmtqfn.o quote.o now.o readsubdir.o connect_mysql.o \ +checkuser_mysql.o getpw_mysql.o vdoms_mysql.o make_query.o do_query.o qmail.o date822fmt.o auto_break.o \ datetime.a case.a ndelay.a getln.a wait.a seek.a fd.a sig.a open.a \ lock.a stralloc.a alloc.a substdio.a error.a str.a fs.a auto_qmail.o \ auto_split.o ./load qmail-send qsutil.o control.o constmap.o newfield.o \ prioq.o trigger.o fmtqfn.o quote.o now.o readsubdir.o \ - qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a \ + connect_mysql.o checkuser_mysql.o getpw_mysql.o vdoms_mysql.o \ + make_query.o do_query.o qmail.o date822fmt.o datetime.a case.a ndelay.a getln.a auto_break.o \ wait.a seek.a fd.a sig.a open.a lock.a stralloc.a alloc.a \ - substdio.a error.a str.a fs.a auto_qmail.o auto_split.o + substdio.a error.a str.a fs.a auto_qmail.o auto_split.o \ + $(MYSQL_LIBS) qmail-send.0: \ qmail-send.8 @@ -1509,7 +1571,7 @@ scan.h case.h auto_qmail.h trigger.h newfield.h stralloc.h quote.h \ qmail.h substdio.h qsutil.h prioq.h datetime.h gen_alloc.h constmap.h \ fmtqfn.h readsubdir.h direntry.h - ./compile qmail-send.c + ./compile qmail-send.c $(MYSQL_INCLUDE) qmail-showctl: \ load qmail-showctl.o auto_uids.o control.o open.a getln.a stralloc.a \ @@ -1534,15 +1596,17 @@ qmail-smtpd: \ load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \ -date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \ +date822fmt.o now.o qmail.o connect_mysql.o rcpthosts_mysql.o \ +qsutil.o do_query.o cdb.a fd.a wait.a datetime.a getln.a \ open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \ fs.a auto_qmail.o socket.lib ./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \ timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \ - received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \ + qsutil.o received.o date822fmt.o now.o qmail.o connect_mysql.o \ + rcpthosts_mysql.o do_query.o cdb.a fd.a wait.a \ datetime.a getln.a open.a sig.a case.a env.a stralloc.a \ alloc.a substdio.a error.a str.a fs.a auto_qmail.o `cat \ - socket.lib` + socket.lib` $(MYSQL_LIBS) qmail-smtpd.0: \ qmail-smtpd.8 @@ -1950,11 +2014,11 @@ stralloc.a: \ makelib stralloc_eady.o stralloc_pend.o stralloc_copy.o \ stralloc_opys.o stralloc_opyb.o stralloc_cat.o stralloc_cats.o \ -stralloc_catb.o stralloc_arts.o +stralloc_catb.o stralloc_arts.o stralloc_free.o ./makelib stralloc.a stralloc_eady.o stralloc_pend.o \ stralloc_copy.o stralloc_opys.o stralloc_opyb.o \ stralloc_cat.o stralloc_cats.o stralloc_catb.o \ - stralloc_arts.o + stralloc_arts.o stralloc_free.o stralloc_arts.o: \ compile stralloc_arts.c byte.h str.h stralloc.h gen_alloc.h @@ -1975,6 +2039,10 @@ stralloc_copy.o: \ compile stralloc_copy.c byte.h stralloc.h gen_alloc.h ./compile stralloc_copy.c + +stralloc_free.o: \ +compile stralloc_free.c stralloc.h gen_alloc.h + ./compile stralloc_free.c stralloc_eady.o: \ compile stralloc_eady.c alloc.h stralloc.h gen_alloc.h \ --- ../qmail-1.03/README.queries Fri May 11 16:38:34 2001 +++ README.queries Thu May 10 10:07:44 2001 @@ -0,0 +1,213 @@ +All about the new "FUNKY" queries +================================= + Starting with version 1.1.2 of these patches, you can now choose to +use "FUNKY" queries on the virtual and alias tables. To decide to use +these new queries, you must uncomment -DFUNKY_ALIAS_QUERIES and/or +-DFUNKY_VIRTUAL_QUERIES in the Makefile. The old-style behaviour will +be the default. + In a nutshell, FUNKY queries let you have SQL wildcards in certain +fields, enabling you, for example, to catch all postmaster addresses +at virtual domains you host by mapping postmaster@% to alias-postmaster. + +IMPORTANT +========= + Because of the nature of these new queries, using wildcards and so +on, the possibility exists for you to deliver mail to THE WRONG +PEOPLE if you don't think carefully about the setup you're going to +use. Read on to get the details and decide if you want to use this +new functionality. + +The old virtual queries +======================= + Given an address of the form vu@vh, where vu is the virtual username +and vh is the virtual host, the following query is generated in order +to find the local user we should deliver to: + + select username, ext from virtual where + (virtual_username='vu' or virtual_username='') and + virtual_host='vh' order by virtual_username desc limit 1 + + By select rows where the virtual username is blank OR where it +matches exactly we can have wildcard matches. By ordering by the +username in descending order we ensure that the exact match comes out +before the wildcard, which is important because the limit 1 ensures +that we (correctly) only get one row back. + + Hence, given a table of the form: + + +---------------+-----+------------------+--------------+ + | username | ext | virtual_username | virtual_host | + +---------------+-----+------------------+--------------+ + | user_one | | one | domain.tld | + | user_two | | two | domain.tld | + | user_three | | | domain.tld | + +---------------+-----+------------------+--------------+ + +we can see at a glance that mail for one@domain.tld goes to user_one, +mail for two@domain.tld goes to user_two and mail for xxx@domain.tld, +where xxx is anything you like (DJB would say xxx is a member of the +complement of the set { "one", "two" } in the set of valid "username +parts" of mail addresses :-), goes to user_three. + Easy. + +The new queries +=============== + Here's the query that gets sent to the database if you #define +FUNKY_VIRTUAL_QUERIES: + + select username, ext, virtual_host rlike '[%_]' as wild_h, + virtual_username rlike '[%_]' as wild_u from virtual where + 'vu' like virtual_username and 'vh' like virtual_host + order by wild_u, wild_h limit 1 + + See, they ain't called FUNKY for nothing! Let's have a close look. + + Selecting username and ext is simple enough but what's this +"virtual_host rlike '[%_]' as wild_h" stuff? Well it simply matches +rows in the virtual table where the value contains a wildcard +character (% or _) and assigns the value 0 (false) or 1 (true) to +the field of wild_h. Similarly for virtual_username. This is +useful because we then order by the values of wild_u and wild_h, +effectively forcing rows without wildcards to the front. In other +words we trust entries more the fewer wildcards they contain. + Here comes the example. I've tacked on the values of wild_u and +wild_h as they would appear in the result set. + + +---------------+-----+------------------+--------------+--------+--------+ + | username | ext | virtual_username | virtual_host | wild_u | wild_h | + +---------------+-----+------------------+--------------+--------+--------+ + | user_one | | one | domain.tld | 0 | 0 | + | user_two | | % | domain.tld | 1 | 0 | + | user_three | | abuse | % | 0 | 1 | + | user_four | | % | % | 1 | 1 | + +---------------+-----+------------------+--------------+--------+--------+ + + In this example, mail for one@domain.tld gets delivered to user_one as +before. As it happens, mail for two@domain.tld still gets delivered to +user_two but three@domain.tld now also goes to user_two because +virtual_username contains the SQL % wildcard. Mail for abuse@example.org, +assuming you also host example.org, goes to user_three, as does mail for +abuse@evil.org (same disclaimer) and abuse@anything. user_four picks up +anything not mentioned previously. + + I know what you're thinking. You've spotted a little omission on my +part, haven't you? What happens to mail for abuse@domain.tld? According +to the the table above it could potentially be delivered to user_two, +user_three or even user_four. + In fact, the query outlined a few paragraphs up specifies that the +mail should go to user_three because the matching rows of the table +appear in the following order after sorting: + + +---------------+-----+------------------+--------------+--------+--------+ + | username | ext | virtual_username | virtual_host | wild_u | wild_h | + +---------------+-----+------------------+--------------+--------+--------+ + | user_three | | abuse | % | 0 | 1 | + | user_two | | % | domain.tld | 1 | 0 | + | user_four | | % | % | 1 | 1 | + +---------------+-----+------------------+--------------+--------+--------+ + + Recall we order by wild_u, wild_h... + + There is a way to override this. If you #define HOST_HAS_PRECEDENCE, +the query will be rewritten to order by wild_h, wild_u, giving the table +the following appearance: + + +---------------+-----+------------------+--------------+--------+--------+ + | username | ext | virtual_username | virtual_host | wild_u | wild_h | + +---------------+-----+------------------+--------------+--------+--------+ + | user_two | | % | domain.tld | 1 | 0 | + | user_three | | abuse | % | 0 | 1 | + | user_four | | % | % | 1 | 1 | + +---------------+-----+------------------+--------------+--------+--------+ + + This is why I said you could deliver mail to the wrong people. You need +to think about how you want wildcarded delivery to work. What's more +important for YOUR site? That you, the administrator, catch abuse, postmaster, +mailer-daemon etc etc messages for ANY domain you host? Or that your users +catch ANY address at their domains? + You can't have it both ways, unless you add explicit entries for all your +domains (eg abuse@domain.tld goes to user_five). + +New alias table queries +======================= + These new queries are a bit more experimental because there's a lot +more potential for confusion. + The alias table query, given a username 'u' and alias 'a', has been +changed from + + select alias_username, alias_host from alias where + username='u' and alias='a' +to + select alias_username, alias_host, username rlike '[%_]' as wild_u, + alias rlike '[%_]' as wild_a from alias where + 'u' like username and 'a' like alias order by wild_u, wild_a + + What this means is that you can have wildcards in the username or +alias fields of the alias table, IF YOU WANT. Remember that SQL "like" +behaves exactly like "=" if there are no wildcards floating around. + This lets you have entries in the alias table viz: + + +----------+-----------+----------------+--------------------+ + | username | alias | alias_username | alias_host | + +----------+-----------+----------------+--------------------+ + | user\_% | % | forward_one | somewhere.else.com | + | user_two | % | forward_two | somewhere.else.com | + +----------+-----------+----------------+--------------------+ + + Here we forward all user_xxx's mail to forward_one@somewhere.else.com +and user_two's mail to forward_two@somewhere.else.com AS WELL. + As with the virtual table, you can force non-default behaviour by +uncommenting -DALIAS_HAS_PRECEDENCE. You would only need to do this +if you also uncommented -DOIL_AND_WATER, which won't let you mix rows +which have wildcarded entries and rows which do not. In other words, +all entries for which a user (or alias) is explicitly matched are +processed and all others are discarded. In the example above, this +would mean that the user_two would not have a copy of his mail +forwarded to forward_one@somewhere.else.com because the matching +stops after the forward_two@somewhere.else.com is found. + Confused? You can always turn off FUNKY_ALIAS_QUERIES... + + Seriously though, I would recommend setting both -DOIL_AND_WATER and +-DALIAS_HAS_PRECEDENCE. In fact, they may well end up being the default +by the time you read this. Default, that is, in that they will be +uncommented in the makefile, while still being non-default in the sense +that you still need to uncomment FUNKY_ALIAS_QUEREIS. Oh, you know what +I mean. Anyway, check out the next example, which should make things a +little clearer. + + +----------+-----------+----------------+--------------------+ + | username | alias | alias_username | alias_host | + +----------+-----------+----------------+--------------------+ + | user_two | % | forward_two | somewhere.else.com | + | user_two | one | user_one | | + +----------+-----------+----------------+--------------------+ + + With -DALIAS_HAS_PRECEDENCE and -DOIL_AND_WATER set, mail for +user_two-one will go to user_one whereas mail for user_two-anything +will go to forward_two@somewhere.else.com. With either +-DOIL_AND_WATER or -DALIAS_HAS_PRECEDENCE set, mail for user_two-one +will go to both user_one and to forward_two@somewhere.else.com. Note, +however, that the REASON that the mail is sent to both addresses will +be different depending on the #defines you set. If you set only +-DOIL_AND_WATER, both rules are followed because the matching is +done on username. If you set only -DALIAS_HAS_PRECEDENCE, both rules +are followed (the second before the first, for all it matters) +because both alias fields match the 'one' extension. + + Got all that? Good. Now to confuse you again. Say my table +looks like this: + + +----------+-----------+----------------+--------------------+ + | username | alias | alias_username | alias_host | + +----------+-----------+----------------+--------------------+ + | % | one | user_three | | + | user_two | % | forward_two | somewhere.else.com | + | user_two | one | user_one | | + +----------+-----------+----------------+--------------------+ + + What happens for mail to user_two-one now? What should happen? +The answer to these questions will help you decide whether or +not you want to run FUNKY alias queries. + + Drop me a line with your comments on these new queries. I'd +especially like some feedback on the alias stuff. --- ../qmail-1.03/addrexp.c Fri May 11 16:38:34 2001 +++ addrexp.c Thu May 10 10:07:44 2001 @@ -0,0 +1,109 @@ +#include "constmap.h" +#include "control.h" +#include "str.h" +#include "stralloc.h" +#include "substdio.h" +#include "subfd.h" +#include "mysql_queries.h" + +extern int connect_mysql(); +extern void disconnect_mysql(); +extern int vdoms_mysql(); +extern int checkuser_mysql(); + +extern char auto_qmail[]; + +stralloc envnoathost = { 0 }; +stralloc locals = { 0 }; +stralloc percenthack = { 0 }; +stralloc qmail_mysql_query = { 0 }; +struct constmap maplocals; +struct constmap mappercenthack; +char *strnum3 = "test"; + +int main(int argc, char **argv) { + int at, i, j, local; + stralloc addr = { 0 }; + stralloc ret = { 0 }; + + if (argc == 1) { + substdio_putsflush(subfdout, "addrexp: expand a virtual address\n"); + substdio_putsflush(subfdout, "usage: addrexp virtual_username@virtual_host\n"); + substdio_putsflush(subfdout, "returns: final delivery address for virtual_username@virtual_host\n"); + substdio_putsflush(subfdout, "exit codes: 0 if the address is local\n"); + substdio_putsflush(subfdout, " 1 if the address is a virtual domain\n"); + substdio_putsflush(subfdout, " 2 if the address is remote\n"); + substdio_putsflush(subfdout, " 100 if there was an error reading controls\n"); + substdio_putsflush(subfdout, " 111 if there was a database error\n"); + _exit(1); + } + + if (chdir(auto_qmail) == -1) _exit(100); + if (control_init() == -1) _exit(100); + if (control_rldef(&envnoathost, "control/envnoathost", 1, "envnoathost") == -1) _exit(100); + if (control_readfile(&locals,"control/locals",1) != 1) _exit(100); + if (!constmap_init(&maplocals,locals.s,locals.len,0)) _exit(100); + switch(control_readfile(&percenthack,"control/percenthack",0)) { + case -1: return 0; + case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break; + case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break; + } + if (! stralloc_ready(&addr, str_len(argv[1]) + 1)) _exit(100); + if (! stralloc_cats(&addr, argv[1])) _exit(100); + + /* some of this ripped from qmail-send */ + i = byte_rchr(addr.s, addr.len, '@'); + if (i == addr.len) { + if (! stralloc_readyplus(&addr, envnoathost.len + 1)) _exit(100); + if (! stralloc_append(&addr, "@")) _exit(100); + if (! stralloc_cat(&addr, &envnoathost)) _exit(100); + } + + while (constmap(&mappercenthack,addr.s + i + 1,addr.len - i - 1)) { + j = byte_rchr(addr.s,i,'%'); + if (j == i) break; + addr.len = i; + i = j; + addr.s[i] = '@'; + } + + at = byte_rchr(addr.s,addr.len,'@'); + + if (! connect_mysql()) _exit(111); + local = 2; + i = 0; + if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { + local = 1; /* maybe one day expand to alias etc */ +/*printf("checkuser_mysql(%s, %d) = ", addr.s, at);*/ + i = checkuser_mysql(addr.s, at); +/*printf("%d\n", i);*/ + if (i == -1) _exit(111); + if (i) { + if (!stralloc_cat(&ret,&addr)) _exit(100); + if (!stralloc_0(&ret)) _exit(100); + local = 0; + } + else { + /* try virtual with the given host - then fall back to envnoathost */ +/*printf("local vdoms_mysql(%s, %i, &ret) = ", addr.s, addr.len);*/ + i = vdoms_mysql(addr.s, addr.len, &ret); +/*printf("%d\n", i);*/ + if (i == -1) _exit(111); + if (i) local = 0; + else { + addr.len = at + 1; + if (! stralloc_cat(&addr, &envnoathost)) _exit(100); + } + } + } + + if (! i) if (i = vdoms_mysql(addr.s, addr.len, &ret)) local = 1; +/*printf("%d\n", i);*/ + if (i == -1) _exit(111); + + if (i) substdio_puts(subfdout, ret.s); + else substdio_puts(subfdout, argv[1]); + substdio_putsflush(subfdout, "\n"); + disconnect_mysql(); + _exit(local); +} --- ../qmail-1.03/checkuser_mysql.c Fri May 11 16:38:34 2001 +++ checkuser_mysql.c Thu May 10 10:07:44 2001 @@ -0,0 +1,39 @@ +#include +#include "auto_break.h" +#include "stralloc.h" + +extern MYSQL_ROW do_query(stralloc *query); +extern MYSQL_RES *result; + +int checkuser_mysql(char *username, int i) { + MYSQL_ROW row; + int j; + stralloc qmail_mysql_query = { 0 }; + stralloc user = { 0 }; + + if (! stralloc_ready(&user, i)) return -1; + if (! stralloc_catb(&user, username, i)) { + stralloc_free(&user); + return -1; + } + user.s[i] = '\0'; + if (! make_query(&qmail_mysql_query, user.s)) { + stralloc_free(&user); + return -1; + } + + stralloc_free(&user); + row = do_query(&qmail_mysql_query); + + switch ((int) row) { + case -1: return -1; break; + case 0: + for (j = i - 1; j > 0; j--) { + if (*(username + j) == *auto_break) return checkuser_mysql(username, j); + } + return 0; + break; + } + mysql_free_result(result); + return 1; +} --- ../qmail-1.03/connect_mysql.c Fri May 11 16:38:34 2001 +++ connect_mysql.c Fri May 11 16:14:00 2001 @@ -0,0 +1,236 @@ +#include "mysql/mysql.h" +#include "auto_qmail.h" +#include "fmt.h" +#include "qsutil.h" +#include "readwrite.h" +#include "scan.h" +#include "str.h" +#include "stralloc.h" +#include "subfd.h" +#include "substdio.h" + +#define SQLSERVER "sqlserver" +#define TRIES 3 + +MYSQL dbh, *mysql; +MYSQL_RES *result; + +int connect_mysql() { + /* if database handle is available AND it pings then there's nothing to do */ + DEBUG_SAY("connect_mysql(): checking existing connection\n"); + if (mysql) if (! mysql_ping(&dbh)) return 1; + DEBUG_SAY("no connection, must reconnect\n"); + return init_mysql(); +} + +/* try to connect to database with the username/password from config file */ +/* returns 1 if we were already connected or we connected successfully */ +/* returns 0 if we could not connect */ +int init_mysql() { + int i, tries; + unsigned long portnum; + stralloc host = { 0 }; + stralloc user = { 0 }; + stralloc pass = { 0 }; + stralloc name = { 0 }; + stralloc port = { 0 }; + stralloc sock = { 0 }; + /* already used tries AND it has four letters :-) */ + stralloc fois = { 0 }; + + DEBUG_SAY("calling mysql_init()\n"); + mysql = mysql_init(&dbh); + if (! mysql) return 0; + DEBUG_SAY("mysql_init() returned successfully\n"); + + DEBUG_SAY("calling read_config_file()\n"); + if (! read_config_file(&host, &user, &pass, &name, &port, &sock, &fois, &portnum, &tries)) { + log1("error: without a valid config file we can't access the database!\n"); + return 0; + } + DEBUG_SAY("read_config_file() returned successfully\n"); + + /* have several goes attempting to the database */ + for (i = 0; i < tries; i++) { + DEBUG_SAY("earth to mysqld, come in please mysqld\n"); + mysql = mysql_real_connect(&dbh, host.s, user.s, pass.s, name.s, + portnum, sock.s, 0); + if (mysql) { +#ifndef O_NOT_LOG_CONNECTS + log3("connected to '", name.s, "' database\n"); +#endif + break; + } + log3("error: ", mysql_error(&dbh), "\n"); + sleep(3); + } + + stralloc_free(&host); + stralloc_free(&user); + stralloc_free(&pass); + stralloc_free(&port); + stralloc_free(&sock); + stralloc_free(&fois); + + if (! mysql) { + log3("giving up on connection to '", name.s, "' database\n"); + stralloc_free(&name); + return 0; + } + stralloc_free(&name); + return 1; +} + +void disconnect_mysql() { + mysql_close(&dbh); +} + +int read_config_file(stralloc *host, stralloc *user, stralloc *pass, stralloc *name, stralloc *port, stralloc *sock, stralloc *fois, unsigned long *portnum, int *tries) { + int error, i, file, match; + substdio ss; + stralloc filename = { 0 }; + stralloc buf = { 0 }; + char inbuf[64]; + + /* get the filename of the sqlserver control file */ + DEBUG_SAY("figuring out sqlserver path\n"); + i = str_len(SQLSERVER) + str_len(auto_qmail) + 10; + if (! stralloc_ready(&filename, i)) return 0; + if (! stralloc_cats(&filename, auto_qmail)) return 0; + if (! stralloc_cats(&filename, "/control/")) return 0; + if (! stralloc_cats(&filename, SQLSERVER)) return 0; + if (! stralloc_0(&filename)) return 0; + DEBUG_PUT("sqlserver file is \""); + DEBUG_PUT(filename.s); + DEBUG_SAY("\"\n"); + + DEBUG_SAY("stand by, we're trying the file\n"); + file = open_read(filename.s); + if (file == -1) { + log3("error: could not open sqlserver file \"", filename.s, "\"\n"); + stralloc_free(&filename); + stralloc_free(&buf); + return 0; + } + + substdio_fdbuf(&ss, read, file, inbuf, sizeof(inbuf)); + while (getln(&ss, &buf, &match, '\n') != -1) { + if (! match && ! buf.len) break; + buf.len--; + if (! stralloc_0(&buf)) break; + /* we consider only lines starting with the letters h, s,l,p,d or t */ + switch (buf.s[0]) { + case 'h': error = getconfig(&buf, "host", host); break; + case 's': + if (buf.s[1] == 'e') error = getconfig(&buf, "server", host); + else error = getconfig(&buf, "socket", sock); + break; + case 'l': error = getconfig(&buf, "login", user); break; + case 'p': + if (buf.s[1] == 'a') error = getconfig(&buf, "password", pass); + else error = getconfig(&buf, "port", port); + break; + case 'd': error = getconfig(&buf, "db", name); break; + case 't': error = getconfig(&buf, "tries", fois); break; + default: continue; + } + if (error == -1) continue; + if (! match) break; + } + + DEBUG_SAY("closing configuration file\n"); + close(file); + stralloc_free(&filename); + stralloc_free(&buf); + + /* if a port is provided it must be a valid number */ + if (port->a > 0) { + DEBUG_SAY("checking validity of provided port number\n"); + i = scan_ulong(port->s, portnum); + if (! i || port->s[i]) { + log3("error: bogus port number: \"", port->s, "\"\n"); + return 0; + } + } + else *portnum = 0; + + /* specific number of connection attempts */ + if (fois->a > 0) { + DEBUG_SAY("checking validity of requested connection attempts\n"); + i = scan_ulong(fois->s, tries); + if (! i || fois->s[i]) { + log3("warning: bogus connection attempts number: \"", fois->s, "\"\n"); + if (stralloc_ready(fois, FMT_ULONG)) { + fois->s[fmt_ulong(fois->s, TRIES)] = '\0'; + log3("warning: using default ", fois->s, "\n"); + } + *tries = TRIES; + } + else { + if (*tries == 0) { + log1("error: we must make at least one connection attempt\n"); + return 0; + } + DEBUG_PUT("we will try "); + if (*tries == 1) DEBUG_PUT("once (only) "); + else { + DEBUG_PUT(fois->s); + DEBUG_PUT(" times "); + } + DEBUG_SAY("to connect to the database\n"); + } + } + else *tries = TRIES; + + /* we must have at least a username */ + if (! user->a) { + log3("error: no username provided in \"", filename.s, "\"\n"); + return 0; + } + + if (host->a) { + DEBUG_PUT("host is \""); + DEBUG_PUT(host->s); + DEBUG_SAY("\"\n"); + } + + if (port->a) { + if (sock->a) { + log3("error: you can't specify a port number AND a socket\n"); + return 0; + } + DEBUG_PUT("port is "); + DEBUG_PUT(port->s); + DEBUG_SAY("\n"); + } + + DEBUG_PUT("user is \""); + DEBUG_PUT(user->s); + DEBUG_SAY("\"\n"); + + if (pass->a) DEBUG_SAY("a password was provided\n"); + + return 1; +} + +/* read lines in the config file and grok entries of the form "x=y" or "x y" */ +/* got is the line in the file, expected is the parmeter we're checking for */ +/* and store is a stralloc to put the result into if it was found */ +int getconfig(stralloc *got, char *expected, stralloc *store) { + char *value, *x; + + x = got->s + str_len(expected); + value = x + 1; + /* we got a meaningful value */ + if (! stralloc_starts(&got, expected) && (*x == ' ' || *x == '\t' || *x == '=') && str_len(value) > 0) { + if (! stralloc_ready(store, str_len(value) + 1)) return -1; + if (! stralloc_cats(store, value)) return -1; + if (! stralloc_0(store)) return -1; + return 0; + } + /* oh dear, the line we got didn't contain what we expected it to */ + log3("error: reading sqlserver file, expected \"", expected, "\" but got \""); + log2(got, "\"\n"); + /* of course, we might have been expecting the wrong thing... */ + return -1; +} --- ../qmail-1.03/do_query.c Fri May 11 16:38:34 2001 +++ do_query.c Fri May 11 10:45:30 2001 @@ -0,0 +1,33 @@ +#include +#include "stralloc.h" +#include "qsutil.h" + +extern MYSQL dbh, *mysql; +extern MYSQL_RES *result; + +/* run a given query and return 1 row only */ +MYSQL_ROW do_query(stralloc *query) { + int num; + + if (! connect_mysql()) return (MYSQL_ROW) -1; +#ifndef O_NOT_LOG + tcplog("query: ", query->s, ";\n"); +#endif + if (mysql_real_query(mysql, query->s, query->len) < 0) { + /* bad => log it anyway */ + tcplog("error: ", mysql_error(&dbh), "\n"); + return (MYSQL_ROW) -1; + } + stralloc_free(query); + if (! (result = mysql_store_result(mysql))) return (MYSQL_ROW) -1; + + num = mysql_num_rows(result); + + if (num != 1) { + mysql_free_result(result); + return 0; + } + + return mysql_fetch_row(result); +} + --- ../qmail-1.03/dotqmail_mysql.c Fri May 11 16:38:34 2001 +++ dotqmail_mysql.c Fri May 11 16:36:22 2001 @@ -0,0 +1,161 @@ +#include +#include "stralloc.h" +#include "qsutil.h" +#include "mysql_queries.h" + +extern int connect_mysql(); +extern int disconnect_mysql(); + +extern MYSQL dbh, *mysql; +extern MYSQL_RES *result; + +/* simulate reading a .qmail file from ye olde database */ +int dotqmail_mysql(char *username, char *ext, stralloc *sa, int bufsize) { + int i, len, num; + MYSQL_ROW row; + stralloc query = { 0 }; + stralloc user = { 0 }; + stralloc real_ext = { 0 }; +#ifdef FUNKY_ALIAS_QUERIES +#ifdef OIL_AND_WATER + stralloc last1 = { 0 }; + stralloc last2 = { 0 }; +#endif +#endif + + /* initialise database handle or signal trouble */ + if (! connect_mysql()) return -1; + + i = str_len(username); + if (! stralloc_ready(&user, 2 * i + 1)) return zoiks(-1); + mysql_escape_string(user.s, username, i); + + i = str_len(ext); + if (! stralloc_ready(&real_ext, 2 * i + 1)) return zoiks(-1); + mysql_escape_string(real_ext.s, ext, i); + + /* make the query or signal an out of memory exception */ + len = str_len(ALIAS) + real_ext.len + user.len - 3; + if (! stralloc_ready(&query, len)) return zoiks(-1); + if (! stralloc_cats(&query, ALIAS1)) return zoiks(-1); + if (! stralloc_cats(&query, user.s)) return zoiks(-1); + if (! stralloc_cats(&query, ALIAS2)) return zoiks(-1); + if (! stralloc_cats(&query, real_ext.s)) return zoiks(-1); + if (! stralloc_cats(&query, ALIAS3)) return zoiks(-1); + if (! stralloc_0(&query)) return zoiks(-1); + +#ifndef O_NOT_LOG + tcplog("query: ", query.s, ";\n"); +#endif + + /* do the query or signal to use .qmail */ + if (mysql_real_query(mysql, query.s, query.len) < 0) { + /* bad => log it anyway */ + tcplog("error: ", mysql_error(&dbh), "\n"); + disconnect_mysql(); + return 0; + } + result = mysql_store_result(mysql); + if (!result) return zoiks(-1); + + num = mysql_num_rows(result); + + /* no rows: let's try the default alias - but not if extension was blank */ + if (num == 0 && *ext) { + mysql_free_result(result); + query.len = 0; + len = str_len(ALIAS) + user.len - 2; + if (! stralloc_ready(&query, len)) return zoiks(-1); + if (! stralloc_cats(&query, ALIAS1)) return zoiks(-1); + if (! stralloc_cats(&query, user.s)) return zoiks(-1); + if (! stralloc_cats(&query, ALIAS2)) return zoiks(-1); + if (! stralloc_append(&query, "@")) return zoiks(-1); + if (! stralloc_cats(&query, ALIAS3)) return zoiks(-1); + if (! stralloc_0(&query)) return zoiks(-1); + +#ifndef O_NOT_LOG + tcplog("query: ", query.s, ";\n"); +#endif + + /* hmm, a function would have been nice here */ + if (mysql_real_query(mysql, query.s, query.len) < 0) { + /* bad => log it anyway */ + tcplog("error: ", mysql_error(&dbh), "\n"); + disconnect_mysql(); + return 0; + } + + result = mysql_store_result(mysql); + if (!result) return zoiks(0); + + num = mysql_num_rows(result); + } + + /* well it looks like there's really no match */ + if (num == 0) return zoiks(0); + +#ifdef FUNKY_ALIAS_QUERIES +#ifdef OIL_AND_WATER + if (! stralloc_ready(&last1, 2)) return zoiks(-1); + if (! stralloc_append(&last1, "0")) return zoiks(-1); + if (! stralloc_0(&last1)) return zoiks(-1); + if (! stralloc_ready(&last2, 2)) return zoiks(-1); + if (! stralloc_append(&last2, "0")) return zoiks(-1); + if (! stralloc_0(&last2)) return zoiks(-1); +#endif +#endif + + for (i = 0; i < num; i++) { + row = mysql_fetch_row(result); + if (! *(row[0])) { + tcplog("MySQL misconfiguration: alias_username is blank for username '", user.s, "' in alias table!\n"); + continue; + } +#ifdef FUNKY_ALIAS_QUERIES +#ifdef OIL_AND_WATER + /* discard wildcard matches if we already had explicit matches */ + if (i && (str_diff(row[2], last1.s) || str_diff(row[3], last2.s))) break; + + if (! stralloc_ready(&last1, str_len(row[2]) + 1)) return zoiks(-1); + if (! stralloc_cats(&last1, row[2])) return zoiks(-1); + if (! stralloc_0(&last1)) return zoiks(-1); + if (! stralloc_ready(&last2, str_len(row[3]) + 1)) return zoiks(-1); + if (! stralloc_cats(&last2, row[3])) return zoiks(-1); + if (! stralloc_0(&last2)) return zoiks(-1); +#endif +#endif + /* we need space for &blabla@crap\n\0 */ + len = str_len(row[0]) + str_len(row[1]) + 4; + if (! stralloc_readyplus(sa, len)) return zoiks(-1); + /* if the result is too big for the buffer we skip it */ + if (len > bufsize) continue; + /* if the alias username is a | we use the alias host value as the target */ + if (*row[0] == '|') { + /* skip if the pipe would break */ + if (str_len(row[1]) == 0) continue; + if (! stralloc_cats(sa, "| ")) return zoiks(-1); + } + /* . or / means delivery to a mailbox or maildir */ + else if (*row[0] == '/' || *row[0] == '.') { + *(row[0] + 1) = '\0'; + if (str_len(row[1]) == 0) continue; + if (! stralloc_cats(sa, row[0])) return zoiks(-1); + } + else { + if (! stralloc_cats(sa, "&")) return zoiks(-1); + if (! stralloc_cats(sa, row[0])) return zoiks(-1); + if (strcmp(row[1], "") && ! stralloc_cats(sa, "@")) return zoiks(-1); + } + if (! stralloc_cats(sa, row[1])) return zoiks(-1); + if (! stralloc_cats(sa, "\n")) return zoiks(-1); + } + + return zoiks(num); +} + +/* free memory used by query, disconnect from database and return */ +int zoiks(int ret) { + mysql_free_result(result); + disconnect_mysql(); + return ret; +} --- ../qmail-1.03/dotqmail_mysql.h Fri May 11 16:38:34 2001 +++ dotqmail_mysql.h Thu May 10 10:07:44 2001 @@ -0,0 +1 @@ +int dotqmail_mysql(char *username, char *ext, stralloc *sa, int bufsize); --- ../qmail-1.03/getpw_mysql.c Fri May 11 16:38:34 2001 +++ getpw_mysql.c Fri May 11 16:04:54 2001 @@ -0,0 +1,50 @@ +#include +#include "mysql_queries.h" +#include "scan.h" +#include "stralloc.h" + +stralloc tmpname = { 0 }; +stralloc tmphome = { 0 }; +stralloc tmpshell = { 0 }; + +extern MYSQL_ROW do_query(stralloc *query); +extern MYSQL_RES *result; +extern int make_query(stralloc *query, char *username); + +/* get a pw entry from the database */ +int getpw_mysql(char *username, int *uid, int *gid) { + MYSQL_ROW row; + int num, len, ret; + stralloc qmail_mysql_query = { 0 }; + + if (! make_query(&qmail_mysql_query, username)) return -1; + if (! (int) row) return -1; + row = do_query(&qmail_mysql_query); + if ((int) row < 1) return (int) row; + + if (! stralloc_ready(&tmpname, str_len(username))) return -1; + if (! stralloc_copys(&tmpname, username)) return -1; + if (! stralloc_0(&tmpname)) return -1; + if (! stralloc_ready(&tmphome, str_len(row[2]))) return -1; + if (! stralloc_copys(&tmphome, row[2])) return -1; + if (! stralloc_0(&tmphome)) return -1; + if (! stralloc_ready(&tmpshell, str_len(row[3]))) return -1; + if (! stralloc_copys(&tmpshell, row[3])) return -1; + if (! stralloc_0(&tmpshell)) return -1; + + /* OK so we're assuming the database really does return ints */ + len = scan_ulong(row[0], uid); + if (! len || (row[0])[len]) { + log3("MySQL misconfiguration: uid should be a number for user ", username , "!\n"); + mysql_free_result(result); + return -1; + } + len = scan_ulong(row[1], gid); + if (! len || (row[1])[len]) { + log3("MySQL misconfiguration: gid should be a number for user ", username, "!\n"); + mysql_free_result(result); + return -1; + } + + return 1; +} --- ../qmail-1.03/make_query.c Fri May 11 16:38:34 2001 +++ make_query.c Thu May 10 10:07:44 2001 @@ -0,0 +1,20 @@ +#include "mysql_queries.h" +#include "stralloc.h" + +int make_query(stralloc *query, char *username) { + int i, len; + stralloc user = { 0 }; + + i = 2 * str_len(username) + 1; + if (! stralloc_ready(&user, i)) return 0; + mysql_escape_string(user.s, username, str_len(username)); + + len = str_len(QUERY) + user.len + 2; + if (! stralloc_ready(query, len)) { stralloc_free(&user); return 0; } + if (! stralloc_cats(query, QUERY)) { stralloc_free(&user); return 0; } + if (! stralloc_cats(query, user.s)) { stralloc_free(&user); return 0; } + stralloc_free(&user); + if (! stralloc_cats(query, "'")) return 0; + if (! stralloc_0(query)) return 0; + return 1; +} --- ../qmail-1.03/mysql_queries.h Fri May 11 16:38:34 2001 +++ mysql_queries.h Thu May 10 10:07:44 2001 @@ -0,0 +1,55 @@ +/******************************************************************************/ +/* We define two queries that will be used to authenticate users from the */ +/* database. QUERY will be used to check for normal mailboxes and */ +/* VIRTUAL will query virtual domains */ +/* Furthermore we define the query that will allow us to expand aliases */ +/******************************************************************************/ + +/* virtual host query */ +/* please read the README.queries file */ +#ifndef VIRTUAL +#ifdef FUNKY_VIRTUAL_QUERIES +#ifdef HOST_HAS_PRECEDENCE +#define VIRTUAL_SORTING "wild_h, wild_u" +#else +#define VIRTUAL_SORTING "wild_u, wild_h" +#endif +#define VIRTUAL1 "select username, ext, virtual_host rlike '[%_]' as wild_h, virtual_username rlike '[%_]' as wild_u from virtual where '" +#define VIRTUAL2 "' like virtual_username and '" +#define VIRTUAL3 "' like virtual_host order by " VIRTUAL_SORTING " limit 1" +#else +#define VIRTUAL1 "select username, ext from virtual where (virtual_username='' or virtual_username='" +#define VIRTUAL2 "') and virtual_host='" +#define VIRTUAL3 "' order by virtual_username desc limit 1" +#endif +#define VIRTUAL VIRTUAL1 VIRTUAL2 VIRTUAL3 +#endif + +/* standard query */ +#define QUERY "select uid, gid, home, '/bin/false' from mailbox where username='" + +/* if we couldn't find a user we process an alias */ +#ifndef ALIAS +#ifdef FUNKY_ALIAS_QUERIES +#ifdef ALIAS_HAS_PRECEDENCE +#define ALIAS_SORTING "wild_a, wild_u" +#define WILD "alias rlike '[%_]' as wild_a, username rlike '[%_]' as wild_u from alias where '" +#else +#define ALIAS_SORTING "wild_u, wild_a" +#define WILD "username rlike '[%_]' as wild_u, alias rlike '[%_]' as wild_a from alias where '" +#endif +#define ALIAS1 "select alias_username, alias_host, " WILD +#define ALIAS2 "' like username and '" +#define ALIAS3 "' like alias order by " ALIAS_SORTING +#else +#define ALIAS1 "select alias_username, alias_host from alias where username='" +#define ALIAS2 "' and alias='" +#define ALIAS3 "'" +#endif +#define ALIAS ALIAS1 ALIAS2 ALIAS3 +#endif + +/* is this domain in rcpthosts? */ +#ifndef RCPTHOSTS +#define RCPTHOSTS "select count(*) from rcpthosts where host='" +#endif --- ../qmail-1.03/qmail-getpw.c Mon Jun 15 18:53:16 1998 +++ qmail-getpw.c Thu May 10 10:07:44 2001 @@ -13,18 +13,29 @@ #include "auto_usera.h" #include "auto_break.h" #include "qlx.h" +#include "stralloc.h" #define GETPW_USERLEN 32 +extern int getpw_mysql (char *username, int *UID, int *GID); +extern void disconnect_mysql(); +extern int connect_mysql(); + char *local; struct passwd *pw; char *dash; char *extension; +int connection; + +extern stralloc tmpname; +extern stralloc tmphome; +extern stralloc tmpshell; int userext() { char username[GETPW_USERLEN]; struct stat st; + int num, uid, gid; extension = local + str_len(local); for (;;) { @@ -34,21 +45,41 @@ username[extension - local] = 0; case_lowers(username); errno = 0; - pw = getpwnam(username); if (errno == error_txtbsy) _exit(QLX_SYS); + num = 0; +#ifdef SOLARIS_STUPIDITY + substdio_put(subfderr,"",1); +#endif + if (connection) num = getpw_mysql(username, &uid, &gid); + if (num == -1) _exit(QLX_SYS); + else if (num) { + pw = (struct passwd *) malloc(sizeof(struct passwd *)); + pw->pw_name = tmpname.s; + pw->pw_uid = uid; + pw->pw_gid = gid; + pw->pw_dir = tmphome.s; + pw->pw_shell = tmpshell.s; + } + if (! pw) pw = getpwnam(username); if (pw) if (pw->pw_uid) if (stat(pw->pw_dir,&st) == 0) { if (st.st_uid == pw->pw_uid) { dash = ""; - if (*extension) { ++extension; dash = "-"; } + if (*extension) { ++extension; dash = auto_break; } return 1; } } else if (error_temp(errno)) _exit(QLX_NFS); } - if (extension == local) return 0; + if (extension == local) { + /* we didn't find a system account */ + /* but the database is down */ + if (connection) return 0; + disconnect_mysql(); + _exit(QLX_SYS); + } --extension; } } @@ -62,11 +93,15 @@ local = argv[1]; if (!local) _exit(100); + connection = connect_mysql(); + if (!userext()) { extension = local; - dash = "-"; + dash = auto_break; pw = getpwnam(auto_usera); } + + if (connection) disconnect_mysql(); if (!pw) _exit(QLX_NOALIAS); --- ../qmail-1.03/qmail-local.c Mon Jun 15 18:53:16 1998 +++ qmail-local.c Thu May 10 10:07:44 2001 @@ -28,6 +28,7 @@ #include "myctime.h" #include "gfrom.h" #include "auto_patrn.h" +#include "dotqmail_mysql.h" void usage() { strerr_die1x(100,"qmail-local: usage: qmail-local [ -nN ] user homedir local dash ext domain sender aliasempty"); } @@ -458,6 +459,7 @@ datetime_sec starttime; int flagforwardonly; char *x; + int mysql_alias; umask(077); sig_pipeignore(); @@ -584,9 +586,14 @@ flagforwardonly = 0; qmesearch(&fd,&flagforwardonly); - if (fd == -1) - if (*dash) - strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)"); + mysql_alias = dotqmail_mysql(user, ext, &cmds, 256); + if (mysql_alias == -1) temp_nomem(); + if (mysql_alias == 0) { + if (fd == -1 && *dash) { + strerr_die1x(100,"Sorry, no mailbox here by that name. (#5.1.1)"); + } + } + else close(fd); if (!stralloc_copys(&ueo,sender)) temp_nomem(); if (str_diff(sender,"")) @@ -611,8 +618,7 @@ if (!env_put2("NEWSENDER",ueo.s)) temp_nomem(); if (!stralloc_ready(&cmds,0)) temp_nomem(); - cmds.len = 0; - if (fd != -1) + if (mysql_alias == 0 && fd != -1) if (slurpclose(fd,&cmds,256) == -1) temp_nomem(); if (!cmds.len) --- ../qmail-1.03/qmail-send.c Mon Jun 15 18:53:16 1998 +++ qmail-send.c Thu May 10 15:14:04 2001 @@ -1,3 +1,4 @@ +#include #include #include #include "readwrite.h" @@ -42,6 +43,12 @@ #define SLEEP_SYSFAIL 123 #define OSSIFIED 129600 /* 36 hours; _must_ exceed q-q's DEATH (24 hours) */ +extern int checkuser_mysql(char *username, int i); +extern int vdoms_mysql(char *host, int len, stralloc *store); + +extern MYSQL dbh, *mysql; +extern MYSQL_RES *result; + int lifetime = 604800; stralloc percenthack = {0}; @@ -119,7 +126,7 @@ int j; char *x; static stralloc addr = {0}; - int at; + int at, ret; if (!stralloc_copys(&rwline,"T")) return 0; if (!stralloc_copys(&addr,recip)) return 0; @@ -139,13 +146,31 @@ } at = byte_rchr(addr.s,addr.len,'@'); + i = 0; + ret = 2; if (constmap(&maplocals,addr.s + at + 1,addr.len - at - 1)) { - if (!stralloc_cat(&rwline,&addr)) return 0; - if (!stralloc_0(&rwline)) return 0; - return 1; + ret = 1; + i = checkuser_mysql(addr.s, at); + if (i == -1) return 0; + if (i) { + if (!stralloc_cat(&rwline,&addr)) return 0; + if (!stralloc_0(&rwline)) return 0; + return 1; + } + i = vdoms_mysql(addr.s, addr.len, &rwline); + if (i == -1) return 0; + if (i == 2) return 2; + if (i) return 1; + addr.len = at + 1; + if (!stralloc_cat(&addr, &envnoathost)) return 0; } + i = vdoms_mysql(addr.s, addr.len, &rwline); + if (i == -1) return 0; + if (i == 2) return 2; + if (i == 1) return 1; + for (i = 0;i <= addr.len;++i) if (!i || (i == at + 1) || (i == addr.len) || ((i > at) && (addr.s[i] == '.'))) if (x = constmap(&mapvdoms,addr.s + i,addr.len - i)) { @@ -159,7 +184,7 @@ if (!stralloc_cat(&rwline,&addr)) return 0; if (!stralloc_0(&rwline)) return 0; - return 2; + return ret; } void senderadd(sa,sender,recip) @@ -1608,5 +1633,6 @@ } pqfinish(); log1("status: exiting\n"); + disconnect_mysql(); _exit(0); } --- ../qmail-1.03/qmail-smtpd.c Mon Jun 15 18:53:16 1998 +++ qmail-smtpd.c Thu May 10 10:07:44 2001 @@ -24,9 +24,14 @@ #include "timeoutwrite.h" #include "commands.h" +extern int connect_mysql(); +extern void disconnect_mysql(); +extern int rcpthosts_mysql(char *host, int len); + #define MAXHOPS 100 unsigned int databytes = 0; int timeout = 1200; +stralloc qmail_mysql_query = { 0 }; int safewrite(fd,buf,len) int fd; char *buf; int len; { @@ -45,6 +50,7 @@ void die_read() { _exit(1); } void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); } void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); } +void die_mysql() { out("421 who killed the MySQL server? (#4.3.0)\r\n"); flush(); _exit(1); } void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); } void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); } void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); } @@ -73,6 +79,7 @@ } void smtp_quit() { + disconnect_mysql(); smtp_greet("221 "); out("\r\n"); flush(); _exit(0); } @@ -213,6 +220,8 @@ int r; r = rcpthosts(addr.s,str_len(addr.s)); if (r == -1) die_control(); + if (! r) r = rcpthosts_mysql(addr.s,str_len(addr.s)); + if (r == -1) die_control(); return r; } @@ -411,6 +420,7 @@ void main() { sig_pipeignore(); + if (! connect_mysql()) die_mysql(); if (chdir(auto_qmail) == -1) die_control(); setup(); if (ipme_init() != 1) die_ipme(); --- ../qmail-1.03/qsutil.c Mon Jun 15 18:53:16 1998 +++ qsutil.c Thu May 10 10:07:44 2001 @@ -7,6 +7,7 @@ static char errbuf[1]; static struct substdio sserr = SUBSTDIO_FDBUF(write,0,errbuf,1); +static struct substdio tcpsserr = SUBSTDIO_FDBUF(write,2,errbuf,1); void logsa(sa) stralloc *sa; { substdio_putflush(&sserr,sa->s,sa->len); } @@ -19,6 +20,10 @@ substdio_putsflush(&sserr,s1); substdio_putsflush(&sserr,s2); substdio_putsflush(&sserr,s3); } +void tcplog(s1,s2,s3) char *s1; char *s2; char *s3; { + substdio_putsflush(&tcpsserr,s1); + substdio_putsflush(&tcpsserr,s2); + substdio_putsflush(&tcpsserr,s3); } void nomem() { log1("alert: out of memory, sleeping...\n"); sleep(10); } void pausedir(dir) char *dir; --- ../qmail-1.03/qsutil.h Mon Jun 15 18:53:16 1998 +++ qsutil.h Fri May 11 10:42:49 2001 @@ -9,4 +9,12 @@ extern void pausedir(); extern void logsafe(); +#ifdef EBUG_DEBUG_DEBUG +#define DEBUG_PUT(x) substdio_puts(subfderr, x) +#define DEBUG_SAY(x) substdio_putsflush(subfderr, x) +#else +#define DEBUG_PUT(x) (0) +#define DEBUG_SAY(x) (0) +#endif + #endif --- ../qmail-1.03/rcpthosts_mysql.c Fri May 11 16:38:34 2001 +++ rcpthosts_mysql.c Thu May 10 10:07:44 2001 @@ -0,0 +1,62 @@ +#include +#include "control.h" +#include "mysql_queries.h" +#include "stralloc.h" + +extern int connect_mysql(); + +extern MYSQL dbh, *mysql; +extern MYSQL_RES *result; + +extern MYSQL_ROW do_query(stralloc *query); + +/* qmail-smtpd needs to know if it can accept delivery for this domain */ +int rcpthosts_mysql(char *addr, int len) { + char *host; + int i, num; + MYSQL_ROW row; + stralloc real_host = { 0 }; + stralloc qmail_mysql_query = { 0 }; + + /* maybe the client's so slow we lost the MySQL connection... */ + if (! connect_mysql()) return -1; + + num = len; + host = addr + len; + while (*host != '@') { + host--; + num--; + if (host == addr) break; + } + + /* no @ => envnoathost */ + if (num == 0) return 1; + + /* let's lose the @ we found and adjust len */ + host++; + + if (! stralloc_ready(&real_host, 2 * str_len(host) + 1)) return -1; + mysql_escape_string(real_host.s, host, str_len(host)); + +#define OOF {\ + stralloc_free(&real_host);\ + stralloc_free(&qmail_mysql_query);\ + return -1;\ +} + len = real_host.len + str_len(RCPTHOSTS) + 2; + if (! stralloc_ready(&qmail_mysql_query, len)) OOF + if (! stralloc_cats(&qmail_mysql_query, RCPTHOSTS)) OOF + if (! stralloc_cats(&qmail_mysql_query, real_host.s)) OOF + if (! stralloc_cats(&qmail_mysql_query, "'")) OOF + if (! stralloc_0(&qmail_mysql_query)) OOF + stralloc_free(&real_host); + + row = do_query(&qmail_mysql_query); + + if ((int) row == 0) return -1; + + num = atoi(row[0]); + mysql_free_result(result); + + return num; +} --- ../qmail-1.03/stralloc.h Mon Jun 15 18:53:16 1998 +++ stralloc.h Thu May 10 10:07:44 2001 @@ -13,6 +13,7 @@ extern int stralloc_cats(); extern int stralloc_copyb(); extern int stralloc_catb(); +extern void stralloc_free(); extern int stralloc_append(); /* beware: this takes a pointer to 1 char */ extern int stralloc_starts(); --- ../qmail-1.03/stralloc_free.c Fri May 11 16:38:34 2001 +++ stralloc_free.c Fri May 11 15:30:20 2001 @@ -0,0 +1,6 @@ +#include "stralloc.h" + +void stralloc_free(stralloc *labrat) { + alloc_free(labrat->s); + labrat->len = 0; +} --- ../qmail-1.03/vdoms_mysql.c Fri May 11 16:38:34 2001 +++ vdoms_mysql.c Fri May 11 11:53:39 2001 @@ -0,0 +1,121 @@ +#include +#include "auto_break.h" +#include "control.h" +#include "fmt.h" +#include "mysql_queries.h" +#include "qsutil.h" +#include "subfd.h" +#include "substdio.h" +#include "stralloc.h" + +extern int connect_mysql(); + +extern MYSQL dbh, *mysql; +extern MYSQL_RES *result; +extern stralloc envnoathost; +extern char strnum3[FMT_ULONG]; + +extern MYSQL_ROW do_query(stralloc *query); + +/* map a virtual domain to a user-ext stralloc */ +int vdoms_mysql(char *addr, int len, stralloc *ret) { + char *host; + int at, num; + MYSQL_ROW row; + stralloc qmail_mysql_query = { 0 }; + stralloc real_addr = { 0 }; + stralloc real_host = { 0 }; + + /* stralloc we're passed is unterminated */ + addr[len] = '\0'; + DEBUG_SAY("vdoms_mysql: "); + DEBUG_SAY(addr); + DEBUG_SAY("\n"); + num = len; + host = addr + len; + while (*host != '@') { + host--; + num--; + if (host == addr) break; + } + at = num; + + /* let's lose the @ we found */ + host++; + if (! stralloc_ready(&real_addr, 2 * str_len(addr) + 1)) return -1; + mysql_escape_string(real_addr.s, addr, at); + if (! stralloc_ready(&real_host, 2 * str_len(host) + 1)) { + stralloc_free(&real_addr); + return -1; + } + mysql_escape_string(real_host.s, host, str_len(host)); + +#define ACK {\ + stralloc_free(&real_addr);\ + stralloc_free(&real_host);\ + stralloc_free(&qmail_mysql_query);\ + return -1;\ +} + if (! connect_mysql()) ACK + + len += real_addr.len + real_host.len + str_len(VIRTUAL) + 2; + if (! stralloc_ready(&qmail_mysql_query, len)) ACK + if (! stralloc_cats(&qmail_mysql_query, VIRTUAL1)) ACK + if (! stralloc_cats(&qmail_mysql_query, real_addr.s)) ACK + if (! stralloc_cats(&qmail_mysql_query, VIRTUAL2)) ACK + if (! stralloc_cats(&qmail_mysql_query, real_host.s)) ACK + if (! stralloc_cats(&qmail_mysql_query, VIRTUAL3)) ACK + if (! stralloc_0(&qmail_mysql_query)) ACK + +#ifndef O_NOT_LOG + log3("query: ", qmail_mysql_query.s, ";\n"); +#endif + row = do_query(&qmail_mysql_query); + + /* no row doesn't mean an error */ + if ((int) row < 1) return (int) row; + + /* run database sanity check: username may not be blank */ + if (! *(row[0])) { + log3("MySQL misconfiguration: username for host '", real_host.s, "' is blank in virtual table!\n"); + stralloc_free(&real_addr); + stralloc_free(&real_host); + mysql_free_result(result); + return 0; + } + +#define YOW {\ + stralloc_free(&real_addr);\ + stralloc_free(&real_host);\ + mysql_free_result(result);\ + return -1;\ +} +#ifdef MAGIC_FORWARDING + if (! str_diff(row[0], MAGIC_FORWARD_USER)) { + if (! stralloc_cats(ret, row[1])) YOW + if (! stralloc_0(ret)) YOW + mysql_free_result(result); + stralloc_free(&real_addr); + stralloc_free(&real_host); +#ifdef MAGIC_LOGGING + log2("magic forward: msg ", strnum3); + log2(" for ", addr); + log3(" to ", row[1], "\n"); +#endif + return 2; + } +#endif + if (! stralloc_cats(ret, row[0])) YOW /* username */ + if (str_len(row[1])) { + if (! stralloc_append(ret, auto_break)) YOW + if (*row[1] != '@') { if (! stralloc_cats(ret, row[1])) YOW } + else if (! stralloc_cats(ret, real_addr.s)) YOW /* ext */ + } + if (! stralloc_append(ret, "@")) return -1; + if (! stralloc_cats(ret, real_host.s)) return -1; + if (! stralloc_0(ret)) return -1; + mysql_free_result(result); + stralloc_free(&real_addr); + stralloc_free(&real_host); + return 1; +}