chiark / gitweb /
Debianization and various other fixes.
[ezmlm] / sub_std / subscribe.c
1 /*$Id: subscribe.c,v 1.5 1999/10/12 23:38:36 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 "strerr.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 "uint32.h"
15 #include "fmt.h"
16 #include "errtxt.h"
17 #include "log.h"
18 #include "idx.h"
19
20 static void die_nomem(fatal)
21 char *fatal;
22 {
23   strerr_die2x(111,fatal,ERR_NOMEM);
24 }
25
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];
36
37 void die_read(fatal)
38 char *fatal;
39 {
40   strerr_die4sys(111,fatal,ERR_READ,fn.s,": ");
41 }
42
43 void die_write(fatal)
44 char *fatal;
45 {
46   strerr_die4sys(111,fatal,ERR_WRITE,fnnew.s,": ");
47 }
48
49 static int fd;
50 static substdio ss;
51 static char ssbuf[256];
52 static int fdnew;
53 static substdio ssnew;
54 static char ssnewbuf[256];
55
56 int subscribe(dbname,userhost,flagadd,comment,event,flagmysql,
57         forcehash,tab,fatal)
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. */
72
73 char *dbname;
74 char *userhost;
75 int flagadd;
76 char *comment;
77 char *event;
78 int flagmysql;
79 int forcehash;
80 char *tab;
81 char *fatal;
82 {
83   int fdlock;
84
85   char szhash[3] = "00";
86
87   unsigned int j;
88   uint32 h,lch;
89   unsigned char ch,lcch;
90   int match;
91   int flagwasthere;
92
93   if (userhost[str_chr(userhost,'\n')])
94     strerr_die2x(100,fatal,ERR_ADDR_NL);
95
96     if (!stralloc_copys(&addr,"T")) die_nomem(fatal);
97     if (!stralloc_cats(&addr,userhost)) die_nomem(fatal);
98     if (addr.len > 401)
99       strerr_die2x(100,fatal,ERR_ADDR_LONG);
100
101     j = byte_rchr(addr.s,addr.len,'@');
102     if (j == 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 */
107
108     if (forcehash >= 0 && forcehash <= 52) {
109       ch = lcch = (unsigned char) forcehash;
110     } else {
111       h = 5381;
112       lch = h;
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];
116       }
117       lcch = 64 + (lch % 53);
118       ch = 64 + (h % 53);
119     }
120
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);
125
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);
135
136     fdlock = open_append(fnlock.s);
137     if (fdlock == -1)
138       strerr_die4sys(111,fatal,ERR_OPEN,fnlock.s,": ");
139     if (lock_ex(fdlock) == -1)
140       strerr_die4sys(111,fatal,ERR_OBTAIN,fnlock.s,": ");
141
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));
146
147     flagwasthere = 0;
148
149     fd = open_read(fn.s);
150     if (fd == -1) {
151       if (errno != error_noent) { close(fdnew); die_read(fatal); }
152     }
153     else {
154       substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
155
156       for (;;) {
157         if (getln(&ss,&line,&match,'\0') == -1) {
158           close(fd); close(fdnew); die_read(fatal);
159         }
160         if (!match) break;
161         if (line.len == addr.len)
162           if (!case_diffb(line.s,line.len,addr.s)) {
163             flagwasthere = 1;
164             if (!flagadd)
165               continue;
166           }
167         if (substdio_bput(&ssnew,line.s,line.len) == -1) {
168           close(fd); close(fdnew); die_write(fatal);
169         }
170       }
171
172       close(fd);
173     }
174
175     if (flagadd && !flagwasthere)
176       if (substdio_bput(&ssnew,addr.s,addr.len) == -1) {
177         close(fdnew); die_write(fatal);
178       }
179
180     if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
181     if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
182     close(fdnew);
183
184     if (rename(fnnew.s,fn.s) == -1)
185       strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
186
187     if ((ch == lcch) || flagwasthere) {
188       close(fdlock);
189       if (flagadd ^ flagwasthere) {
190         if (!stralloc_0(&addr)) die_nomem(fatal);
191         log(dbname,event,addr.s+1,comment);
192         return 1;
193       }
194       return 0;
195     }
196
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 */
200
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));
206
207     fd = open_read(fn.s);
208     if (fd == -1) {
209       if (errno != error_noent) { close(fdnew); die_read(fatal); }
210     } else {
211       substdio_fdbuf(&ss,read,fd,ssbuf,sizeof(ssbuf));
212
213       for (;;) {
214         if (getln(&ss,&line,&match,'\0') == -1)
215           { close(fd); close(fdnew); die_read(fatal); }
216         if (!match) break;
217         if (line.len == addr.len)
218           if (!case_diffb(line.s,line.len,addr.s)) {
219             flagwasthere = 1;
220             continue;   /* always want to remove from case-sensitive hash */
221           }
222         if (substdio_bput(&ssnew,line.s,line.len) == -1)
223           { close(fd); close(fdnew); die_write(fatal); }
224       }
225
226       close(fd);
227     }
228
229     if (substdio_flush(&ssnew) == -1) { close(fdnew); die_write(fatal); }
230     if (fsync(fdnew) == -1) { close(fdnew); die_write(fatal); }
231     close(fdnew);
232
233     if (rename(fnnew.s,fn.s) == -1)
234       strerr_die6sys(111,fatal,ERR_MOVE,fnnew.s," to ",fn.s,": ");
235
236     close(fdlock);
237     if (flagadd ^ flagwasthere) {
238       if (!stralloc_0(&addr)) die_nomem(fatal);
239       log(dbname,event,addr.s+1,comment);
240       return 1;
241     }
242     return 0;
243
244 }