chiark / gitweb /
Merge branches 'idx/verh' and 'idx/qmqpc'
[qmail] / qsmhook.c
1 #include "fd.h"
2 #include "stralloc.h"
3 #include "readwrite.h"
4 #include "sgetopt.h"
5 #include "wait.h"
6 #include "env.h"
7 #include "byte.h"
8 #include "str.h"
9 #include "alloc.h"
10 #include "exit.h"
11 #include "fork.h"
12 #include "case.h"
13 #include "subfd.h"
14 #include "error.h"
15 #include "substdio.h"
16 #include "sig.h"
17
18 void die(e,s) int e; char *s; { substdio_putsflush(subfderr,s); _exit(e); }
19 void die_usage() { die(100,"qsmhook: fatal: incorrect usage\n"); }
20 void die_temp() { die(111,"qsmhook: fatal: temporary problem\n"); }
21 void die_read() { die(111,"qsmhook: fatal: unable to read message\n"); }
22 void die_badcmd() { die(100,"qsmhook: fatal: command not found\n"); }
23
24 int flagrpline = 0; char *rpline;
25 int flagufline = 1; char *ufline;
26 int flagdtline = 0; char *dtline;
27 char *host;
28 char *sender;
29 char *recip;
30
31 stralloc newarg = {0};
32
33 substdio ssout;
34 char outbuf[SUBSTDIO_OUTSIZE];
35 substdio ssin;
36 char inbuf[SUBSTDIO_INSIZE];
37
38 void main(argc,argv)
39 int argc;
40 char **argv;
41 {
42  int pid;
43  int wstat;
44  int pi[2];
45  int opt;
46  char **arg;
47  char *x;
48  int i;
49  int flagesc;
50
51  sig_pipeignore();
52
53  if (!(dtline = env_get("DTLINE"))) die_usage();
54  if (!(rpline = env_get("RPLINE"))) die_usage();
55  if (!(ufline = env_get("UFLINE"))) die_usage();
56  if (!(recip = env_get("LOCAL"))) die_usage();
57  if (!(host = env_get("HOST"))) die_usage();
58  if (!(sender = env_get("SENDER"))) die_usage();
59
60  while ((opt = getopt(argc,argv,"DFlMmnPsx:")) != opteof)
61    switch(opt)
62     {
63      case 'D': case 'F': case 'M': break; /* be serious */
64      case 'l': flagdtline = 1; break; /* also return-receipt-to? blech */
65      case 'm': break; /* we only handle one recipient anyway */
66      case 'n': flagufline = 0; break;
67      case 's': break; /* could call quote() otherwise, i suppose... */
68      case 'P': flagrpline = 1; break;
69      case 'x':
70        if (case_starts(recip,optarg))
71          recip += str_len(optarg);
72        break;
73      default:
74        _exit(100);
75     }
76  argc -= optind;
77  argv += optind;
78
79  if (!*argv) die_usage();
80
81  for (arg = argv;x = *arg;++arg)
82   {
83    if (!stralloc_copys(&newarg,"")) die_temp();
84    flagesc = 0;
85    for (i = 0;x[i];++i)
86      if (flagesc)
87       {
88        switch(x[i])
89         {
90          case '%': if (!stralloc_cats(&newarg,"%")) die_temp(); break;
91          case 'g': if (!stralloc_cats(&newarg,sender)) die_temp(); break;
92          case 'h': if (!stralloc_cats(&newarg,host)) die_temp(); break;
93          case 'u': if (!stralloc_cats(&newarg,recip)) die_temp(); break;
94         }
95        flagesc = 0;
96       }
97      else
98        if (x[i] == '%')
99          flagesc = 1;
100        else
101          if (!stralloc_append(&newarg,&x[i])) die_temp();
102    if (!stralloc_0(&newarg)) die_temp();
103    i = str_len(newarg.s) + 1;
104    if (!(x = alloc(i))) die_temp();
105    byte_copy(x,i,newarg.s);
106    *arg = x;
107   }
108
109  if (pipe(pi) == -1) die_temp();
110
111  switch(pid = fork())
112   {
113    case -1:
114      die_temp();
115    case 0:
116      close(pi[1]);
117      if (fd_move(0,pi[0]) == -1) die_temp();
118      sig_pipedefault();
119      execvp(*argv,argv);
120      if (error_temp(errno)) die_temp();
121      die_badcmd();
122   }
123  close(pi[0]);
124
125  substdio_fdbuf(&ssout,write,pi[1],outbuf,sizeof(outbuf));
126  substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
127  if (flagufline) substdio_bputs(&ssout,ufline);
128  if (flagrpline) substdio_bputs(&ssout,rpline);
129  if (flagdtline) substdio_bputs(&ssout,dtline);
130  if (substdio_copy(&ssout,&ssin) == -2) die_read();
131  substdio_flush(&ssout);
132  close(pi[1]);
133
134  if (wait_pid(&wstat,pid) == -1) die_temp();
135  if (wait_crashed(wstat)) die_temp();
136  _exit(wait_exitcode(wstat));
137 }