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