1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <unistd.h>
4 #include <time.h>
5 #include <sys/time.h>
6 #include "sig.h"
7 #include "qconfirm.h"
8 #include "qconfirm_conf_get.h"
9 #include "qconfirm_inject.h"
10 #include "qconfirm_key.h"
11 #include "next_paragraph.h"
12 #include "getline.h"
13 #include "strerr.h"
14 #include "error.h"
15 #include "buffer.h"
16 #include "env.h"
17 #include "str.h"
18 #include "fd.h"
19 #include "pathexec.h"
20 #include "wait.h"
21 #include "stralloc.h"
22 #include "byte.h"
23 #include "open.h"
24 #include "fmt.h"
25 #include "scan.h"
26 #include "sgetopt.h"
27
28 #define USAGE " [-v] [-t sec] [-d dir ] "
29 #define VERSION "$Id: qconfirm-control.c,v 1.10 2003/11/21 13:45:53 pape Exp $"
30 #define FATAL "qconfirm-control: fatal: "
31 #define WARNING "qconfirm-control: warning: "
32 #define INFO "qconfirm-control: info: "
33
34 #define QCONTROLQUOTE "> "
35
36 const char *progname;
37
38 char *qconfirm_dir;
39 char *qcontrol_owner;
40 char *qconfirm_prepend;
41 char *qcontrol_quote;
42 char *mailname;
43 char *dflt;
44 char *local;
45 char *host;
46 char *sender;
47
48 int verbose =0;
49 unsigned long timeout =300;
50
51 char *key;
52 stralloc sa ={0};
53 stralloc command ={0};
54 struct stat st;
55 buffer b;
56 char b_space[BUFFER_OUTSIZE];
57 int fd;
58 int pid;
59 int wstat;
60 char inode[FMT_ULONG];
61
usage()62 void usage() { strerr_die4x(111, "usage: ", progname, USAGE, "\n"); }
die_nomem()63 void die_nomem() { strerr_die2x(111, FATAL, "out of memory."); }
fatal(char * m1,char * m2)64 void fatal(char *m1, char *m2) { strerr_die4sys(111, FATAL, m1, m2, ": "); }
warn(char * m1,char * m2)65 void warn(char *m1, char *m2) { strerr_warn3(WARNING, m1, m2, 0); }
info(char * m1,char * m2)66 void info(char *m1, char *m2) { strerr_die3x(0, INFO, m1, m2); }
defer(char * m1,char * m2)67 void defer(char *m1, char *m2) { strerr_die3x(111, INFO, m1, m2); }
bounce(char * m)68 void bounce(char *m) { strerr_die1x(100, m); }
69
create_ctrl()70 char* create_ctrl() {
71 if (fstat(0, &st) == -1) fatal("unable to fstat stdin", 0);
72 inode[fmt_ulong(inode, st.st_ino)] = 0;
73 if (! inode) fatal("bad inode", 0);
74
75 do {
76 if (! stralloc_copyb(&sa, (char *) &st.st_ino, 1)) die_nomem();
77 if (! stralloc_cats(&sa, sender)) die_nomem();
78 if (! (key =qconfirm_key(&sa))) die_nomem();
79 if (! stralloc_copys(&sa, qconfirm_dir)) die_nomem();
80 if (! stralloc_cats(&sa, "/control/")) die_nomem();
81 if (! stralloc_cats(&sa, key)) die_nomem();
82 if (! stralloc_0(&sa)) die_nomem();
83 } while (stat(sa.s, &st) != -1);
84 if (errno != error_noent) fatal("unable to stat: ", sa.s);
85 if (open_trunc(sa.s) == -1) fatal("unable to create: ", sa.s);
86 if (! stralloc_copys(&sa, qconfirm_dir)) die_nomem();
87 if (! stralloc_cats(&sa, "/control/")) die_nomem();
88 if (! stralloc_cats(&sa, inode)) die_nomem();
89 if (! stralloc_0(&sa)) die_nomem();
90 if (symlink(key, sa.s) == -1)
91 fatal("unable to create symbolic link: ", sa.s);
92 return(key);
93 }
94
check_ctrl()95 int check_ctrl() {
96 int stamp;
97 time_t now;
98 char buf[33];
99
100 if (fstat(0, &st) == -1) fatal("unable to fstat stdin", 0);
101 inode[fmt_ulong(inode, st.st_ino)] = 0;
102 if (! inode) fatal("bad inode", 0);
103 stamp =st.st_mtime;
104
105 if (! stralloc_copys(&sa, qconfirm_dir)) die_nomem();
106 if (! stralloc_cats(&sa, "/control/")) die_nomem();
107 if (! stralloc_cats(&sa, inode)) die_nomem();
108 if (! stralloc_0(&sa)) die_nomem();
109 if (stat(sa.s, &st) == -1) {
110 if (errno != error_noent) fatal("unable to stat: ", sa.s);
111 return(1);
112 }
113 /* check timeout and mode */
114 now =time(NULL);
115 if (((now -stamp) > timeout) || ((st.st_mode & S_IRWXU) == S_IWUSR)) {
116 /* remove */
117 if (readlink(sa.s, buf, 32) != 32)
118 fatal("unable to read key from inode: ", inode);
119 buf[32] =0;
120 if (unlink(sa.s) == -1) warn("unable to unlink: ", sa.s);
121 if (! stralloc_copys(&sa, qconfirm_dir)) die_nomem();
122 if (! stralloc_cats(&sa, "/control/")) die_nomem();
123 if (! stralloc_catb(&sa, buf, 32)) die_nomem();
124 if (! stralloc_0(&sa)) die_nomem();
125 if (unlink(sa.s) == -1) warn("unable to unlink: ", sa.s);
126 info("remove: ", buf);
127 }
128 defer("active: ", inode);
129 return(-1);
130 }
131
check_key()132 int check_key() {
133 if (! stralloc_copys(&sa, qconfirm_dir)) die_nomem();
134 if (! stralloc_cats(&sa, "/control/")) die_nomem();
135 if (! stralloc_cats(&sa, dflt)) die_nomem();
136 if (! stralloc_0(&sa)) die_nomem();
137 if (stat(sa.s, &st) == -1) bounce("not authorized.");
138 if ((st.st_mode & S_IRWXU) == S_IWUSR) bounce("not authorized.");
139 return(1);
140 }
141
run_qconfirm(char * arg,char * addresses,buffer * out)142 int run_qconfirm(char *arg, char *addresses, buffer *out) {
143 char *prog[1000];
144 int progc;
145 int i;
146
147 prog[0] ="qconfirm";
148 prog[1] =arg;
149 prog[2] =0;
150 progc =2;
151
152 if (addresses) {
153 i =0;
154 for (;;) {
155 while ((addresses[i] == ' ') || (addresses[i] == '\t')) ++i;
156 if (! addresses[i]) break;
157 prog[progc++] =&addresses[i];
158 if (progc == 999) {
159 warn("too many addresses, truncate.", 0);
160 break;
161 }
162 while (addresses[i] && (addresses[i] != ' ') && (addresses[i] != '\t'))
163 ++i;
164 if (! addresses[i]) break;
165 addresses[i++] =0;
166 }
167 prog[progc] =0;
168 }
169 if (verbose) {
170 buffer_puts(out, " ");
171 for (i =0; i < progc; ++i) {
172 buffer_puts(out, " ");
173 buffer_puts(out, prog[i]);
174 }
175 buffer_puts(out, "\n");
176 }
177 buffer_flush(out);
178
179 if ((pid =fork()) == -1) fatal("unable to fork", 0);
180 if (! pid) {
181 /* child */
182 if (fd_move(1, out->fd) == -1) fatal("unable to set filedescriptor", 0);
183 if (fd_copy(2, 1) == -1) fatal("unable to set filedescriptor", 0);
184 pathexec((const char **)prog);
185 fatal("unable to run: ", prog[0]);
186 }
187 if (wait_pid(&wstat, pid) == -1) fatal(prog[0], ": wait failed");
188 if (wait_exitcode(wstat) != 0) warn(prog[0], ": exited abnormally.");
189 buffer_putsflush(out, "\n");
190 return(1);
191 }
192
qconfirm_list(char * arg,char * extension)193 int qconfirm_list(char *arg, char *extension) {
194 if (open_qmail_inject(&fd, qcontrol_owner) == -1)
195 fatal("unable to start qmail-inject", 0);
196 buffer_init(&b, buffer_unixwrite, fd, b_space, sizeof b_space);
197 buffer_puts(&b, "Return-Path: <>\n");
198 buffer_puts(&b, "Subject: qconfirm list\n");
199 buffer_puts(&b, "From: \"");
200 buffer_puts(&b, mailname);
201 buffer_puts(&b, "\" <");
202 buffer_puts(&b, local);
203 if (extension) buffer_puts(&b, extension);
204 buffer_puts(&b, "@");
205 buffer_puts(&b, host);
206 buffer_puts(&b, ">\nTo: ");
207 buffer_puts(&b, qcontrol_owner);
208 buffer_puts(&b, "\n\n");
209
210 if (! stralloc_copys(&sa, "qconfirm list ")) die_nomem();
211 if (! stralloc_cats(&sa, arg)) die_nomem();
212 if (! stralloc_cats(&sa, " | sort -r")) die_nomem();
213 if (! stralloc_0(&sa)) die_nomem();
214 if (verbose) {
215 buffer_puts(&b, " ");
216 buffer_puts(&b, sa.s);
217 buffer_puts(&b, "\n");
218 }
219 buffer_flush(&b);
220 if ((pid =fork()) == -1) fatal("unable to fork", 0);
221 if (! pid) {
222 /* child */
223 const char *prog[4];
224
225 if (fd_move(1, b.fd) == -1) fatal("unable to set filedescriptor", 0);
226 if (fd_copy(2, 1) == -1) fatal("unable to set filedescriptor", 0);
227 prog[0] ="sh";
228 prog[1] ="-c";
229 prog[2] =sa.s;
230 prog[3] =0;
231 pathexec(prog);
232 fatal("unable to run: ", sa.s);
233 }
234 if (wait_pid(&wstat, pid) == -1) fatal(sa.s, ": wait failed");
235 if (wait_exitcode(wstat) != 0) warn(sa.s, ": exited abnormally.");
236 buffer_putsflush(&b, "\n");
237 return(1);
238 }
239
process()240 int process() {
241 int r;
242 stralloc args ={0};
243
244 if (open_qmail_inject(&fd, qcontrol_owner) == -1)
245 fatal("unable to start qmail-inject", 0);
246 buffer_init(&b, buffer_unixwrite, fd, b_space, sizeof b_space);
247 buffer_puts(&b, "Return-Path: <>\n");
248 buffer_puts(&b, "Subject: qconfirm control\n");
249 buffer_puts(&b, "From: \"");
250 buffer_puts(&b, mailname);
251 buffer_puts(&b, "\" <");
252 buffer_puts(&b, local);
253 buffer_puts(&b, "@");
254 buffer_puts(&b, host);
255 buffer_puts(&b, ">\nTo: ");
256 buffer_puts(&b, qcontrol_owner);
257 buffer_putsflush(&b, "\n\n");
258
259 /* process message */
260 /* skip header */
261 if (next_paragraph(buffer_0) == -1) strerr_die2x(0, WARNING, "no body.");
262 if (! stralloc_copys(&args, "")) die_nomem();
263
264 /* lines */
265 while ((r =getline(buffer_0, &sa)) > 0) {
266 if (sa.s[0] == '\n') continue;
267 if ((sa.len > 2) && str_start(sa.s, qcontrol_quote)) {
268 int i, j;
269
270 /* quoted qconfirm list item */
271 buffer_puts(&b, qcontrol_quote);
272 buffer_put(&b, sa.s, sa.len);
273 if ((sa.s[2] < '1') || (sa.s[2] > '9')) continue;
274 if ((i =byte_chr(&sa.s[2], sa.len -2, ' ')) == sa.len -2) {
275 if (verbose) buffer_putsflush(&b, "parse error.\n");
276 continue;
277 }
278 i +=3;
279 for (j =i; sa.s[j]; ++j) {
280 if ((sa.s[j] == ' ') || (sa.s[j] == '\n')) break;
281 }
282 if (j > i) {
283 if (! stralloc_catb(&args, &sa.s[i], j -i)) die_nomem();
284 if (! stralloc_cats(&args, " ")) die_nomem();
285 if (verbose) {
286 buffer_puts(&b, " ");
287 buffer_put(&b, &sa.s[i], j -i);
288 buffer_puts(&b, "\n");
289 }
290 }
291 buffer_flush(&b);
292 continue;
293 }
294 if ((sa.len >= 6) && str_start(sa.s, "accept")) {
295 if ((sa.s[6] == ' ') && sa.s[7]) {
296 if (! stralloc_catb(&args, &sa.s[7], sa.len -7)) die_nomem();
297 if (args.s[args.len -1] == '\n')
298 args.s[args.len -1] =0;
299 }
300 if (! stralloc_0(&args)) die_nomem();
301 buffer_puts(&b, qcontrol_quote);
302 buffer_putflush(&b, sa.s, sa.len);
303 run_qconfirm("accept", args.s, &b);
304 if (! stralloc_copys(&args, "")) die_nomem();
305 continue;
306 }
307 if ((sa.len >= 4) && str_start(sa.s, "drop")) {
308 if ((sa.s[4] == ' ') && sa.s[5]) {
309 if (! stralloc_catb(&args, &sa.s[5], sa.len -5)) die_nomem();
310 if (args.s[args.len -1] == '\n')
311 args.s[args.len -1] =0;
312 }
313 if (! stralloc_0(&args)) die_nomem();
314 buffer_puts(&b, qcontrol_quote);
315 buffer_putflush(&b, sa.s, sa.len);
316 run_qconfirm("drop", args.s, &b);
317 if (! stralloc_copys(&args, "")) die_nomem();
318 continue;
319 }
320 if ((sa.len >= 6) && str_start(sa.s, "bounce")) {
321 if ((sa.s[6] == ' ') && sa.s[7]) {
322 if (! stralloc_catb(&args, &sa.s[7], sa.len -7)) die_nomem();
323 if (args.s[args.len -1] == '\n')
324 args.s[args.len -1] =0;
325 }
326 if (! stralloc_0(&args)) die_nomem();
327 buffer_puts(&b, qcontrol_quote);
328 buffer_putflush(&b, sa.s, sa.len);
329 run_qconfirm("bounce", args.s, &b);
330 if (! stralloc_copys(&args, "")) die_nomem();
331 continue;
332 }
333 if ((sa.len >= 3) && str_start(sa.s, "bad")) {
334 if ((sa.s[3] == ' ') && sa.s[4]) {
335 if (! stralloc_catb(&args, &sa.s[4], sa.len -4)) die_nomem();
336 if (args.s[args.len -1] == '\n')
337 args.s[args.len -1] =0;
338 }
339 if (! stralloc_0(&args)) die_nomem();
340 buffer_puts(&b, qcontrol_quote);
341 buffer_putflush(&b, sa.s, sa.len);
342 run_qconfirm("bad", args.s, &b);
343 if (! stralloc_copys(&args, "")) die_nomem();
344 continue;
345 }
346 if ((sa.len >= 7) && str_start(sa.s, "pending")) {
347 if ((sa.s[7] == ' ') && sa.s[8]) {
348 if (! stralloc_catb(&args, &sa.s[8], sa.len -8)) die_nomem();
349 if (args.s[args.len -1] == '\n')
350 args.s[args.len -1] =0;
351 }
352 if (! stralloc_0(&args)) die_nomem();
353 buffer_puts(&b, qcontrol_quote);
354 buffer_putflush(&b, sa.s, sa.len);
355 run_qconfirm("pending", args.s, &b);
356 if (! stralloc_copys(&args, "")) die_nomem();
357 continue;
358 }
359 if ((sa.len >= 6) && str_start(sa.s, "remove")) {
360 if ((sa.s[6] == ' ') && sa.s[7]) {
361 if (! stralloc_catb(&args, &sa.s[7], sa.len -7)) die_nomem();
362 if (args.s[args.len -1] == '\n')
363 args.s[args.len -1] =0;
364 }
365 if (! stralloc_0(&args)) die_nomem();
366 buffer_puts(&b, qcontrol_quote);
367 buffer_putflush(&b, sa.s, sa.len);
368 run_qconfirm("remove", args.s, &b);
369 if (! stralloc_copys(&args, "")) die_nomem();
370 continue;
371 }
372 if ((sa.len >= 5) && str_start(sa.s, "sneak")) {
373 if ((sa.s[5] == ' ') && sa.s[6]) {
374 if (! stralloc_catb(&args, &sa.s[6], sa.len -6)) die_nomem();
375 if (args.s[args.len -1] == '\n')
376 args.s[args.len -1] =0;
377 }
378 if (! stralloc_0(&args)) die_nomem();
379 buffer_puts(&b, qcontrol_quote);
380 buffer_putflush(&b, sa.s, sa.len);
381 run_qconfirm("sneak", args.s, &b);
382 if (! stralloc_copys(&args, "")) die_nomem();
383 continue;
384 }
385 if ((sa.len >= 4) && str_start(sa.s, "list")) {
386 if ((sa.s[4] == ' ') && sa.s[5]) {
387 if (! stralloc_copyb(&args, &sa.s[5], sa.len -5)) die_nomem();
388 if (args.s[args.len -1] == '\n')
389 args.s[args.len -1] =0;
390 }
391 if (! stralloc_0(&args)) die_nomem();
392 buffer_puts(&b, qcontrol_quote);
393 buffer_putflush(&b, sa.s, sa.len);
394 run_qconfirm("list", args.s, &b);
395 if (! stralloc_copys(&args, "")) die_nomem();
396 continue;
397 }
398 if ((sa.len >= 6) && str_start(sa.s, "thanks")) {
399 buffer_puts(&b, qcontrol_quote);
400 buffer_put(&b, sa.s, sa.len);
401 /* remove this key */
402 if (! stralloc_copys(&sa, qconfirm_dir)) die_nomem();
403 if (! stralloc_cats(&sa, "/control/")) die_nomem();
404 if (! stralloc_cats(&sa, dflt)) die_nomem();
405 if (! stralloc_0(&sa)) die_nomem();
406 if (chmod(sa.s, S_IWUSR) == -1)
407 fatal("unable to chmod: ", sa.s);
408 buffer_puts(&b, "Removing temporary authorization.\n");
409 break;
410 }
411 if ((sa.len >= 4) && str_start(sa.s, "stop")) {
412 buffer_puts(&b, qcontrol_quote);
413 buffer_put(&b, sa.s, sa.len);
414 break;
415 }
416 buffer_puts(&b, qcontrol_quote);
417 buffer_put(&b, sa.s, sa.len);
418 buffer_putsflush(&b, "unknown command.\n\n");
419 }
420 buffer_putsflush(&b, "Stopping processing here.\n");
421 close(fd);
422 if (! close_qmail_inject()) fatal("unable to run qmail-inject", 0);
423 return(1);
424 }
425
main(int argc,const char ** argv)426 int main(int argc, const char **argv) {
427 int i;
428 int opt;
429
430 progname =*argv;
431 umask(0177);
432 sig_ignore(sig_pipe);
433
434 qconfirm_dir =env_get("QCONFIRM_DIR");
435
436 while ((opt =getopt(argc, argv, "vVt:d:")) != opteof) {
437 switch(opt) {
438 case 'v': verbose =1; break;
439 case 't': scan_ulong(optarg, &timeout); break;
440 case 'd': qconfirm_dir =(char *)optarg; break;
441 case 'V': strerr_warn1(VERSION, 0);
442 case '?': usage();
443 }
444 }
445 argv +=optind;
446
447 sender =env_get("SENDER");
448 if (! sender)
449 strerr_die2x(100, FATAL, "environment variable SENDER not set.\n");
450 dflt =env_get("DEFAULT");
451 if (! dflt)
452 strerr_die2x(100, FATAL, "environment variable DEFAULT not set.\n");
453 local =env_get("LOCAL");
454 if (! local)
455 strerr_die2x(100, FATAL, "environment variable LOCAL not set.\n");
456 host =env_get("HOST");
457 if (! host)
458 strerr_die2x(100, FATAL, "environment variable HOST not set.\n");
459
460 if (! qconfirm_dir) qconfirm_dir =QCONFIRMDIR;
461 qconfirm_prepend =conf_get_dflt(qconfirm_dir, "QCONFIRM_PREPEND", "");
462 if (! qconfirm_prepend)
463 fatal("unable to read config: ", "QCONFIRM_PREPEND");
464 qcontrol_quote =conf_get_dflt(qconfirm_dir, "QCONTROL_QUOTE", QCONTROLQUOTE);
465 if (! qcontrol_quote)
466 fatal("unable to read config: ", "QCONTROL_QUOTE");
467 qcontrol_owner =conf_get(qconfirm_dir, "QCONTROL_OWNER");
468 if (! qcontrol_owner)
469 fatal("unable to read config: ", "QCONTROL_OWNER");
470 if (! *qcontrol_owner)
471 strerr_die2x(100, FATAL, "QCONTROL_OWNER is empty.\n");
472 mailname =conf_get_dflt(qconfirm_dir, "QCONFIRM_MAILNAME", QCONFIRMMAILNAME);
473 if (! mailname)
474 fatal("unable to read config: ", "QCONFIRM_MAILNAME");
475
476 if (! pathexec_env("QCONFIRM_DIR", qconfirm_dir)) die_nomem();
477
478 i =str_len(qconfirm_prepend);
479 if (byte_equal(qconfirm_prepend, i, local)) local +=i;
480
481 i =str_len(dflt);
482 switch(i) {
483 case 2: /* ok */
484 if (str_equal("ok", dflt)) {
485 check_ctrl();
486 key =create_ctrl();
487 local[str_len(local) -i] =0;
488 qconfirm_list("ok", key);
489 defer("create: ", inode);
490 }
491 break;
492 case 3: /* bad */
493 if (str_equal("bad", dflt)) {
494 check_ctrl();
495 key =create_ctrl();
496 local[str_len(local) -i] =0;
497 qconfirm_list("bad", key);
498 defer("create: ", inode);
499 }
500 break;
501 case 6:
502 if (str_equal("return", dflt)) {
503 check_ctrl();
504 key =create_ctrl();
505 local[str_len(local) -i] =0;
506 qconfirm_list("return", key);
507 defer("create: ", inode);
508 }
509 break;
510 case 0: case 7: /* create control and list pending */
511 if (! *dflt || str_equal("pending", dflt)) {
512 check_ctrl();
513 key =create_ctrl();
514 local[str_len(local) -i] =0;
515 qconfirm_list("", key);
516 defer("create: ", inode);
517 }
518 break;
519 case 32: /* key */
520 check_key();
521 process();
522 info("processed", 0);
523 break;
524 }
525 bounce("Sorry, no mailbox here by that name.");
526 _exit(0);
527 }
528