ezmlmx 0.68
ezmlmx
Loading...
Searching...
No Matches
ezmlm-archive.c
Go to the documentation of this file.
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <unistd.h>
4#include "alloc.h"
5#include "error.h"
6#include "stralloc.h"
7#include "str.h"
8#include "sig.h"
9#include "getconf.h"
10#include "logmsg.h"
11#include "getln.h"
12#include "buffer.h"
13#include "readwrite.h"
14#include "fmt.h"
15#include "getoptb.h"
16#include "idxthread.h"
17#include "makehash.h"
18#include "lock.h"
19#include "open.h"
20#include "scan.h"
21#include "idx.h"
22#include "errtxt.h"
23#include "lockfile.h"
24#include "auto_version.h"
25
26#define WHO "ezmlm-archive"
27
32
33buffer bi;
34char inbuf[1024];
35buffer bo;
36char outbuf[1024];
37buffer bn;
38char numbuf[16];
39
40stralloc line = {0};
41stralloc num = {0};
42stralloc fn = {0};
43stralloc fnn = {0};
44
45char strnum[FMT_ULONG];
46int flagerror = 0;
47int flagsync = 1; /* sync() by default, not for -c or -f or -t */
48char *dir;
49
50struct ca {
51 char *s; /* start */
52 unsigned int l; /* length */
54
55static void die_nomem() { logmsg(WHO,111,FATAL,ERR_NOMEM); }
56static void die_usage() { logmsg(WHO,100,USAGE,"ezmlm-archive [-cCFsSTvV] [-f min_msg] [-t max_msg] dir"); }
57
63
64void close_proper(buffer *bo,char *s,char *sn)
65{
66 if (buffer_flush(bo) == -1)
67 logmsg(WHO,111,FATAL,B(ERR_FLUSH,dir,"/",s));
68 if (flagsync)
69 if (fsync(bo->fd) == -1)
70 logmsg(WHO,111,FATAL,B(ERR_SYNC,dir,"/",s));
71 if (close(bo->fd) == -1)
72 logmsg(WHO,111,FATAL,B(ERR_CLOSE,dir,"/",s));
73 if (rename(sn,s) == -1)
74 logmsg(WHO,111,FATAL,B(ERR_MOVE,dir,"/",s));
75}
76
82
83void write_threads(msgentry *msgtable,subentry *subtable,authentry *authtable,dateentry *datetable,unsigned long from,unsigned long to)
84{
85 msgentry *pmsgt;
86 subentry *psubt, *psubtm, *psubtlast;
87 subentry *presubt = (subentry *)0;
88 authentry *pautht;
89 dateentry *pdatet;
90 const char *cp;
91 const char *cp1;
92 unsigned long msg;
93 unsigned long ulmsginthread;
94 unsigned long subnum;
95 unsigned long authnum;
96 unsigned long msgnum;
97 unsigned int pos;
98 unsigned int startdate, nextdate;
99 unsigned int startmsg, nextmsg;
100 int fd = -1;
101 int fdn = -1;
102 int match;
103 int ffound;
104 int lineno;
105 int res;
106 int r;
107
108 psubtm = subtable; /* now for new threads */
109 pdatet = datetable;
110 nextmsg = 0L;
111 nextdate = pdatet->date;
112
113 while (psubtm->sub) { /* these are in msgnum order */
114 if (!presubt) /* for rewind */
115 if (psubtm->lastmsg >= nextmsg)
116 presubt = psubtm; /* this thread extends beyond current month */
117
118 if (psubtm->firstmsg >= nextmsg) { /* done with this month */
119 if (fdn != -1) close_proper(&bo,fn.s,fnn.s);
120 psubtlast = psubtm; /* last thread done */
121 if (presubt) /* need to rewind? */
122 psubtm = presubt; /* do it */
123 psubt = psubtm; /* tmp pointer to reset done flag */
124 presubt = (subentry *)0; /* reset rewind pointer */
125 pdatet++; /* next month */
126 startdate = nextdate; /* startdate */
127 nextdate = pdatet->date; /* end date */
128 startmsg = nextmsg; /* first message in month */
129 nextmsg = pdatet->msg; /* first message in next month */
130
131 if (!stralloc_copys(&fn,"archive/threads/")) die_nomem();
132 if (!stralloc_catb(&fn,strnum,fmt_uint(strnum,startdate))) die_nomem();
133 if (!stralloc_copy(&fnn,&fn)) die_nomem();
134 if (!stralloc_0(&fn)) die_nomem();
135 if (!stralloc_cats(&fnn,"n")) die_nomem();
136 if (!stralloc_0(&fnn)) die_nomem();
137 if ((fdn = open_trunc(fnn.s)) == -1)
138 logmsg(WHO,111,FATAL,B(ERR_CREATE,dir,"/",fnn.s));
139 buffer_init(&bo,buffer_unixwrite,fdn,outbuf,sizeof(outbuf));
140 if ((fd = open_read(fn.s)) == -1) {
141 if (errno != ENOENT)
142 logmsg(WHO,111,FATAL,B(ERR_OPEN,dir,"/",fnn.s));
143 } else {
144 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
145
146 for (;;) {
147 if (getln(&bi,&line,&match,'\n') == -1)
148 logmsg(WHO,111,FATAL,B(ERR_READ,dir,"/",fnn.s));
149 if (!match) break;
150 pos = scan_ulong(line.s,&msgnum);
151 pos++; /* skip ':' */
152 if (msgnum >= from) continue;/* ignore entries from threading range */
153 if (line.len < pos + HASHLEN) {
154 flagerror = -1; /* and bad ones */
155 continue;
156 }
157 psubt = subtable;
158 cp = line.s + pos;
159 ffound = 0; /* search among already known subjects */
160
161 for (;;) {
162 res = str_diffn(psubt->sub,cp,HASHLEN);
163 if (res < 0) {
164 if (psubt->higher)
165 psubt = psubt->higher;
166 else
167 break;
168 } else if (res > 0) {
169 if (psubt->lower)
170 psubt = psubt->lower;
171 else
172 break;
173 } else {
174 ffound = 1;
175 break;
176 }
177 }
178
179 if (!ffound) {
180 if (buffer_put(&bo,line.s,line.len) == -1)
181 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
182 } else { /* new # of msg in thread */
183 cp += HASHLEN; /* HASHLEN [#] Subject always \n at end */
184 if (*(cp++) == ' ' && *(cp++) == '[') {
185 cp += scan_ulong(cp,&ulmsginthread);
186 if (*cp == ']') {
187 psubt->msginthread += (unsigned char) (ulmsginthread & 0xff);
188 }
189 } else
190 flagerror = -5;
191 }
192 }
193 close(fd);
194 }
195 continue;
196 } /* done month if */
197
198 if (psubtm->firstmsg < nextmsg && psubtm->lastmsg >= startmsg) {
199 if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,psubtm->lastmsg)))
200 die_nomem();
201 if (!stralloc_cats(&line,":")) die_nomem();
202 if (!stralloc_catb(&line,psubtm->sub,HASHLEN)) die_nomem();
203 if (!stralloc_cats(&line," [")) die_nomem();
204 if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,(unsigned long) psubtm->msginthread)))
205 die_nomem();
206 if (!stralloc_cats(&line,"]")) die_nomem();
207 if (!stralloc_catb(&line,psubtm->sub + HASHLEN,psubtm->sublen - HASHLEN))
208 die_nomem(); /* has \n */
209 if (buffer_put(&bo,line.s,line.len) == -1)
210 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
211
212 }
213 psubtm++;
214 }
215 if (fdn != -1)
216 close_proper(&bo,fn.s,fnn.s);
217
218 psubt = subtable;
219 while (psubt->sub) { /* now the threads */
220 if (!stralloc_copys(&fn,"archive/subjects/")) die_nomem();
221 if (!stralloc_catb(&fn,psubt->sub,2)) die_nomem();
222 if (!stralloc_0(&fn)) die_nomem();
223 if (mkdir(fn.s,0755) == -1)
224 if (errno != EEXIST)
225 logmsg(WHO,111,FATAL,B(ERR_CREATE,dir,"/",fnn.s));
226 fn.s[fn.len - 1] = '/';
227 if (!stralloc_catb(&fn,psubt->sub+2,HASHLEN-2)) die_nomem();
228 if (!stralloc_copy(&fnn,&fn)) die_nomem();
229 if (!stralloc_cats(&fnn,"n")) die_nomem();
230 if (!stralloc_0(&fn)) die_nomem();
231 if (!stralloc_0(&fnn)) die_nomem();
232 if ((fdn = open_trunc(fnn.s)) == -1)
233 logmsg(WHO,111,FATAL,B(ERR_CREATE,fnn.s));
234 buffer_init(&bo,buffer_unixwrite,fdn,outbuf,sizeof(outbuf));
235 if ((fd = open_read(fn.s)) == -1) {
236 if (errno != ENOENT)
237 logmsg(WHO,111,FATAL,B(ERR_OPEN,fn.s));
238 if (buffer_puts(&bo,psubt->sub) == -1) /* write subject */
239 logmsg(WHO,111,FATAL,B(ERR_WRITE,fnn.s));
240 } else { /* copy data */
241 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
242 lineno = 0;
243
244 for (;;) { // STOP
245 if (getln(&bi,&line,&match,'\n') == -1)
246 logmsg(WHO,111,FATAL,B(ERR_READ,dir,"/",fnn.s));
247 if (!match) break;
248 if (!lineno) { /* write subject */
249 if (line.len < HASHLEN + 1 || line.s[HASHLEN] != ' ')
250 flagerror = -3;
251 if (buffer_put(&bo,line.s,line.len) == -1)
252 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
253 lineno = 1;
254 continue;
255 }
256 r = scan_ulong(line.s,&msgnum);
257 if (msgnum >= from) break;
258 if (buffer_put(&bo,line.s,line.len) == -1)
259 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
260 }
261 r = close(fd); /* close old index */
262 }
263
264 subnum = (unsigned long) (psubt - subtable + 1); /* idx of this subj */
265 pmsgt = msgtable + psubt->firstmsg - from; /* first message entry */
266 for (msg = psubt->firstmsg; msg <= psubt->lastmsg; msg++) {
267 if (pmsgt->subnum == subnum) {
268 if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem();
269 if (!stralloc_cats(&line,":")) die_nomem();
270 if (!stralloc_catb(&line,strnum,fmt_uint(strnum,pmsgt->date)))
271 die_nomem();
272 if (!stralloc_cats(&line,":")) die_nomem();
273 if (pmsgt->authnum) {
274 pautht = authtable + pmsgt->authnum - 1;
275 cp = pautht->auth;
276 cp1 = cp + str_chr(cp,' ');
277 if (cp + HASHLEN != cp1)
278 logmsg(WHO,100,ERROR,ERR_BAD_INDEX);
279 if (!stralloc_cats(&line,cp))
280 die_nomem(); /* hash */
281 } else
282 if (!stralloc_cats(&line,"\n")) die_nomem();
283 if (buffer_put(&bo,line.s,line.len) == -1)
284 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
285 }
286 pmsgt++;
287 }
288 close_proper(&bo,fn.s,fnn.s);
289 psubt++;
290 }
291
292 /* (no master author index) */
293
294 pautht = authtable;
295
296 while (pautht->auth) { /* now the authors */
297 if (!stralloc_copys(&fn,"archive/authors/")) die_nomem();
298 if (!stralloc_catb(&fn,pautht->auth,2)) die_nomem();
299 if (!stralloc_0(&fn)) die_nomem();
300 if (mkdir(fn.s,0755) == -1)
301 if (errno != EEXIST)
302 logmsg(WHO,111,FATAL,B(ERR_CREATE,dir,"/",fn.s));
303 fn.s[fn.len - 1] = '/';
304 if (!stralloc_catb(&fn,pautht->auth+2,HASHLEN-2)) die_nomem();
305 if (!stralloc_copy(&fnn,&fn)) die_nomem();
306 if (!stralloc_cats(&fnn,"n")) die_nomem();
307 if (!stralloc_0(&fn)) die_nomem();
308 if (!stralloc_0(&fnn)) die_nomem();
309 if ((fdn = open_trunc(fnn.s)) == -1)
310 logmsg(WHO,111,FATAL,B(ERR_CREATE,fnn.s));
311 buffer_init(&bo,write,fdn,outbuf,sizeof(outbuf));
312 if ((fd = open_read(fn.s)) == -1) {
313 if (errno != ENOENT)
314 logmsg(WHO,111,FATAL,B(ERR_OPEN,fn.s));
315 else { /* didn't exist before: write author */
316 if (buffer_put(&bo,pautht->auth,pautht->authlen) == -1)
317 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
318 }
319 } else { /* copy data */
320 buffer_init(&bi,buffer_unixread,fd,inbuf,sizeof(inbuf));
321 lineno = 0;
322
323 for (;;) {
324 if (getln(&bi,&line,&match,'\n') == -1)
325 logmsg(WHO,111,FATAL,B(ERR_READ,dir,"/",fn.s));
326 if (!match) break;
327 if (!lineno) { /* write author */
328 if (line.len < HASHLEN + 1 || line.s[HASHLEN] != ' ')
329 flagerror = - 4;
330 if (buffer_put(&bo,line.s,line.len) == -1)
331 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
332 lineno = 1;
333 continue;
334 }
335 scan_ulong(line.s,&msgnum);
336 if (msgnum >= from) break;
337 if (buffer_put(&bo,line.s,line.len) == -1)
338 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
339 }
340 close(fd); /* close old index */
341 }
342
343 authnum = (unsigned long) (pautht - authtable + 1); /* idx of this auth */
344 pmsgt = msgtable + pautht->firstmsg - from; /* first message entry */
345 for (msg = pautht->firstmsg; msg <= to; msg++) {
346 if (pmsgt->authnum == authnum) {
347 if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem();
348 if (!stralloc_cats(&line,":")) die_nomem();
349 if (!stralloc_catb(&line,strnum,fmt_uint(strnum,pmsgt->date)))
350 die_nomem();
351 if (!stralloc_cats(&line,":")) die_nomem();
352 if (pmsgt->subnum) {
353 psubt = subtable + pmsgt->subnum - 1;
354 if (!stralloc_catb(&line,psubt->sub,psubt->sublen))
355 die_nomem();
356 }
357 if (buffer_put(&bo,line.s,line.len) == -1)
358 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
359 }
360 pmsgt++;
361 }
362 close_proper(&bo,fn.s,fnn.s);
363 pautht++;
364 }
365}
366
367int main(int argc,char **argv)
368{
369 unsigned long archnum = 0L;
370 unsigned long to = 0L;
371 unsigned long max;
372 int fd;
373 int fdlock;
374 int flagcreate = 0;
375 int flagsyncall = 0;
376 int opt;
377 msgentry *msgtable;
378 subentry *subtable;
379 authentry *authtable;
380 dateentry *datetable;
381
382 umask(022);
383 sig_pipeignore();
384
385 while ((opt = getoptb(argc,argv,"cCf:FsSt:TvV")) != opteof)
386 switch (opt) {
387 case 'c': flagcreate = 1; flagsync = 0; break; /* start at beginning of archive */
388 case 'C': flagcreate = 0; break; /* Do only archnum+1 => num */
389 case 'f': if (optarg) { scan_ulong(optarg,&archnum); archnum = (archnum / 100) * 100; }
390 flagsync = 0; break;
391 case 'F': archnum = 0; break;
392 case 's': flagsyncall = 1; break;
393 case 'S': flagsyncall = 0; break;
394 case 't': if (optarg) { scan_ulong(optarg,&to); } flagsync = 0; break;
395 case 'T': to = 0; break;
396 case 'v':
397 case 'V': logmsg(WHO,0,VERSION,auto_version);
398 default: die_usage();
399 }
400
401 if (flagsyncall) flagsync = 1; /* overrides */
402 dir = argv[optind++];
403 if (!dir) die_usage();
404 if (chdir(dir) == -1)
405 logmsg(WHO,111,FATAL,B(ERR_SWITCH,dir));
406
407 if (mkdir("archive/threads",0755) == -1)
408 if (errno != EEXIST)
409 logmsg(WHO,111,FATAL,B(ERR_CREATE,dir,"/archive/threads"));
410 if (mkdir("archive/subjects",0755) == -1)
411 if (errno != EEXIST)
412 logmsg(WHO,111,FATAL,B(ERR_CREATE,dir,"/archive/subjects"));
413 if (mkdir("archive/authors",0755) == -1)
414 if (errno != EEXIST)
415 logmsg(WHO,111,FATAL,B(ERR_CREATE,dir,"/archive/authors"));
416
417 /* Lock list to assure that no ezmlm-send is working on it */
418 /* and that the "num" message is final */
419
420 fdlock = lockfile("lock");
421 if (!getconf_line(&num,"num",0,dir)) /* get num */
422 logmsg(WHO,100,ERROR,ERR_EMPTY_LIST);
423 close(fdlock);
424
425 if (!stralloc_0(&num)) die_nomem(); /* parse num */
426 scan_ulong(num.s,&max);
427 if (!to || to > max) to = max;
428
429 fdlock = lockfile("archive/lock"); /* lock index */
430 if (!flagcreate && !archnum) { /* adjust archnum (from) / to */
431 if (getconf_line(&num,"archnum",0,dir)) {
432 if (!stralloc_0(&num)) die_nomem();
433 scan_ulong(num.s,&archnum);
434 archnum++;
435 }
436 }
437
438 if (archnum > to) _exit(0); /* nothing to do */
439
440 /* do the subject threading */
441 idx_mkthreads(&msgtable,&subtable,&authtable,&datetable,archnum,to,max,0);
442
443 /* update the index */
444 write_threads(msgtable,subtable,authtable,datetable,archnum,to);
445
446 /* update archnum */
447 if ((fd = open_trunc("archnumn")) == -1)
448 logmsg(WHO,111,FATAL,B(ERR_CREATE,dir,"/archnumn"));
449 buffer_init(&bn,buffer_unixwrite,fd,numbuf,sizeof(numbuf));
450 if (buffer_put(&bn,strnum,fmt_ulong(strnum,to)) == -1)
451 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
452 if (buffer_puts(&bn,"\n") == -1)
453 logmsg(WHO,111,FATAL,B(ERR_WRITE,dir,"/",fnn.s));
454 close_proper(&bn,"archnum","archnumn");
455
456 switch (flagerror) {
457 case 0: _exit(0); /* go bye-bye */
458 case -1: logmsg(WHO,99,WARN,"threads entry with illegal format"); break;
459 case -2: logmsg(WHO,99,WARN,"thread in index, but threadfile mibing"); break;
460 case -3: logmsg(WHO,99,WARN,"a subject file lacks subject"); break;
461 case -4: logmsg(WHO,99,WARN,"an author file lacks author/hash"); break;
462 case -5: logmsg(WHO,99,WARN,"threads entry lacks message count"); break;
463 default: logmsg(WHO,99,WARN,"something happened that isn't quite right");
464 }
465}
const char auto_version[]
Error messages. If you translate these, I would urge you to keep the English version as well....
#define ERR_BAD_INDEX
Definition errtxt.h:64
#define ERR_FLUSH
Definition errtxt.h:20
#define ERR_NOMEM
Definition errtxt.h:14
#define ERR_SYNC
Definition errtxt.h:22
#define ERR_OPEN
Definition errtxt.h:30
#define ERR_MOVE
Definition errtxt.h:29
#define ERR_READ
Definition errtxt.h:18
#define ERR_CREATE
Definition errtxt.h:28
#define ERR_SWITCH
Definition errtxt.h:42
#define ERR_EMPTY_LIST
Definition errtxt.h:62
#define ERR_WRITE
Definition errtxt.h:17
#define ERR_CLOSE
Definition errtxt.h:16
int lockfile(const char *)
Definition lockfile.c:15
void die_nomem()
Definition getconf.c:17
int getconf_line(stralloc *sa, const char *fn, int flagrequired, const char *dir)
Definition getconf.c:53
#define WHO
Definition author.c:1
unsigned long max
Definition ezmlm-get.c:83
int flagerror
stralloc fn
char inbuf[1024]
void close_proper(buffer *bo, char *s, char *sn)
flush,sync,close,move sn->s
buffer bn
char outbuf[1024]
stralloc fnn
stralloc num
char numbuf[16]
char * dir
void write_threads(msgentry *msgtable, subentry *subtable, authentry *authtable, dateentry *datetable, unsigned long from, unsigned long to)
buffer bi
buffer bo
int flagsync
stralloc from
int main()
Definition ezmlm-weed.c:69
unsigned long lineno
Definition ezmlm-split.c:52
int opt
Definition ezmlm-cron.c:53
const char * cp
Definition ezmlm-cron.c:76
int fdlock
Definition ezmlm-cron.c:71
int fd
Definition ezmlm-cgi.c:141
int match
Definition ezmlm-cgi.c:140
#define HASHLEN
Definition idxthread.c:25
void idx_mkthreads(msgentry **pmsgtable, subentry **psubtable, authentry **pauthtable, dateentry **pdatetable, unsigned long msg_from, unsigned long msg_to, unsigned long msg_latest, int locked)
Definition idxthread.c:156
unsigned long msgnum
stralloc to
Definition ezmlm-clean.c:97
unsigned int l
char * s
unsigned int date
Definition idx.h:330
unsigned long authnum
Definition idx.h:329
unsigned long subnum
Definition idx.h:328
void * higher
Definition idx.h:334
unsigned long firstmsg
Definition idx.h:342
char * sub
Definition idx.h:336
unsigned char msginthread
Definition idx.h:344
unsigned long lastmsg
Definition idx.h:343
void * lower
Definition idx.h:335
unsigned int sublen
Definition idx.h:341
unsigned long firstmsg
Definition idx.h:356
unsigned long authlen
Definition idx.h:355
char * auth
Definition idx.h:350
unsigned int date
Definition idx.h:362
unsigned int msg
Definition idx.h:363
const char * logmsg(const char *dir, unsigned long num, unsigned long listno, unsigned long subs, int done)
Definition loginfo.c:32