1 #include <bglibs/sysdeps.h>
2 #include <ctype.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <unistd.h>
7 #include <bglibs/iobuf.h>
8 #include <bglibs/msg.h>
9 #include <bglibs/path.h>
10 #include <bglibs/str.h>
11 #include <bglibs/striter.h>
12 #include <bglibs/systime.h>
13 #include "qmail-autoresponder.h"
14
15 str boundary = {0,0,0};
16 str copyheaders = {0,0,0};
17 str message_id = {0,0,0};
18 str subject = {0,0,0};
19
20 static const char* dtline;
21 static size_t dtline_len;
22 static str content_type;
23 static str headers;
24
ignore_ml(const str * s,const char * header)25 static void ignore_ml(const str* s, const char* header)
26 {
27 unsigned hdrlen = strlen(header);
28 if (strncasecmp(s->s, header, hdrlen) == 0 && s->s[hdrlen] == ':') {
29 fail_msg("Ignoring message:");
30 msgf("{Message appears to be from a mailing list (}s{ header)}", header);
31 exit(0);
32 }
33 }
34
skip_space(const char * s)35 static const char* skip_space(const char* s)
36 {
37 while(*s && isspace(*s))
38 ++s;
39 return s;
40 }
41
header_copy_if(const str * src,str * dest,const char * prefix,unsigned prefix_len)42 static void header_copy_if(const str* src, str* dest,
43 const char* prefix, unsigned prefix_len)
44 {
45 unsigned int lf;
46 unsigned int end;
47
48 if (src->len > prefix_len
49 && strncasecmp(src->s, prefix, prefix_len) == 0) {
50 str_copyb(dest, src->s + prefix_len, src->len - prefix_len);
51 str_strip(dest);
52 /* Replace embeded newlines followed by variable whitespace with a
53 * single space. */
54 while ((lf = str_findfirst(dest, '\n')) < dest->len) {
55 end = lf + 1;
56 while (lf > 0 && isspace(dest->s[lf-1]))
57 --lf;
58 while (end < dest->len && isspace(dest->s[end]))
59 ++end;
60 str_spliceb(dest, lf, end - lf, " ", 1);
61 }
62 }
63 }
64
header_test(const str * h,const char * list)65 static int header_test(const str* h, const char* list)
66 {
67 static str pattern;
68 const char* colon;
69 size_t len;
70
71 do {
72 len = ((colon = strchr(list, ':')) == 0)
73 ? strlen(list)
74 : (size_t)(colon - list);
75 str_copyb(&pattern, list, len);
76 str_cats(&pattern, ": *");
77 if (str_case_glob(h, &pattern))
78 return 1;
79 list = colon + 1;
80 } while (colon != 0);
81 return 0;
82 }
83
parse_header(const str * s)84 static void parse_header(const str* s)
85 {
86 if (opt_copymsg) {
87 if (opt_headerkeep
88 ? header_test(s, opt_headerkeep)
89 : opt_headerstrip
90 ? !header_test(s, opt_headerstrip)
91 : 1)
92 str_cat(©headers, s);
93 }
94
95 ignore_ml(s, "List-ID");
96 ignore_ml(s, "Mailing-List");
97 ignore_ml(s, "X-Mailing-List");
98 ignore_ml(s, "X-ML-Name");
99 ignore_ml(s, "List-Help");
100 ignore_ml(s, "List-Unsubscribe");
101 ignore_ml(s, "List-Subscribe");
102 ignore_ml(s, "List-Post");
103 ignore_ml(s, "List-Owner");
104 ignore_ml(s, "List-Archive");
105
106 if(!strncasecmp(s->s, "Precedence:", 11)) {
107 const char* start = skip_space(s->s + 11);
108 const char* end = start;
109 while (end < s->s + s->len && !isspace(*end))
110 ++end;
111 if(!strncasecmp(start, "junk", end-start) ||
112 !strncasecmp(start, "bulk", end-start) ||
113 !strncasecmp(start, "list", end-start))
114 ignore("Message has a junk, bulk, or list precedence header");
115 }
116 else if(!strncasecmp(s->s, dtline, dtline_len-1))
117 ignore("Message already has my Delivered-To line");
118 else {
119 header_copy_if(s, &subject, "subject:", 8);
120 header_copy_if(s, &message_id, "message-id:", 11);
121 header_copy_if(s, &content_type, "content-type:", 13);
122 }
123 }
124
read_headers(void)125 static void read_headers(void)
126 {
127 str_truncate(&headers, 0);
128 while (ibuf_getstr(&inbuf, &tmpstr, LF)) {
129 if (tmpstr.s[0] == LF)
130 break;
131 str_cat(&headers, &tmpstr);
132 }
133 }
134
find_field(const char * s,const char * field)135 static const char* find_field(const char* s, const char* field)
136 {
137 int fpos;
138 for (fpos = -1; *s != 0; ++s) {
139 if (isspace(*s) || *s == ';')
140 fpos = 0;
141 else if (fpos >= 0) {
142 if (tolower(*s) != field[fpos])
143 fpos = -1;
144 else if (field[++fpos] == 0) {
145 do { ++s; } while(isspace(*s));
146 if (*s != '=')
147 fpos = -1;
148 else {
149 do { ++s; } while(isspace(*s));
150 return s;
151 }
152 }
153 }
154 }
155 return 0;
156 }
157
extract_field(const char * s,const char * field,str * value)158 static int extract_field(const char* s, const char* field, str* value)
159 {
160 char quote;
161 const char* start;
162 const char* end;
163
164 if ((start = find_field(s, field)) != 0) {
165 if (*start == '"' || *start == '\'')
166 for (quote = *start++, end = start; *end != 0 && *end != quote; ++end)
167 ;
168 else
169 for (end = start; *end != 0 && !isspace(*end) && (*end != ';'); ++end)
170 ;
171 str_copyb(value, start, end - start);
172 return 1;
173 }
174 return 0;
175 }
176
parse_content_type(void)177 static void parse_content_type(void)
178 {
179 if (str_starts(&content_type, "multipart/")) {
180 if (extract_field(content_type.s + 10, "boundary", &boundary))
181 str_splices(&boundary, 0, 0, "--");
182 }
183 }
184
parse_headers(void)185 static void parse_headers(void)
186 {
187 striter i;
188
189 striter_loop(&i, &headers, LF) {
190 unsigned next = i.start + i.len + 1;
191
192 str_catb(&tmpstr, headers.s + i.start, i.len + 1);
193
194 if (next >= headers.len
195 || !isspace(headers.s[next])) {
196 parse_header(&tmpstr);
197 str_truncate(&tmpstr, 0);
198 }
199 }
200 parse_content_type();
201 }
202
read_parse_headers(void)203 void read_parse_headers(void)
204 {
205 dtline = getenv("DTLINE");
206 if(!dtline)
207 usage("DTLINE is not set; must be run from qmail.");
208 dtline_len = strlen(dtline);
209
210 // Read and parse header
211 if(lseek(0, 0, SEEK_SET) == -1)
212 fail_temp("Could not rewind input message.");
213 read_headers();
214 parse_headers();
215 }
216