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 */