1 /*$Id: subscribe.c,v 1.5 1999/10/12 23:38:36 lindberg Exp $*/
2 /*$Name: ezmlm-idx-040 $*/
13 #include "subscribe.h"
20 static void die_nomem(fatal)
23 strerr_die2x(111,fatal,ERR_NOMEM);
26 static stralloc addr = {0};
27 static stralloc lcaddr = {0};
28 static stralloc line = {0};
29 static stralloc domain = {0};
30 static stralloc logline = {0};
31 static stralloc quoted = {0};
32 static stralloc fnnew = {0};
33 static stralloc fn = {0};
34 static stralloc fnlock = {0};
35 static char szh[FMT_ULONG];
40 strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
46 strerr_die4sys(111,fatal,ERR_WRITE,fnnew.s,": ");
51 static char ssbuf[256];
53 static substdio ssnew;
54 static char ssnewbuf[256];
56 int subscribe(dbname,userhost,flagadd,comment,event,flagmysql,
58 /* add (flagadd=1) or remove (flagadd=0) userhost from the subscr. database */
59 /* dbname. Comment is e.g. the subscriber from line or name. It is added to */
60 /* the log. Event is the action type, e.g. "probe", "manual", etc. The */
61 /* direction (sub/unsub) is inferred from flagadd. Returns 1 on success, 0 */
62 /* on failure. If flagmysql is set and the file "sql" is found in the */
63 /* directory dbname, it is parsed and a mysql db is assumed. if forcehash is */
64 /* >=0 it is used in place of the calculated hash. This makes it possible to */
65 /* add addresses with a hash that does not exist. forcehash has to be 0..99. */
66 /* for unsubscribes, the address is only removed if forcehash matches the */
67 /* actual hash. This way, ezmlm-manage can be prevented from touching certain*/
68 /* addresses that can only be removed by ezmlm-unsub. Usually, this would be */
69 /* used for sublist addresses (to avoid removal) and sublist aliases (to */
70 /* prevent users from subscribing them (although the cookie mechanism would */
71 /* prevent the resulting duplicate message from being distributed. */
85 char szhash[3] = "00";
89 unsigned char ch,lcch;
93 if (userhost[str_chr(userhost,'\n')])
94 strerr_die2x(100,fatal,ERR_ADDR_NL);
96 if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
97 if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
99 strerr_die2x(100,fatal,ERR_ADDR_LONG);
101 j = byte_rchr(addr.s,addr.len,'@');
103 strerr_die2x(100,fatal,ERR_ADDR_AT);
104 case_lowerb(addr.s + j + 1,addr.len - j - 1);
105 if (!stralloc_copy(&lcaddr,&addr)) die_nomem(fatal);
106 case_lowerb(lcaddr.s + 1,j - 1); /* make all-lc version of address */
108 if (forcehash >= 0 && forcehash <= 52) {
109 ch = lcch = (unsigned char) forcehash;
113 for (j = 0;j < addr.len;++j) {
114 h = (h + (h << 5)) ^ (uint32) (unsigned char) addr.s[j];
115 lch = (lch + (lch << 5)) ^ (uint32) (unsigned char) lcaddr.s[j];
117 lcch = 64 + (lch % 53);
121 if (!stralloc_0(&addr)) die_nomem(fatal);
122 if (!stralloc_0(&lcaddr)) die_nomem(fatal);
123 if (!stralloc_copys(&fn,dbname)) die_nomem(fatal);
124 if (!stralloc_copys(&fnlock,dbname)) die_nomem(fatal);
126 if (!stralloc_cats(&fn,"/subscribers/")) die_nomem(fatal);
127 if (!stralloc_catb(&fn,&lcch,1)) die_nomem(fatal);
128 if (!stralloc_copy(&fnnew,&fn)) die_nomem(fatal);
129 /* code later depends on fnnew = fn + 'n' */
130 if (!stralloc_cats(&fnnew,"n")) die_nomem(fatal);
131 if (!stralloc_cats(&fnlock,"/lock")) die_nomem(fatal);
132 if (!stralloc_0(&fnnew)) die_nomem(fatal);
133 if (!stralloc_0(&fn)) die_nomem(fatal);
134 if (!stralloc_0(&fnlock)) die_nomem(fatal);
136 fdlock = open_append(fnlock.s);
138 strerr_die4sys(111,fatal,ERR_OPEN,fnlock.s,": ");
139 if (lock_ex(fdlock) == -1)
140 strerr_die4sys(111,fatal,ERR_OBTAIN,fnlock.s,": ");
142 /* do lower case hashed version first */
143 fdnew = open_trunc(fnnew.s);
144 if (fdnew == -1) die_write(fatal);
145 substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
149 fd = open_read(fn.s);
151 if (errno != error_noent) { close(fdnew); die_read(fatal); }
154 substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
157 if (getln(&ss,&line,&match,'\0') == -1) {
158 close(fd); close(fdnew); die_read(fatal);
161 if (line.len == addr.len)
162 if (!case_diffb(line.s,line.len,addr.s)) {
167 if (substdio_bput(&ssnew,line.s,line.len) == -1) {
168 close(fd); close(fdnew); die_write(fatal);
175 if (flagadd && !flagwasthere)
176 if (substdio_bput(&ssnew,addr.s,addr.len) == -1) {
177 close(fdnew); die_write(fatal);
180 if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
181 if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
184 if (rename(fnnew.s,fn.s) == -1)
185 strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
187 if ((ch == lcch) || flagwasthere) {
189 if (flagadd ^ flagwasthere) {
190 if (!stralloc_0(&addr)) die_nomem(fatal);
191 log(dbname,event,addr.s+1,comment);
197 /* If unsub and not found and hashed differ, OR */
198 /* sub and not found (so added with new hash) */
199 /* do the 'case-dependent' hash */
201 fn.s[fn.len - 2] = ch;
202 fnnew.s[fnnew.len - 3] = ch;
203 fdnew = open_trunc(fnnew.s);
204 if (fdnew == -1) die_write(fatal);
205 substdio_fdbuf(&ssnew,write,fdnew,ssnewbuf,sizeof(ssnewbuf));
207 fd = open_read(fn.s);
209 if (errno != error_noent) { close(fdnew); die_read(fatal); }
211 substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
214 if (getln(&ss,&line,&match,'\0') == -1)
215 { close(fd); close(fdnew); die_read(fatal); }
217 if (line.len == addr.len)
218 if (!case_diffb(line.s,line.len,addr.s)) {
220 continue; /* always want to remove from case-sensitive hash */
222 if (substdio_bput(&ssnew,line.s,line.len) == -1)
223 { close(fd); close(fdnew); die_write(fatal); }
229 if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
230 if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
233 if (rename(fnnew.s,fn.s) == -1)
234 strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
237 if (flagadd ^ flagwasthere) {
238 if (!stralloc_0(&addr)) die_nomem(fatal);
239 log(dbname,event,addr.s+1,comment);