chiark / gitweb /
Debianization and various other fixes.
[ezmlm] / sub_mysql / issub.c
1 /*$Id: issub.c,v 1.16 1999/12/11 03:04:19 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
3 #include "stralloc.h"
4 #include "getln.h"
5 #include "readwrite.h"
6 #include "substdio.h"
7 #include "open.h"
8 #include "byte.h"
9 #include "case.h"
10 #include "strerr.h"
11 #include "error.h"
12 #include "uint32.h"
13 #include "fmt.h"
14 #include "subscribe.h"
15 #include "errtxt.h"
16 #include <mysql.h>
17
18 static void die_nomem(fatal)
19 char *fatal;
20 {
21   strerr_die2x(111,fatal,ERR_NOMEM);
22 }
23
24 static stralloc addr = {0};
25 static stralloc lcaddr = {0};
26 static stralloc line = {0};
27 static stralloc quoted = {0};
28 static stralloc fn = {0};
29 static substdio ss;
30 static char ssbuf[512];
31 static char szh[FMT_ULONG];
32
33 char *issub(dbname,userhost,tab,fatal)
34 /* Returns (char *) to match if userhost is in the subscriber database     */
35 /* dbname, 0 otherwise. dbname is a base directory for a list and may NOT  */
36 /* be NULL        */
37 /* NOTE: The returned pointer is NOT VALID after a subsequent call to issub!*/
38
39 char *dbname;           /* directory to basedir */
40 char *userhost;
41 char *tab;              /* override table name */
42 char *fatal;
43
44 {
45   MYSQL_RES *result;
46   MYSQL_ROW row;
47   char *ret;
48   char *table;
49   unsigned long *lengths;
50
51   int fd;
52   unsigned int j;
53   uint32 h,lch;
54   char ch,lcch;
55   int match;
56
57   table = tab;
58   if ((ret = opensql(dbname,&table))) {
59     if (*ret) strerr_die2x(111,fatal,ret);
60                                                 /* fallback to local db */
61
62     if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
63     if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
64
65     j = byte_rchr(addr.s,addr.len,'@');
66     if (j == addr.len) return 0;
67     case_lowerb(addr.s + j + 1,addr.len - j - 1);
68     if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
69     case_lowerb(lcaddr.s + 1,j - 1);    /* totally lc version of addr */
70
71     h = 5381;
72     lch = h;                    /* make hash for both for backwards comp */
73     for (j = 0;j < addr.len;++j) {      /* (lcaddr.len == addr.len) */
74       h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
75       lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
76     }
77     ch = 64 + (h % 53);
78     lcch = 64 + (lch % 53);
79
80     if (!stralloc_0(&addr)) die_nomem(fatal);
81     if (!stralloc_0(&lcaddr)) die_nomem(fatal);
82     if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
83     if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
84     if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
85     if (!stralloc_0(&fn)) die_nomem(fatal);
86
87     fd = open_read(fn.s);
88     if (fd == -1) {
89       if (errno != error_noent)
90         strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
91     } else {
92       substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
93
94       for (;;) {
95         if (getln(&ss,&line,&match,'\0') == -1)
96           strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
97         if (!match) break;
98         if (line.len == lcaddr.len)
99           if (!case_diffb(line.s,line.len,lcaddr.s))
100             { close(fd); return line.s+1; }
101       }
102
103       close(fd);
104     }
105         /* here if file not found or (file found && addr not there) */
106
107     if (ch == lcch) return 0;
108
109         /* try case sensitive hash for backwards compatibility */
110     fn.s[fn.len - 2] = ch;
111     fd = open_read(fn.s);
112     if (fd == -1) {
113       if (errno != error_noent)
114         strerr_die4sys(111,fatal,ERR_OPEN,fn.s,": ");
115       return 0;
116     }
117     substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
118
119     for (;;) {
120       if (getln(&ss,&line,&match,'\0') == -1)
121         strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
122       if (!match) break;
123       if (line.len == addr.len)
124         if (!case_diffb(line.s,line.len,addr.s))
125           { close(fd); return line.s+1; }
126     }
127
128     close(fd);
129
130     return 0;
131   } else {                                              /* SQL version  */
132         /* SELECT address FROM list WHERE address = 'userhost' AND hash */
133         /* BETWEEN 0 AND 52. Without the hash restriction, we'd make it */
134         /* even easier to defeat. Just faking sender to the list name would*/
135         /* work. Since sender checks for posts are bogus anyway, I don't */
136         /* know if it's worth the cost of the "WHERE ...". */
137
138     if (!stralloc_copys(&addr,userhost)) die_nomem(fatal);
139     j = byte_rchr(addr.s,addr.len,'@');
140     if (j == addr.len) return 0;
141     case_lowerb(addr.s + j + 1,addr.len - j - 1);
142
143     if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(fatal);
144     if (!stralloc_cats(&line,table)) die_nomem(fatal);
145     if (!stralloc_cats(&line," WHERE address = '")) die_nomem(fatal);
146     if (!stralloc_ready(&quoted,2 * addr.len + 1)) die_nomem(fatal);
147     if (!stralloc_catb(&line,quoted.s,
148         mysql_escape_string(quoted.s,userhost,addr.len))) die_nomem(fatal);
149     if (!stralloc_cats(&line,"'"))
150                 die_nomem(fatal);
151     if (mysql_real_query((MYSQL *) psql,line.s,line.len))       /* query */
152                 strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
153     if (!(result = mysql_use_result((MYSQL *) psql)))
154                 strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
155     row = mysql_fetch_row(result);
156     ret = (char *) 0;
157     if (!row) {         /* we need to return the actual address as other */
158                         /* dbs may accept user-*@host, but we still want */
159                         /* to make sure to send to e.g the correct moderator*/
160                         /* address. */
161       if (!mysql_eof(result))
162                 strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
163     } else {
164       if (!(lengths = mysql_fetch_lengths(result)))
165                 strerr_die2x(111,fatal,mysql_error((MYSQL *) psql));
166       if (!stralloc_copyb(&line,row[0],lengths[0])) die_nomem(fatal);
167       if (!stralloc_0(&line)) die_nomem(fatal);
168       ret = line.s;
169       while ((row = mysql_fetch_row(result)));  /* maybe not necessary */
170       mysql_free_result(result);
171     }
172     return ret;
173   }
174 }