Import ezmlm-idx 0.40
[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 }