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,auto_bin
)) 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
);