1 /*Id: ezmlm-make.c,v 1.31 1997/12/08 23:44:02 lindberg Exp lindberg $*/
2 /*$Name: ezmlm-idx-040 $*/
10 #include "readwrite.h"
21 /* defaults. All other flags are false = 0 */
22 char *defflags="ap"; /* archived list -a */
24 /* no ezmlm-archive -I */
25 /* no text edit for remote admin -D */
26 /* not in edit mode -E */
27 /* no subs list for remote admin -L */
28 /* no remote admin -R */
29 /* no message moderation -M */
30 /* no subscription moderation -S */
31 /* don't use .ezmlmrc from dot-file dir -C */
35 #define NO_FLAGS ('z' - 'a' + 1)
36 int flags[NO_FLAGS]; /* holds flags */
39 stralloc dotplus = {0};
40 stralloc dirplus = {0};
43 #define FATAL "ezmlm-make: fatal: "
44 #define WARNING "ezmlm-make: warning: "
49 "ezmlm-make: usage: ezmlm-make [-+] [ -a..zA..Z03..9 ] dir dot local host");
53 strerr_die2x(100,FATAL,ERR_SLASH);
57 strerr_die2x(100,FATAL,ERR_NEWLINE);
61 strerr_die2x(100,FATAL,ERR_QUOTE);
65 strerr_die2x(111,FATAL,ERR_NOMEM);
70 strerr_die4sys(111,FATAL,ERR_READ,dirplus.s,": ");
73 stralloc cmdline = {0};
74 stralloc outline = {0};
78 stralloc fname = {0}; /* file name */
79 stralloc oldfname = {0}; /* file name from prevoius tag */
80 stralloc dname = {0}; /* directory name */
81 stralloc lname = {0}; /* link name */
82 stralloc template = {0}; /* template file name */
83 stralloc ext1 = {0}; /* dot = dir/.qmail-ext1-ext2-list */
94 ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
95 ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
96 ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem(); u >>= 8;
97 ch = (char) u; if (!stralloc_append(&key,&ch)) die_nomem();
102 gettimeofday(&tv,(struct timezone *) 0);
108 char *local = (char *) 0;
109 char *host = (char *) 0;
111 void dirplusmake(slash)
114 if (!stralloc_copys(&dirplus,dir)) die_nomem();
115 if (!stralloc_cats(&dirplus,slash)) die_nomem();
116 if (!stralloc_0(&dirplus)) die_nomem();
119 void linkdotdir(dash,slash)
123 if (!stralloc_copys(&dotplus,dot)) die_nomem();
124 if (!stralloc_cats(&dotplus,dash)) die_nomem();
125 if (!stralloc_0(&dotplus)) die_nomem();
127 if (flags['e' - 'a'])
128 if (unlink(dotplus.s) == -1)
129 if (errno != error_noent)
130 strerr_die4x(111,FATAL,ERR_DELETE,dotplus.s,": ");
131 if (symlink(dirplus.s,dotplus.s) == -1)
132 strerr_die4sys(111,FATAL,ERR_CREATE,dotplus.s,": ");
140 if (mkdir(dirplus.s,0755) == -1)
141 if ((errno != error_exist) || !flags['e' - 'a'])
142 strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
147 char ssbuf[SUBSTDIO_OUTSIZE];
155 fd = open_trunc(dirplus.s);
157 strerr_die4sys(111,FATAL,ERR_CREATE,dirplus.s,": ");
159 substdio_fdbuf(&ss,write,fd,ssbuf,sizeof(ssbuf));
166 if (substdio_bput(&ss,buf,len) == -1)
167 strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
172 if (substdio_bputs(&ss,buf) == -1)
173 strerr_die4sys(111,FATAL,ERR_WRITE,dirplus.s,": ");
178 if (substdio_flush(&ss) == -1)
179 strerr_die4sys(111,FATAL,ERR_FLUSH,dirplus.s,": ");
180 if (fsync(ss.fd) == -1)
181 strerr_die4sys(111,FATAL,ERR_SYNC,dirplus.s,": ");
182 if (close(ss.fd) == -1) /* NFS stupidity */
183 strerr_die4sys(111,FATAL,ERR_CLOSE,dirplus.s,": ");
191 if (unlink(dirplus.s) == -1)
192 if (errno != error_noent)
193 strerr_die4sys(111,FATAL,ERR_DELETE,dirplus.s,": ");
211 unsigned int next,i,j;
213 unsigned int slpos,hashpos,pos;
214 int fdin,fdlock,fdtmp;
216 char *oldflags = (char *) 0;
217 char *code = (char *) 0;
218 char *cfname = (char *) 0; /* config file if spec as -C cf_file */
221 keyadd((unsigned long) getpid());
222 keyadd((unsigned long) getppid());
223 euid = (unsigned long) geteuid();
225 keyadd((unsigned long) getgid());
226 gettimeofday(&tv,(struct timezone *) 0);
230 /* flags with defined use. vV for version. Others free */
232 for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) {
235 for (pos = 0; pos < 10; popt[pos++] = (char *) 0);
237 while ((opt = getopt(argc,argv,
238 "+aAbBcC:dDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0:3:4:5:6:7:8:9:"))
240 if (opt == 'v' || opt == 'V')
241 strerr_die2x(0,"ezmlm-make version: ezmlm-0.53+",EZIDX_VERSION);
242 if (opt =='C') /* treat this like nl switch to allow override of -c*/
244 if (opt >= 'a' && opt <= 'z') {
245 flags[opt - 'a'] = 3; /* Dominant "set" */
246 if (opt == 'e') flagforce++; /* two 'e' => ignore 'E' */
247 } else if (opt >= 'A' && opt <= 'Z')
248 flags[opt - 'A'] = 2; /* Dominant "unset" */
249 else if (opt >= '0' && opt <= '9')
250 popt[opt-'0'] = optarg;
251 else if (opt == '+') {
252 flagforce_p++; /* two '+' => ignore 'E' */
253 flags['e' - 'a'] = 3; /* -+ implies -e */
260 if (flagforce_p > 1 || flagforce > 1)
265 if (!(dir = *argv++)) die_usage();
266 if (dir[0] != '/') die_relative();
267 if (dir[str_chr(dir,'\'')]) die_quote();
268 if (dir[str_chr(dir,'\n')]) die_newline();
270 if (flags['e' - 'a'] & 1) { /* lock for edit */
271 dirplusmake("/lock");
272 fdlock = open_append(dirplus.s);
274 strerr_die4sys(111,FATAL,ERR_OPEN,dirplus.s,": ");
275 if (lock_ex(fdlock) == -1)
276 strerr_die4sys(111,FATAL,ERR_OBTAIN,dirplus.s,": ");
278 /* for edit, try to get args from dir/config */
279 dirplusmake("/config");
280 if ((fdin = open_read(dirplus.s)) == -1) {
281 if (errno != error_noent) die_read();
283 substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
285 if (getln(&sstext,&line,&match,'\n') == -1) die_read();
287 if (line.s[0] == '#') continue;
288 if (line.len == 1) break;
289 if (line.s[1] != ':') break;
290 line.s[line.len - 1] = '\0';
291 if (!stralloc_cat(&cmdline,&line)) die_nomem();
295 while (pos < cmdline.len) {
299 case 'X': if (euid && !flags['c' - 'a'] && (!cfname))
300 cfname = cmdline.s + pos; /* cmdline overrides */
301 break; /* for safety: ignore if root */
302 case 'T': dot = cmdline.s + pos; break;
303 case 'L': local = cmdline.s + pos; break;
304 case 'H': host = cmdline.s + pos; break;
305 case 'C': code = cmdline.s + pos; break;
306 case 'D': break; /* no reason to check */
307 case 'F': oldflags = cmdline.s + pos; break;
309 if (ch == '0' || (ch >= '3' && ch <= '9')) {
310 if (usecfg && !popt[ch - '0'])
311 popt[ch - '0'] = cmdline.s + pos;
313 strerr_die4x(111,FATAL,dirplus.s,ERR_SYNTAX,
317 pos += str_len(cmdline.s + pos) + 1;
325 if (!local || str_diff(local,p))
326 flagforce = 1; /* must rewrite if list name changed */
329 if (!host || str_diff(host,p))
330 flagforce = 1; /* must rewrite if list name changed */
338 if (!dot || !local || !host) die_usage();
339 if (dot[0] != '/') die_relative(); /* force absolute dot */
341 /* use flags from config, overridden with new values */
342 /* if there are old flags, we're in "edit" and "-+" */
343 /* Previous versions only wrote _set_ flags to */
344 /* to DIR/confiag. We need to make sure that we */
345 /* don't apply the defaults for non-specified ones! */
346 if (usecfg && oldflags && flags['e' - 'a']) {
347 while ((ch = *(oldflags++))) {
348 if (ch >= 'a' && ch <= 'z') { /* unset flags ignored */
350 if (!flags[ch - 'a']) /* cmd line overrides */
356 if (!usecfg) { /* apply defaults */
357 while (( ch = *(defflags++))) { /* gets used up! */
358 if (ch >= 'a' && ch <= 'z') { /* defensive! */
359 if (!flags[ch - 'a']) /* cmdline still overrides */
365 for (pos = 0; pos < (unsigned int) NO_FLAGS; pos++) { /* set real flags */
366 if (flags[pos] & 2) /* 2 = "dominant" 0 */
367 flags[pos] = flags[pos] & 1; /* 3 = "dominant" 1 */
370 if (local[str_chr(local,'\n')]) die_newline();
371 if (host[str_chr(host,'\n')]) die_newline();
373 /* build 'f' for <#F#> */
374 if (!stralloc_ready(&f,28)) die_nomem();
375 if (!stralloc_copys(&f,"-")) die_nomem();
376 for (ch = 0; ch <= 'z' - 'a'; ch++) { /* build string with flags */
381 if (!stralloc_append(&f,sz)) die_nomem();
384 fdin = -1; /* assure failure for .ezmlmrc in case flags['c'-'a'] = 0 */
385 slpos = str_len(dot);
386 while ((--slpos > 0) && dot[slpos] != '/');
387 if (dot[slpos] == '/') {
388 if (!stralloc_copyb(&template,dot,slpos+1)) die_nomem(); /* dot dir */
389 slpos += str_chr(dot+slpos,'-');
392 pos = slpos + str_chr(dot+slpos,'-');
394 if (!stralloc_copyb(&ext1,dot+slpos,pos-slpos)) die_nomem();
396 slpos = pos + str_chr(dot+pos,'-');
398 if (!stralloc_copyb(&ext2,dot+pos,slpos-pos)) die_nomem();
402 if (!stralloc_0(&ext1)) die_nomem();
403 if (!stralloc_0(&ext2)) die_nomem();
406 /* if 'c', template already has the dot directory. If 'C', cfname */
407 /* (if exists and != '') points to the file name to use instead. */
408 if (flags['c'-'a'] || (cfname && *cfname)) {
409 if (!flags['c'-'a']) { /* i.e. there is a cfname specified */
410 if (!stralloc_copys(&template,cfname)) die_nomem();
412 if (!stralloc_cats(&template,TXT_DOTEZMLMRC)) die_nomem();
413 if (!stralloc_0(&template)) die_nomem();
414 if ((fdin = open_read(template.s)) == -1)
415 if (errno != error_noent)
416 strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
418 strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
419 } else { /* /etc/ezmlmrc */
420 if (!stralloc_copys(&template,TXT_ETC_EZMLMRC)) die_nomem();
421 if (!stralloc_0(&template)) die_nomem();
422 if ((fdin = open_read(template.s)) == -1)
423 if (errno != error_noent)
424 strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
425 else { /* ezbin/ezmlmrc */
426 if (!stralloc_copys(&template,"/usr/share/ezmlm")) die_nomem();
427 if (!stralloc_cats(&template,TXT_EZMLMRC)) die_nomem();
428 if (!stralloc_0(&template)) die_nomem();
429 if ((fdin = open_read(template.s)) == -1)
430 if (errno != error_noent)
431 strerr_die4sys(111,FATAL,ERR_OPEN,template.s,": ");
433 strerr_die3x(100,FATAL,template.s,ERR_NOEXIST);
437 dcreate(""); /* This is all we do, the rest is up to ezmlmrc */
438 /* do it after opening template to avoid aborts */
439 /* with created DIR. Well we also write DIR/key */
440 /* at the end except in -e[dit] mode. */
442 substdio_fdbuf(&sstext,read,fdin,textbuf,sizeof(textbuf));
443 if (!stralloc_0(&oldfname)) die_nomem(); /* init oldfname */
446 if (getln(&sstext,&line,&match,'\n') == -1)
447 strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
449 strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
450 i = str_rchr(EZIDX_VERSION,'-'); /* check version */
451 if (EZIDX_VERSION[i]) i++;
453 while (line.s[j] == EZIDX_VERSION[i] && j < line.len &&
454 EZIDX_VERSION[i] != '.' && EZIDX_VERSION[i]) {
455 i++; j++; /* major */
457 if (EZIDX_VERSION[i] != '.' || j + 1 >= line.len ||
458 EZIDX_VERSION[i+1] != line.s[j+1])
459 strerr_warn2(WARNING,ERR_VERSION, (struct strerr *) 0);
462 if (getln(&sstext,&line,&match,'\n') == -1)
463 strerr_die4sys(111,FATAL,ERR_READ,template.s,": ");
466 if (line.s[0] == '#') /* comment */
468 if (!stralloc_0(&line)) die_nomem();
469 if (line.s[0] == '<' && line.s[1] == '/') { /* tag */
470 if (line.s[str_chr(line.s,'.')])
471 strerr_die3x(100,FATAL,ERR_PERIOD,line.s);
475 pos = str_chr(line.s+2,'#')+2;
480 while ((ch = line.s[pos]) &&
481 (line.s[pos] != '/' && line.s[pos+1] != '>')) {
487 /* E is ignored. For files => create unless exists */
488 if (ch == 'E' && !flagnot || ch == 'e' && flagnot) {
489 if (flags['e' - 'a'] && !flagforce)
490 flagover = 1; /* ignore #E & #^e, but set flagover */
491 } else if (ch >= 'a' && ch <= 'z')
492 flagdo &= (flags[ch - 'a'] ^ flagnot);
493 else if (ch >= 'A' && ch <= 'Z')
494 flagdo &= !(flags[ch - 'A'] ^ flagnot);
495 else if (ch >= '0' && ch <= '9')
496 flagdo &= (popt[ch - '0'] && *popt[ch - '0']) ^flagnot;
500 if (line.s[pos] != '/' || line.s[pos+1] != '>')
501 strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
504 pos = 2; /* name needs to be >= 1 char */
505 while (line.s[pos = str_chr(line.s+pos,'/')+pos]) {
506 if (line.s[pos+1] == '>')
511 strerr_die3x(100,FATAL,ERR_ENDTAG,line.s);
514 pos = hashpos; /* points to after file name */
516 if (line.s[2] == '+') { /* mkdir */
519 if (!stralloc_copys(&dname,"/")) die_nomem();
520 if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
521 if (!stralloc_0(&dname)) die_nomem();
525 } else if (line.s[2] == ':') { /* ln -s */
528 slpos = str_chr(line.s + 3,'/') + 3;
530 strerr_die3x(100,FATAL,ERR_LINKDIR,line.s);
531 if (!stralloc_copyb(&dname,line.s+slpos,pos-slpos)) die_nomem();
532 if (!stralloc_copyb(&lname,line.s+3,slpos-3)) die_nomem();
533 if (!stralloc_0(&dname)) die_nomem();
534 if (!stralloc_0(&lname)) die_nomem();
535 linkdotdir(lname.s,dname.s);
538 } else if (line.s[2] == '-') { /* rm */
541 if (!stralloc_copys(&dname,"/")) die_nomem();
542 if (!stralloc_catb(&dname,line.s+3,pos-3)) die_nomem();
543 if (!stralloc_0(&dname)) die_nomem();
548 /* only plain files left */
549 /* first get file name */
550 if (pos > 2) { /* </#ai/> => add to open file */
551 if (!stralloc_copyb(&fname,line.s+1,pos-1)) die_nomem();
552 if (!stralloc_0(&fname)) die_nomem();
555 if (str_diff(fname.s, oldfname.s)) {
557 /* Treat special case of #E when editing which _should*/
558 /* write only if the file does not exist. flagover */
559 /* is set if we need to check */
560 if (flagover) { /* skip if exists */
561 dirplusmake(fname.s); /* decided by FIRST tag for file */
562 fdtmp = open_read(dirplus.s);
564 if (errno != error_noent)
565 strerr_die3sys(111,ERR_OPEN,dirplus.s,": ");
567 flagnotexist = 0; /* already there - don't do it */
571 if (oldfname.len > 1) {
573 if (!stralloc_copys(&oldfname,"")) die_nomem();
574 if (!stralloc_0(&oldfname)) die_nomem();
576 if (flagdo && flagnotexist) {
578 strerr_die3x(100,FATAL,ERR_FILENAME,line.s);
580 if (!stralloc_copy(&oldfname,&fname)) die_nomem();
583 if (flagdo) flagdo = flagnotexist;
586 continue; /* part not to go out */
591 pos = next + str_chr(line.s+next,'<');
593 line.s[pos+1] == '#' &&
595 line.s[pos+3] == '#' &&
596 line.s[pos+4] == '>') { /* host/local */
597 if (!stralloc_catb(&outline,line.s+last+1,pos-last-1))
599 switch (line.s[pos+2]) {
600 case 'B': /* path to ezmlm binaries (no trailing /) */
601 if (!stralloc_cats(&outline,auto_bin)) die_nomem();
602 last = pos + 4; next = pos + 5; break;
603 case 'C': /* digestcode */
605 if (!stralloc_cats(&outline,code)) die_nomem();
606 last = pos + 4; next = pos + 5; break;
607 case 'D': /* listdir */
608 if (!stralloc_cats(&outline,dir)) die_nomem();
609 last = pos + 4; next = pos + 5; break;
610 case 'F': /* flags */
611 if (!stralloc_cat(&outline,&f)) die_nomem();
612 last = pos + 4; next = pos + 5; break;
613 case 'H': /* hostname */
614 if (!stralloc_cats(&outline,host)) die_nomem();
615 last = pos + 4; next = pos + 5; break;
616 case 'L': /* local */
617 if (!stralloc_cats(&outline,local)) die_nomem();
618 last = pos + 4; next = pos + 5; break;
620 if (!stralloc_cats(&outline,dot)) die_nomem();
621 last = pos + 4; next = pos + 5; break;
622 case 'X': /* config file name */
624 if (!stralloc_cats(&outline,cfname)) die_nomem();
625 last = pos + 4; next = pos + 5; break;
626 default: /* copy unknown tag as is for e.g. <#A#> and*/
627 /* <#R#> to be processed by -manage/store */
628 /* stuff in args for <#0#> .. <#9#> */
629 if ((line.s[pos+2] >= '0') && (line.s[pos+2] <= '9')) {
630 if (popt[line.s[pos+2] - '0'])
631 if (!stralloc_cats(&outline,popt[line.s[pos+2]-'0']))
634 if (!stralloc_catb(&outline,line.s+pos,5)) die_nomem();
635 last = pos + 4; next = pos + 5; break;
637 } else { /* not tag */
641 if (!stralloc_catb(&outline,line.s+last+1,line.len-last-1))
651 if (oldfname.len > 1)
654 if (!flags['e' - 'a']) { /* don't redo key when editing a list */
656 f_put(key.s,key.len);