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