1 #include <sys/types.h>
2 #include <sys/stat.h>
3 #include <unistd.h>
4 #include "sgetopt.h"
5 #include "strerr.h"
6 #include "error.h"
7 #include "stralloc.h"
8 #include "buffer.h"
9 #include "sig.h"
10 #include "env.h"
11 #include "open.h"
12 #include "byte.h"
13 #include "scan.h"
14 #include "str.h"
15 #include "openreadclose.h"
16 #include "qconfirm.h"
17 #include "qconfirm_id.h"
18 #include "next_paragraph.h"
19 #include "getline.h"
20
21 #define USAGE " [-n limit ] [dir]"
22 #define VERSION "$Id: qconfirm-return.c,v 1.11 2003/11/21 13:45:53 pape Exp $"
23 #define FATAL "qconfirm-return: fatal: "
24 #define WARNING "qconfirm-return: warning: "
25 #define INFO "qconfirm-return: info: "
26
27 const char *progname;
28 char *sender;
29 const char *qconfirm_dir;
30 stralloc sa ={0};
31 stralloc pending ={0};
32 stralloc id ={0};
33 stralloc k ={0};
34 struct stat s;
35 unsigned long limit =0;
36
usage()37 void usage() { strerr_die4x(111, "usage: ", progname, USAGE, "\n"); }
die_nomem()38 void die_nomem() { strerr_die2x(111, FATAL, "out of memory."); }
die_noqsbmf()39 void die_noqsbmf() { strerr_die2x(0, WARNING, "no QSBMF."); }
die_badheader()40 void die_badheader() {
41 strerr_die2x(0, WARNING, "bad header in original message.");
42 }
info(char * m0,char * m1)43 void info(char *m0, char *m1) { strerr_die3x(99, INFO, m0, m1); }
warn(char * m0,char * m1)44 void warn(char *m0, char *m1) { strerr_die3x(0, WARNING, m0, m1); }
fatal(char * m0,char * m1)45 void fatal(char *m0, char *m1) { strerr_die4sys(111, FATAL, m0, m1, ": "); }
46
main(int argc,const char ** argv)47 int main(int argc, const char **argv) {
48 int r;
49 int fd;
50 int opt;
51
52 progname =*argv;
53
54 while ((opt =getopt(argc, argv, "Vn:")) != opteof) {
55 switch(opt) {
56 case 'n': scan_ulong(optarg, &limit); break;
57 case 'V': strerr_warn1(VERSION, 0);
58 case '?': usage();
59 }
60 }
61 argv +=optind;
62
63 umask(0177);
64 sig_ignore(sig_pipe);
65
66 qconfirm_dir =*argv;
67 if (! qconfirm_dir) qconfirm_dir =env_get("QCONFIRM_DIR");
68 if (! qconfirm_dir) qconfirm_dir =QCONFIRMDIR;
69
70 /* check for empty sender */
71 sender =env_get("SENDER");
72 if (sender && *sender) warn("sender not empty: ", sender);
73
74 /* skip header */
75 if (next_paragraph(buffer_0) == -1) strerr_die2x(0, WARNING, "no body.");
76
77 /* 'Hi. This is the' */
78 if ((r =getline(buffer_0, &sa)) == -1)
79 strerr_die2x(0, WARNING, "empty body.");
80 if ((sa.len < 15) || ! str_start(sa.s, "Hi. This is the")) die_noqsbmf();
81 if (next_paragraph(buffer_0) == -1) die_noqsbmf();
82
83 /* failure paragraph */
84 if (getline(buffer_0, &sa) == -1) die_noqsbmf();
85 /* create id */
86 if ((sa.len < 4) || (sa.s[0] != '<') ||
87 (sa.s[sa.len -2] != ':') || (sa.s[sa.len -3] != '>')) die_noqsbmf();
88 sa.s[sa.len -3] =0;
89 if (address2id(&id, sa.s +1) == -1) die_nomem();
90
91 /* check pending/id */
92 if (! stralloc_copys(&pending, qconfirm_dir)) die_nomem();
93 if (! stralloc_cats(&pending, "/pending/")) die_nomem();
94 if (! stralloc_cat(&pending, &id)) die_nomem();
95 if (stat(pending.s, &s) == -1) {
96 if (errno != error_noent) fatal("unable to stat: ", pending.s);
97 warn("not pending: ", id.s);
98 }
99 if ((s.st_mode & S_IRWXU) == 0) info("already drop: ", pending.s);
100 if (next_paragraph(buffer_0) == -1) die_noqsbmf();
101
102 /* break paragraph */
103 if (getline(buffer_0, &sa) == -1) die_noqsbmf();
104 if (! sa.len || (sa.s[0] != '-')) die_noqsbmf();
105 if (next_paragraph(buffer_0) == -1) die_noqsbmf();
106
107 /* original message */
108 while ((r =getline(buffer_0, &sa)) > 0) {
109 int at, dash;
110
111 if ((sa.len > 6) && str_start(sa.s, "From: ")) {
112 /* From: header */
113 if (sa.s[sa.len -2] != '>') die_badheader();
114 if ((at =byte_rchr(sa.s, sa.len, '@')) == sa.len) die_badheader();
115 if ((dash =byte_rchr(sa.s, at, '-')) == at) die_badheader();
116 if (at -dash != 33) die_badheader();
117
118 /* read key */
119 if (openreadclose(pending.s, &k, 74) == -1)
120 fatal("unable to read: ", pending.s);
121 if ((k.len != 32) && (k.len < 34)) /* backward compatibility 0.8.0 */
122 strerr_die4x(111, FATAL,
123 "unable to read: ", pending.s, ": bad format");
124 if (byte_diff(sa.s +dash +1, 32, k.s) != 0)
125 warn("keys do not match: ", id.s);
126 break;
127 }
128 }
129 if (r <= 0) die_badheader();
130
131 /* check return/id */
132 if (! stralloc_copys(&sa, qconfirm_dir)) die_nomem();
133 if (! stralloc_cats(&sa, "/return/")) die_nomem();
134 if (! stralloc_cat(&sa, &id)) die_nomem();
135 if (stat(sa.s, &s) == -1) {
136 if (errno != error_noent) fatal("unable to stat: ", sa.s);
137 if ((fd =open_trunc(sa.s)) == -1) fatal("unable to create: ", sa.s);
138 close(fd);
139 info("create: ", sa.s);
140 }
141 if (s.st_size >= limit) {
142 /* drop */
143 if (chmod(pending.s, 0) == -1) fatal("unable to chmod: ", pending.s);
144 info("drop: ", pending.s);
145 }
146 if ((fd =open_append(sa.s)) == -1) fatal("unable to open: ", sa.s);
147 if (write(fd, "-", 1) != 1) fatal("unable to write: ", sa.s);
148 close(fd);
149 info("touch: ", sa.s);
150 _exit(0);
151 }
152