ezmlmx 0.68
ezmlmx
Loading...
Searching...
No Matches
subscribe.c
Go to the documentation of this file.
1#include <unistd.h>
2#include "str.h"
3#include "stralloc.h"
4#include "getln.h"
5#include "readwrite.h"
6#include "buffer.h"
7#include "logmsg.h"
8#include "open.h"
9#include "byte.h"
10#include "case.h"
11#include "lock.h"
12#include "error.h"
13#include "subscribe.h"
14#include "uint_t.h"
15#include "fmt.h"
16#include "errtxt.h"
17#include "log.h"
18#include "idx.h"
19#include <mysql.h>
20#include <mysqld_error.h>
21
27
28extern MYSQL *mysql;
29
30static stralloc addr = {0};
31static stralloc lcaddr = {0};
32static stralloc line = {0};
33static stralloc domain = {0};
34static stralloc logline = {0};
35static stralloc quoted = {0};
36static stralloc fnnew = {0};
37static stralloc fn = {0};
38static stralloc fnlock = {0};
39
40void die_read(void) { logmsg(WH0,111,FATAL,B("ERR_READ :",fn.s)); }
41void die_write(void) { logmsg(WH0,111,FATAL,B("ERR_WRITE :",fnnew.s)); }
42
43static int fd;
44static buffer bi;
45static char inbuf[256];
46static int fdnew;
47static buffer bo;
48static char outbuf[256];
49
75
76int subscribe(const char *dbname,const char *userhost,int flagadd,const char *comment,
77 const char *event,int flagmysql,int forcehash,const char *tab)
78{
79 int fdlock;
80 MYSQL_RES *result;
81 MYSQL_ROW row;
82 char *cpat;
83 char szhash[3] = "00";
84 const char *r = (char *) 0;
85 const char *table = (char *) 0;
86 const char **ptable = &table;
87
88 unsigned int j;
89 uint32 h,lch;
90 unsigned char ch,lcch;
91 int match;
92 int flagwasthere;
93
94 if (userhost[str_chr(userhost,'\n')])
95 logmsg(WH0,100,FATAL,ERR_ADDR_NL);
96
97 if (tab) ptable = &tab;
98
99 if (!flagmysql || (r = opensql(dbname,ptable))) {
100 if (r && *r) logmsg(WH0,111,FATAL,r);
101
102 /* fallback to local db */
103
104 if (!stralloc_copys(&addr,"T")) die_nomem();
105 if (!stralloc_cats(&addr,userhost)) die_nomem();
106 if (addr.len > 401)
107 logmsg(WH0,100,FATAL,ERR_ADDR_LONG);
108
109 j = byte_rchr(addr.s,addr.len,'@');
110 if (j == addr.len)
111 logmsg(WH0,100,FATAL,ERR_ADDR_AT);
112 case_lowerb(addr.s + j + 1,addr.len - j - 1);
113 if (!stralloc_copy(&lcaddr,&addr)) die_nomem();
114 case_lowerb(lcaddr.s + 1,j - 1); /* make all-lc version of address */
115
116 if (forcehash >= 0 && forcehash <= 52) {
117 ch = lcch = (unsigned char) forcehash;
118 } else {
119 h = 5381;
120 lch = h;
121 for (j = 0;j < addr.len;++j) {
122 h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
123 lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
124 }
125 lcch = 64 + (lch % 53);
126 ch = 64 + (h % 53);
127 }
128
129 if (!stralloc_0(&addr)) die_nomem();
130 if (!stralloc_0(&lcaddr)) die_nomem();
131 if (!stralloc_copys(&fn,dbname)) die_nomem();
132 if (!stralloc_copys(&fnlock,dbname)) die_nomem();
133
134 if (!stralloc_cats(&fn,"/subscribers/")) die_nomem();
135 if (!stralloc_catb(&fn,&lcch,1)) die_nomem();
136 if (!stralloc_copy(&fnnew,&fn)) die_nomem();
137 /* code later depends on fnnew = fn + 'n' */
138 if (!stralloc_cats(&fnnew,"n")) die_nomem();
139 if (!stralloc_cats(&fnlock,"/lock")) die_nomem();
140 if (!stralloc_0(&fnnew)) die_nomem();
141 if (!stralloc_0(&fn)) die_nomem();
142 if (!stralloc_0(&fnlock)) die_nomem();
143
144 fdlock = lockfile(fnlock.s);
145
146 /* do lower case hashed version first */
147
148 fdnew = open_trunc(fnnew.s);
149 if (fdnew == -1) die_write();
150 buffer_init(&bo,buffer_unixwrite,fdnew,outbuf,sizeof(outbuf));
151
152 flagwasthere = 0;
153
154 fd = open_read(fn.s);
155 if (fd == -1) {
156 if (errno != ENOENT) { close(fdnew); die_read(); }
157 }
158 else {
159 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
160
161 for (;;) {
162 if (getln(&bi,&line,&match,'\0') == -1) {
163 close(fd); close(fdnew); die_read();
164 }
165 if (!match) break;
166 if (line.len == addr.len)
167 if (!case_diffb(line.s,line.len,addr.s)) {
168 flagwasthere = 1;
169 if (!flagadd) continue;
170 }
171 if (buffer_bput(&bo,line.s,line.len) == -1) {
172 close(fd); close(fdnew); die_write();
173 }
174 }
175
176 close(fd);
177 }
178
179 if (flagadd && !flagwasthere)
180 if (buffer_bput(&bo,addr.s,addr.len) == -1) {
181 close(fdnew); die_write();
182 }
183
184 if (buffer_flush(&bo) == -1) { close(fdnew); die_write(); }
185 if (fsync(fdnew) == -1) { close(fdnew); die_write(); }
186 close(fdnew);
187
188 if (rename(fnnew.s,fn.s) == -1)
189 logmsg(WH0,111,FATAL,B("ERR_MOVE :",fnnew.s," to : ",fn.s));
190
191 if ((ch == lcch) || flagwasthere) {
192 close(fdlock);
193 if (flagadd ^ flagwasthere) {
194 if (!stralloc_0(&addr)) die_nomem();
195 logaddr(dbname,event,addr.s+1,comment);
196 return 1;
197 }
198 return 0;
199 }
200
201 /* If unsub and not found and hashed differ, OR */
202 /* sub and not found (so added with new hash) */
203 /* do the 'case-dependent' hash */
204
205 fn.s[fn.len - 2] = ch;
206 fnnew.s[fnnew.len - 3] = ch;
207 fdnew = open_trunc(fnnew.s);
208 if (fdnew == -1) die_write();
209 buffer_init(&bo,buffer_unixwrite,fdnew,outbuf,sizeof(outbuf));
210
211 fd = open_read(fn.s);
212 if (fd == -1) {
213 if (errno != ENOENT) { close(fdnew); die_read(); }
214 } else {
215 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
216
217 for (;;) {
218 if (getln(&ss,&line,&match,'\0') == -1)
219 { close(fd); close(fdnew); die_read(); }
220 if (!match) break;
221 if (line.len == addr.len)
222 if (!case_diffb(line.s,line.len,addr.s)) {
223 flagwasthere = 1;
224 continue; /* always want to remove from case-sensitive hash */
225 }
226 if (buffer_bput(&bo,line.s,line.len) == -1)
227 { close(fd); close(fdnew); die_write(); }
228 }
229
230 close(fd);
231 }
232
233 if (buffer_flush(&bo) == -1) { close(fdnew); die_write(); }
234 if (fsync(fdnew) == -1) { close(fdnew); die_write(); }
235 close(fdnew);
236
237 if (rename(fnnew.s,fn.s) == -1)
238 logmsg(WH0,111,FATAL,B("ERR_MOVE :",fnnew.s," to :",fn.s));
239
240 close(fdlock);
241 if (flagadd ^ flagwasthere) {
242 if (!stralloc_0(&addr)) die_nomem();
243 logaddr(dbname,event,addr.s + 1,comment);
244 return 1;
245 }
246 return 0;
247
248 } else { /* SQL version */
249 domain.len = 0; /* clear domain */
250
251 /* lowercase and check address */
252
253 if (!stralloc_copys(&addr,userhost)) die_nomem();
254 if (addr.len > 255) /* this is 401 in std ezmlm. 255 should be plenty! */
255 logmsg(WH0,100,FATAL,ERR_ADDR_LONG);
256
257 j = byte_rchr(addr.s,addr.len,'@');
258 if (j == addr.len)
259 logmsg(WH0,100,FATAL,ERR_ADDR_AT);
260 cpat = addr.s + j;
261
262 case_lowerb(cpat + 1,addr.len - j - 1);
263 if (!stralloc_ready(&quoted,2 * addr.len + 1)) die_nomem();
264 quoted.len = mysql_escape_string(quoted.s,addr.s,addr.len);
265
266 /* stored unescaped, so it should be ok if quoted.len is >255, as */
267 /* long as addr.len is not */
268
269 if (forcehash < 0) {
270 if (!stralloc_copy(&lcaddr,&addr)) die_nomem();
271 case_lowerb(lcaddr.s,j); /* make all-lc version of address */
272 h = 5381;
273 for (j = 0;j < lcaddr.len;++j) {
274 h = (h + (h << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
275 }
276 ch = (h % 53); /* 0 - 52 */
277 } else
278 ch = (forcehash % 100);
279
280 szhash[0] = '0' + ch / 10; /* hash for sublist split */
281 szhash[1] = '0' + (ch % 10);
282
283 if (flagadd) {
284 if (!stralloc_copys(&line,"LOCK TABLES ")) die_nomem();
285 if (!stralloc_cats(&line,table)) die_nomem();
286 if (!stralloc_cats(&line," WRITE")) die_nomem();
287 if (mysql_real_query(mysql,line.s,line.len))
288 logmsg(WH0,111,FATAL,mysql_error(mysql));
289 if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem();
290 if (!stralloc_cats(&line,table)) die_nomem();
291 if (!stralloc_cats(&line," WHERE address='")) die_nomem();
292 if (!stralloc_cat(&line,&quoted)) die_nomem(); /* addr */
293 if (!stralloc_cats(&line,"'")) die_nomem();
294 if (mysql_real_query(mysql,line.s,line.len))
295 logmsg(WH0,111,FATAL,mysql_error(mysql));
296 if (!(result = mysql_use_result(mysql)))
297 logmsg(WH0,111,FATAL,mysql_error(mysql));
298
299 if ((row = mysql_fetch_row(result))) { /* there */
300 while (mysql_fetch_row(result)); /* use'm up */
301 mysql_free_result(result);
302 if (mysql_query(mysql,"UNLOCK TABLES"))
303 logmsg(WH0,111,"FATAL",mysql_error(mysql));
304 return 0; /* there */
305 } else { /* not there */
306 mysql_free_result(result);
307 if (mysql_errno(mysql)) /* or ERROR */
308 logmsg(WH0,111,FATAL,mysql_error(mysql));
309 if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem();
310 if (!stralloc_cats(&line,table)) die_nomem();
311 if (!stralloc_cats(&line," (address,hash) VALUES ('")) die_nomem();
312 if (!stralloc_cat(&line,&quoted)) die_nomem(); /* addr */
313 if (!stralloc_cats(&line,"',")) die_nomem();
314 if (!stralloc_cats(&line,szhash)) die_nomem(); /* hash */
315 if (!stralloc_cats(&line,")")) die_nomem();
316 if (mysql_real_query(mysql,line.s,line.len)) /* INSERT */
317 logmsg(WH0,111,FATAL,mysql_error(mysql));
318 if (mysql_query(mysql,"UNLOCK TABLES"))
319 logmsg(WH0,111,FATAL,mysql_error(mysql));
320 }
321 } else { /* unsub */
322 if (!stralloc_copys(&line,"DELETE FROM ")) die_nomem();
323 if (!stralloc_cats(&line,table)) die_nomem();
324 if (!stralloc_cats(&line," WHERE address='")) die_nomem();
325 if (!stralloc_cat(&line,&quoted)) die_nomem(); /* addr */
326 if (forcehash >= 0) {
327 if (!stralloc_cats(&line,"' AND hash=")) die_nomem();
328 if (!stralloc_cats(&line,szhash)) die_nomem();
329 } else {
330 if (!stralloc_cats(&line,"' AND hash BETWEEN 0 AND 52")) die_nomem();
331 }
332 if (mysql_real_query(mysql,line.s,line.len))
333 logmsg(WH0,111,FATAL,mysql_error(mysql));
334 if (mysql_affected_rows(mysql) == 0)
335
336 return 0; /* address wasn't there*/
337 }
338
339 /* log to subscriber log */
340 /* INSERT INTO t_slog (address,edir,etype,fromline) */
341 /* VALUES('address',{'+'|'-'},'etype','[comment]') */
342
343 if (!stralloc_copys(&logline,"INSERT INTO ")) die_nomem();
344 if (!stralloc_cats(&logline,table)) die_nomem();
345 if (!stralloc_cats(&logline,"_slog (address,edir,etype,fromline) VALUES ('")) die_nomem();
346 if (!stralloc_cat(&logline,&quoted)) die_nomem();
347 if (flagadd) { /* edir */
348 if (!stralloc_cats(&logline,"','+','")) die_nomem();
349 } else {
350 if (!stralloc_cats(&logline,"','-','")) die_nomem();
351 }
352
353 if (*(event + 1)) /* ezmlm-0.53 uses '' for ezmlm-manage's work */
354 if (!stralloc_catb(&logline,event + 1,1)) die_nomem(); /* etype */
355 if (!stralloc_cats(&logline,"','")) die_nomem();
356 if (comment && *comment) {
357 j = str_len(comment);
358 if (!stralloc_ready(&quoted,2*j + 1)) die_nomem();
359 quoted.len = mysql_escape_string(quoted.s,comment,j);/* from */
360 if (!stralloc_cat(&logline,&quoted)) die_nomem();
361 }
362 if (!stralloc_cats(&logline,"')")) die_nomem();
363
364 if (mysql_real_query(mysql,logline.s,logline.len))
365 ; /* log (ignore errors) */
366 if (!stralloc_0(&addr))
367 ; /* ignore errors */
368 logaddr(dbname,event,addr.s,comment); /* also log to old log */
369
370 return 1; /* desired effect */
371 }
372}
Error messages. If you translate these, I would urge you to keep the English version as well....
#define ERR_ADDR_AT
Definition errtxt.h:148
#define ERR_ADDR_LONG
Definition errtxt.h:149
#define ERR_ADDR_NL
Definition errtxt.h:150
int lockfile(const char *)
Definition lockfile.c:15
const char * opensql(const char *dir, const char **table)
Definition opensql.c:13
void logaddr(const char *dir, const char *event, const char *addr, const char *comment)
Definition log.c:30
void die_nomem()
Definition getconf.c:17
stralloc fn
char inbuf[1024]
char outbuf[1024]
buffer bi
buffer bo
char * userhost
stralloc domain
Definition ezmlm-split.c:47
stralloc addr
Definition ezmlm-cron.c:45
int fdlock
Definition ezmlm-cron.c:71
int fd
Definition ezmlm-cgi.c:141
int match
Definition ezmlm-cgi.c:140
stralloc quoted
Definition ezmlm-clean.c:90
stralloc comment
stralloc fnnew
MYSQL * mysql
Definition opensql.c:23
const char * logmsg(const char *dir, unsigned long num, unsigned long listno, unsigned long subs, int done)
Definition loginfo.c:32
void die_write(void)
Definition subscribe.c:41
int subscribe(const char *dbname, const char *userhost, int flagadd, const char *comment, const char *event, int flagmysql, int forcehash, const char *tab)
Definition subscribe.c:76
void die_read(void)
Definition subscribe.c:40