1 /*$Id: subscribe.c,v 1.3 1999/10/07 23:31:01 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
13 #include "subscribe.h"
23 static void die_nomem(fatal)
26 strerr_die2x(111,fatal,ERR_NOMEM);
29 static stralloc addr = {0};
30 static stralloc lcaddr = {0};
31 static stralloc line = {0};
32 static stralloc domain = {0};
33 static stralloc logline = {0};
34 static stralloc fnnew = {0};
35 static stralloc fn = {0};
36 static stralloc fnlock = {0};
41 strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
47 strerr_die4sys(111,fatal,ERR_WRITE,fnnew.s,": ");
52 static char ssbuf[256];
54 static substdio ssnew;
55 static char ssnewbuf[256];
57 int subscribe(dbname,userhost,flagadd,comment,event,flagsql,
59 /* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database */
60 /* dbname. Comment is e.g. the subscriber from line or name. It is added to */
61 /* the log. Event is the action type, e.g. "probe", "manual", etc. The */
62 /* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0 */
63 /* on failure. If flagmysql is set and the file "sql" is found in the */
64 /* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */
65 /* >=0 it is used in place of the calculated hash. This makes it possible to */
66 /* add addresses with a hash that does not exist. forcehash has to be 0..99. */
67 /* for unsubscribes, the address is only removed if forcehash matches the */
68 /* actual hash. This way, ezmlm-manage can be prevented from touching certain*/
69 /* addresses that can only be removed by ezmlm-unsub. Usually, this would be */
70 /* used for sublist addresses (to avoid removal) and sublist aliases (to */
71 /* prevent users from subscribing them (although the cookie mechanism would */
72 /* prevent the resulting duplicate message from being distributed. */
89 char szhash[3] = "00";
91 char *table = (char *) 0;
92 char **ptable = &table;
96 unsigned char ch,lcch;
100 if (userhost[str_chr(userhost,'\n')])
101 strerr_die2x(100,fatal,ERR_ADDR_NL);
103 if (tab) ptable = &tab;
105 if (!flagsql || (r = opensql(dbname,ptable))) {
106 if (r && *r) strerr_die2x(111,fatal,r);
107 /* fallback to local db */
108 if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
109 if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
111 strerr_die2x(100,fatal,ERR_ADDR_LONG);
113 j = byte_rchr(addr.s,addr.len,'@');
115 strerr_die2x(100,fatal,ERR_ADDR_AT);
116 case_lowerb(addr.s + j + 1,addr.len - j - 1);
117 if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
118 case_lowerb(lcaddr.s + 1,j - 1); /* make all-lc version of address */
120 if (forcehash >= 0 && forcehash <= 52) {
121 ch = lcch = (unsigned char) forcehash;
125 for (j = 0;j < addr.len;++j) {
126 h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
127 lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
129 lcch = 64 + (lch % 53);
133 if (!stralloc_0(&addr)) die_nomem(fatal);
134 if (!stralloc_0(&lcaddr)) die_nomem(fatal);
135 if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
136 if (!stralloc_copys(&fnlock,dbname)) die_nomem(fatal);
138 if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
139 if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
140 if (!stralloc_copy(&fnnew,&fn)) die_nomem(fatal);
141 /* code later depends on fnnew = fn + 'n' */
142 if (!stralloc_cats(&fnnew,"n")) die_nomem(fatal);
143 if (!stralloc_cats(&fnlock,"/lock")) die_nomem(fatal);
144 if (!stralloc_0(&fnnew)) die_nomem(fatal);
145 if (!stralloc_0(&fn)) die_nomem(fatal);
146 if (!stralloc_0(&fnlock)) die_nomem(fatal);
148 fdlock = open_append(fnlock.s);
150 strerr_die4sys(111,fatal,ERR_OPEN,fnlock.s,": ");
151 if (lock_ex(fdlock) == -1)
152 strerr_die4sys(111,fatal,ERR_OBTAIN,fnlock.s,": ");
154 /* do lower case hashed version first */
155 fdnew = open_trunc(fnnew.s);
156 if (fdnew == -1) die_write(fatal);
157 substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
161 fd = open_read(fn.s);
163 if (errno != error_noent) { close(fdnew); die_read(fatal); }
166 substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
169 if (getln(&ss,&line,&match,'\0') == -1) {
170 close(fd); close(fdnew); die_read(fatal);
173 if (line.len == addr.len)
174 if (!case_diffb(line.s,line.len,addr.s)) {
179 if (substdio_bput(&ssnew,line.s,line.len) == -1) {
180 close(fd); close(fdnew); die_write(fatal);
187 if (flagadd && !flagwasthere)
188 if (substdio_bput(&ssnew,addr.s,addr.len) == -1) {
189 close(fdnew); die_write(fatal);
192 if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
193 if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
196 if (rename(fnnew.s,fn.s) == -1)
197 strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
199 if ((ch == lcch) || flagwasthere) {
201 if (flagadd ^ flagwasthere) {
202 if (!stralloc_0(&addr)) die_nomem(fatal);
203 log(dbname,event,addr.s+1,comment);
209 /* If unsub and not found and hashed differ, OR */
210 /* sub and not found (so added with new hash) */
211 /* do the 'case-dependent' hash */
213 fn.s[fn.len - 2] = ch;
214 fnnew.s[fnnew.len - 3] = ch;
215 fdnew = open_trunc(fnnew.s);
216 if (fdnew == -1) die_write(fatal);
217 substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
219 fd = open_read(fn.s);
221 if (errno != error_noent) { close(fdnew); die_read(fatal); }
223 substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
226 if (getln(&ss,&line,&match,'\0') == -1)
227 { close(fd); close(fdnew); die_read(fatal); }
229 if (line.len == addr.len)
230 if (!case_diffb(line.s,line.len,addr.s)) {
232 continue; /* always want to remove from case-sensitive hash */
234 if (substdio_bput(&ssnew,line.s,line.len) == -1)
235 { close(fd); close(fdnew); die_write(fatal); }
241 if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
242 if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
245 if (rename(fnnew.s,fn.s) == -1)
246 strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
249 if (flagadd ^ flagwasthere) {
250 if (!stralloc_0(&addr)) die_nomem(fatal);
251 log(dbname,event,addr.s+1,comment);
256 } else { /* SQL version */
257 domain.len = 0; /* clear domain */
258 /* lowercase and check address */
259 if (!stralloc_copys(&addr,userhost)) die_nomem(fatal);
260 if (addr.len > 255) /* this is 401 in std ezmlm. 255 */
261 /* should be plenty! */
262 strerr_die2x(100,fatal,ERR_ADDR_LONG);
263 j = byte_rchr(addr.s,addr.len,'@');
265 strerr_die2x(100,fatal,ERR_ADDR_AT);
267 case_lowerb(cpat + 1,addr.len - j - 1);
270 if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
271 case_lowerb(lcaddr.s,j); /* make all-lc version of address */
273 for (j = 0;j < lcaddr.len;++j) {
274 h = (h + (h << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
276 ch = (h % 53); /* 0 - 52 */
278 ch = (forcehash % 100);
280 szhash[0] = '0' + ch / 10; /* hash for sublist split */
281 szhash[1] = '0' + (ch % 10);
284 if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(fatal);
285 if (!stralloc_cats(&line,table)) die_nomem(fatal);
286 if (!stralloc_cats(&line," WHERE address ~* '^")) die_nomem(fatal);
287 if (!stralloc_cat(&line,&addr)) die_nomem(fatal); /* addr */
288 if (!stralloc_cats(&line,"$'")) die_nomem(fatal);
289 if (!stralloc_0(&line)) die_nomem(fatal);
290 result = PQexec(psql,line.s);
292 strerr_die2x(111,fatal,PQerrorMessage(psql));
293 if (PQresultStatus(result) != PGRES_TUPLES_OK)
294 strerr_die2x(111,fatal,PQresultErrorMessage(result));
296 if (PQntuples(result)>0) { /* there */
298 return 0; /* there */
299 } else { /* not there */
301 if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(fatal);
302 if (!stralloc_cats(&line,table)) die_nomem(fatal);
303 if (!stralloc_cats(&line," (address,hash) VALUES ('"))
305 if (!stralloc_cat(&line,&addr)) die_nomem(fatal); /* addr */
306 if (!stralloc_cats(&line,"',")) die_nomem(fatal);
307 if (!stralloc_cats(&line,szhash)) die_nomem(fatal); /* hash */
308 if (!stralloc_cats(&line,")")) die_nomem(fatal);
309 if (!stralloc_0(&line)) die_nomem(fatal);
310 result = PQexec(psql,line.s);
312 strerr_die2x(111,fatal,PQerrorMessage(psql));
313 if (PQresultStatus(result) != PGRES_COMMAND_OK)
314 strerr_die2x(111,fatal,PQresultErrorMessage(result));
317 if (!stralloc_copys(&line,"DELETE FROM ")) die_nomem(fatal);
318 if (!stralloc_cats(&line,table)) die_nomem(fatal);
319 if (!stralloc_cats(&line," WHERE address ~* '^")) die_nomem(fatal);
320 if (!stralloc_cat(&line,&addr)) die_nomem(fatal); /* addr */
321 if (forcehash >= 0) {
322 if (!stralloc_cats(&line,"$' AND hash=")) die_nomem(fatal);
323 if (!stralloc_cats(&line,szhash)) die_nomem(fatal);
325 if (!stralloc_cats(&line,"$' AND hash BETWEEN 0 AND 52"))
329 if (!stralloc_0(&line)) die_nomem(fatal);
330 result = PQexec(psql,line.s);
332 strerr_die2x(111,fatal,PQerrorMessage(psql));
333 if (PQresultStatus(result) != PGRES_COMMAND_OK)
334 strerr_die2x(111,fatal,PQresultErrorMessage(result));
335 if (atoi(PQcmdTuples(result))<1)
336 return 0; /* address wasn't there*/
340 /* log to subscriber log */
341 /* INSERT INTO t_slog (address,edir,etype,fromline) */
342 /* VALUES('address',{'+'|'-'},'etype','[comment]') */
344 if (!stralloc_copys(&logline,"INSERT INTO ")) die_nomem(fatal);
345 if (!stralloc_cats(&logline,table)) die_nomem(fatal);
346 if (!stralloc_cats(&logline,
347 "_slog (address,edir,etype,fromline) VALUES ('")) die_nomem(fatal);
348 if (!stralloc_cat(&logline,&addr)) die_nomem(fatal);
349 if (flagadd) { /* edir */
350 if (!stralloc_cats(&logline,"','+','")) die_nomem(fatal);
352 if (!stralloc_cats(&logline,"','-','")) die_nomem(fatal);
354 if (*(event + 1)) /* ezmlm-0.53 uses '' for ezmlm-manage's work */
355 if (!stralloc_catb(&logline,event+1,1)) die_nomem(fatal); /* etype */
356 if (!stralloc_cats(&logline,"','")) die_nomem(fatal);
357 if (comment && *comment) {
358 if (!stralloc_cats(&logline,comment)) die_nomem(fatal);
360 if (!stralloc_cats(&logline,"')")) die_nomem(fatal);
362 if (!stralloc_0(&logline)) die_nomem(fatal);
363 result = PQexec(psql,logline.s); /* log (ignore errors) */
366 if (!stralloc_0(&addr))
367 ; /* ignore errors */
368 log(dbname,event,addr.s,comment); /* also log to old log */
369 return 1; /* desired effect */