ezmlmx 0.68
ezmlmx
Loading...
Searching...
No Matches
ezmlm-reject.c
Go to the documentation of this file.
1#include <unistd.h>
2#include "buffer.h"
3#include "byte.h"
4#include "case.h"
5#include "str.h"
6#include "readwrite.h"
7#include "stralloc.h"
8#include "getln.h"
9#include "getoptb.h"
10#include "getconf.h"
11#include "constmap.h"
12#include "fmt.h"
13#include "qmail.h"
14#include "seek.h"
15#include "scan.h"
16#include "env.h"
17#include "errtxt.h"
18#include "idx.h"
19#include "auto_version.h"
20#include "logmsg.h"
21#include "ezmlm.h"
22
23#define WHO "ezmlm-reject"
24
30
31int flagrejectcommands = 1; /* reject if subject is simple command */
32int flagneedsubject = 1; /* reject if subject is mibig */
33int flagtook = 0; /* reject unless listaddress in To: or Cc: */
34int exitquiet = 100; /* reject with error (100) rather than exit */
35 /* quietly (99) if listaddress mibig */
36int flagheaderreject = 0; /* don't reject messages with headers from */
37 /* other mailing lists. */
38int flagbody = 0; /* =1 => reject if subject or body starts with*/
39 /* "subscribe" or "unsubscribe" */
40int flagforward = 0; /* =1 => forward commands to list-request */
44int flagcheck = 0; /* set after boundary is found in body, until blank line */
45
46stralloc mimeremove = {0};
47stralloc mimereject = {0};
48stralloc headerreject = {0};
49
50struct constmap mimeremovemap;
51struct constmap mimerejectmap;
52struct constmap headerrejectmap;
53
54char strnum[FMT_ULONG];
55char inbuf[256];
56buffer bi = BUFFER_INIT(read,0,inbuf,(int) sizeof(inbuf));
57buffer bj = BUFFER_INIT(read,0,inbuf,(int) sizeof(inbuf));
58
59struct qmail qq;
60ssize_t qqwrite(int fd,char *buf,unsigned int len)
61{
63 return len;
64}
65
66char qqbuf[256];
67buffer bq = BUFFER_INIT(qqwrite,-1,qqbuf,(int) sizeof(qqbuf));
68
69stralloc outhost = {0};
70stralloc outlocal = {0};
71
72stralloc line = {0};
73stralloc to = {0};
74stralloc content = {0};
75stralloc subject = {0};
76stralloc boundary = {0};
77stralloc precd = {0};
78stralloc mydtline = {0};
79
80static void die_nomem() { logmsg(WHO,100,FATAL,ERR_NOMEM); }
81static void die_usage() { logmsg(WHO,100,USAGE,"ezmlm-reject [-bBcCfFhHqQsStT] [dir]"); }
82
89
90unsigned int findlocal(stralloc *sa,unsigned int n)
91{
92 char *first;
93 char *s;
94 int level = 0;
95
96 first = sa->s;
97 s = sa->s + n;
98 if (s <= first) return n;
99
100 while (--s >= first) {
101 switch (*s) {
102 case ' ': case '\t': case '\n': break;
103 case ')': if (--s <= first) return n;
104 if (*s == '\\') break;
105 ++level; ++s;
106 while (level && --s > first) {
107 if (*s == ')') if (*(s-1) != '\\') ++level;
108 if (*s == '(') if (*(s-1) != '\\') --level;
109 }
110 break;
111 case '"': --s;
112 if (s < first) return n;
113 return (unsigned int) (s - first);
114 default: return (unsigned int) (s - first);
115 }
116 }
117}
118
124unsigned int findhost(stralloc *sa,unsigned int n)
125{
126 char *last;
127 char *s;
128 int level = 0;
129
130 last = sa->s + sa->len - 1;
131 s = sa->s + n;
132 if (s >= last) return n;
133
134 while (++s <= last) {
135 switch (*s) {
136 case ' ': case '\t': case '\n': break;
137 case '(': ++level;
138 while (level && (++s < last)) {
139 if (*s == ')') --level; if (!level) break;
140 if (*s == '(') ++level;
141 if (*s == '\\') ++s;
142 }
143 break;
144 case '"': while (++s < last) {
145 if (*s == '"') break;
146 if (*s == '\\') ++s;
147 }
148 break;
149 default: return (unsigned int) (s - sa->s);
150 }
151 }
152}
153
159
160int getto(stralloc *sa)
161{
162 unsigned int pos = 0;
163 unsigned int pos1;
164
165 if (!sa->len) return 0; /* no To: or Cc: line */
166 while ((pos += 1 + byte_chr(sa->s+pos+1,sa->len-pos-1,'@')) != sa->len) {
167 pos1 = findhost(sa,pos);
168 if (pos1 == pos) break;
169 if (pos1 + outhost.len <= sa->len)
170 if (!case_diffb(sa->s+pos1,outhost.len,outhost.s)) { /* got host */
171 pos1 = findlocal(sa,pos);
172 if (pos1 == pos) break;
173 ++pos1; /* avoids 1 x 2 below */
174 if (pos1 >= outlocal.len)
175 if (!case_diffb(sa->s+pos1-outlocal.len,outlocal.len,outlocal.s))
176 return 1; /* got local as well */
177 }
178 }
179 return 0;
180}
181
182int main(int argc,char **argv)
183{
184 unsigned long maxmsgsize = 0L;
185 unsigned long minmsgsize = 0L;
186 unsigned long msgsize = 0L;
187 int opt;
188 char linetype = ' ';
189 char *cp, *cpstart, *cpafter;
190 char *dir;
191 const char *err;
192 char *sender;
193 unsigned int len;
194 int match;
195
196 while ((opt = getoptb(argc,argv,"bBcCfFhHqQsStT")) != opteof)
197 switch(opt) {
198 case 'b': flagbody = 1; break;
199 case 'B': flagbody = 0; break;
200 case 'c': flagrejectcommands = 1; break;
201 case 'C': flagrejectcommands = 0; break;
202 case 'f': flagforward = 1; break;
203 case 'F': flagforward = 0; break;
204 case 'h': flagheaderreject = 1; break;
205 case 'H': flagheaderreject = 0; break;
206 case 'q': exitquiet = 99; break;
207 case 'Q': exitquiet = 100; break;
208 case 's': flagneedsubject = 1; break;
209 case 'S': flagneedsubject = 0; break;
210 case 't': flagtook = 0; break;
211 case 'T': flagtook = 1; break;
212 case 'v':
213 case 'V': logmsg(WHO,0,VERSION,auto_version);
214 default: die_usage();
215 }
216
217 dir = argv[optind];
218 if (dir) {
219 if (chdir(dir) == -1)
220 logmsg(WHO,111,FATAL,B(ERR_SWITCH,dir));
221
222 flagparsemime = 1; /* only if dir do we have mimeremove/reject */
223 if (getconf_line(&line,"msgsize",0,dir)) {
224 if (!stralloc_0(&line)) die_nomem();
225 len = scan_ulong(line.s,&maxmsgsize);
226 if (line.s[len] == ':')
227 scan_ulong(line.s+len+1,&minmsgsize);
228 }
229 if (!flagtook || flagforward) {
230 getconf_line(&outlocal,"outlocal",1,dir);
231 getconf_line(&outhost,"outhost",1,dir);
232 }
233 if (flagforward) {
234 if (!stralloc_copys(&mydtline,"Delivered-To: command forwarder for "))
235 die_nomem();
236 if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem();
237 if (!stralloc_cats(&mydtline,"@")) die_nomem();
238 if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem();
239 if (!stralloc_cats(&mydtline,"\n")) die_nomem();
240 }
241 } else {
242 flagtook = 1; /* if no "dir" we can't get outlocal/outhost */
243 flagforward = 0; /* nor forward requests */
244 }
245
246 sender = env_get("SENDER");
247 if (!sender)
248 logmsg(WHO,100,FATAL,ERR_NOSENDER);
249 if (!*sender)
250 logmsg(WHO,100,FATAL,ERR_BOUNCE);
251
252 if (flagparsemime) { /* set up MIME parsing */
253 getconf(&mimeremove,"mimeremove",0,dir);
254 constmap_init(&mimeremovemap,mimeremove.s,mimeremove.len,0);
255 getconf(&mimereject,"mimereject",0,dir);
256 constmap_init(&mimerejectmap,mimereject.s,mimereject.len,0);
257 }
258 if (flagheaderreject) {
259 if (!dir) die_usage();
260 getconf(&headerreject,"headerreject",1,dir);
261 constmap_init(&headerrejectmap,headerreject.s,headerreject.len,0);
262 }
263 for (;;) {
264 if (getln(&bi,&line,&match,'\n') == -1)
265 logmsg(WHO,111,FATAL,ERR_READ_INPUT);
266 if (!match) break;
268 if (constmap(&headerrejectmap,line.s,byte_chr(line.s,line.len,':')))
269 logmsg(WHO,100,FATAL,ERR_MAILING_LIST);
270
271 if (line.len == 1) break;
272 cp = line.s; len = line.len;
273 if ((*cp == ' ' || *cp == '\t')) {
274 switch (linetype) {
275 case 'T': if (!stralloc_catb(&to,cp,len-1)) die_nomem(); break;
276 case 'S': if (!stralloc_catb(&subject,cp,len-1)) die_nomem(); break;
277 case 'C': if (!stralloc_catb(&content,cp,len-1)) die_nomem(); break;
278 case 'P': if (!stralloc_catb(&precd,cp,len-1)) die_nomem(); break;
279 default: break;
280 }
281 } else {
282 if (!flagtook && (case_startb(cp,len,"to:") || case_startb(cp,len,"cc:"))) {
283 linetype = 'T'; /* cat so that To/Cc don't overwrite */
284 if (!stralloc_catb(&to,line.s + 3,line.len - 4)) die_nomem();
285 } else if ((flagneedsubject || flagrejectcommands) &&
286 case_startb(cp,len,"subject:")) {
287 if (!stralloc_copyb(&subject,cp+8,len-9)) die_nomem();
288 linetype = 'S';
289 } else if (case_startb(cp,len,"content-type:")) {
290 if (!stralloc_copyb(&content,cp+13,len-14)) die_nomem();
291 linetype = 'C';
292 } else if (case_startb(cp,len,"precedence:")) {
293 if (!stralloc_copyb(&precd,cp+11,len-12)) die_nomem();
294 linetype = 'P';
295 } else {
296 if (flagforward && line.len == mydtline.len) {
297 if (!byte_diff(line.s,line.len,mydtline.s))
298 logmsg(WHO,100,FATAL,ERR_LOOPING);
299 }
300 linetype = ' ';
301 }
302 }
303 }
304 if (precd.len >= 4 &&
305 (!case_diffb(precd.s + precd.len - 4,4,"junk") ||
306 !case_diffb(precd.s + precd.len - 4,4,"bulk")))
307 logmsg(WHO,99,INFO,ERR_JUNK); /* ignore precedence junk/bulk */
308
309 cp = subject.s;
310 len = subject.len;
311 while (len && (cp[len-1] == ' ' || cp[len-1] == '\t'))
312 --len;
313 while (len && ((*cp == ' ') || (*cp == '\t')))
314 { ++cp; --len; }
315 flaghavesubject = 1;
316
317 if (flagbody)
318 if (len > 9 && case_starts(cp,"subscribe") ||
319 len > 11 && case_starts(cp,"unsubscribe"))
320 flaghavecommand = 1;
321
322 switch(len) {
323 case 0: flaghavesubject = 0; break;
324 case 4: if (!case_diffb("help",4,cp)) flaghavecommand = 1; break;
325 /* Why can't they just leave an empty subject empty? */
326 case 6: if (!case_diffb("(null)",6,cp))
327 flaghavesubject = 0;
328 else
329 if (!case_diffb("(none)",6,cp))
330 flaghavesubject = 0;
331 else
332 if (!case_diffb("remove",6,cp))
333 flaghavecommand = 1;
334 break;
335 case 9: if (!case_diffb("subscribe",9,cp)) flaghavecommand = 1; break;
336 case 11: if (!case_diffb("unsubscribe",11,cp)) flaghavecommand = 1; break;
337 case 12: if (!case_diffb("(no subject)",12,cp)) flaghavesubject = 0; break;
338 default: break;
339 }
340
341 if (!flagtook && !getto(&to))
343
345 logmsg(WHO,100,FATAL,ERR_NO_SUBJECT);
346
348 if (flagforward) { /* flagforward => forward */
349 if (qmail_open(&qq,(stralloc *) 0) == -1) /* open queue */
350 logmsg(WHO,111,FATAL,ERR_QMAIL_QUEUE);
352 if (seek_begin(0) == -1)
353 logmsg(WHO,111,FATAL,ERR_SEEK_INPUT);
354 if (buffer_copy(&bq,&bj) != 0)
355 logmsg(WHO,111,FATAL,ERR_READ_INPUT);
356 if (!stralloc_copy(&to,&outlocal)) die_nomem();
357 if (!stralloc_cats(&to,"-request@")) die_nomem();
358 if (!stralloc_cat(&to,&outhost)) die_nomem();
359 if (!stralloc_0(&to)) die_nomem();
360 qmail_from(&qq,sender);
361 qmail_to(&qq,to.s);
362 if (*(err = qmail_close(&qq)) == '\0') {
363 strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0;
364 logmsg(WHO,99,INFO,B("forward qp ",strnum));
365 } else
366 logmsg(WHO,111,FATAL,B(ERR_TMP_QMAIL_QUEUE,err + 1));
367 } else
368 logmsg(WHO,100,FATAL,ERR_SUBCOMMAND);
369
370 if (content.len) { /* MIME header */
371 cp = content.s;
372 len = content.len;
373 while (len && *cp == ' ' || *cp == '\t') { ++cp; --len; }
374 cpstart = cp;
375 if (*cp == '"') { /* might be commented */
376 ++cp; cpstart = cp;
377 while (len && *cp != '"') { ++cp; --len; }
378 } else {
379 while (len && *cp != ' ' && *cp != '\t' && *cp != ';') {
380 ++cp; --len;
381 }
382 }
383
384 if (flagparsemime)
385 if (constmap(&mimeremovemap,cpstart,cp-cpstart) ||
386 constmap(&mimerejectmap,cpstart,cp-cpstart)) {
387 *(cp) = (char) 0;
388 logmsg(WHO,100,FATAL,B(ERR_BAD_TYPE,cpstart,"'",ERR_SIZE_CODE));
389 }
390
391 cpafter = content.s+content.len;
392 while ((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) {
393 ++cp;
394 while (cp < cpafter && (*cp == ' ') || (*cp == '\t'))
395 ++cp;
396 if (case_startb(cp,cpafter - cp,"boundary=")) {
397 cp += 9; /* after boundary= */
398 if (cp < cpafter && *cp == '"') {
399 ++cp;
400 cpstart = cp;
401 while (cp < cpafter && *cp != '"')
402 ++cp;
403 if (cp == cpafter)
404 logmsg(WHO,100,FATAL,ERR_MIME_QUOTE);
405 } else {
406 cpstart = cp;
407 while (cp < cpafter && *cp != ';' && *cp != ' ' && *cp != '\t')
408 ++cp;
409 }
410 if (!stralloc_copys(&boundary,"--")) die_nomem();
411 if (!stralloc_catb(&boundary,cpstart,cp-cpstart))
412 die_nomem();
413 break;
414 }
415 } /* got boundary, now parse for parts */
416 }
417
418 for (;;) {
419 if (getln(&bi,&line,&match,'\n') == -1)
420 logmsg(WHO,111,FATAL,ERR_READ_INPUT);
421 if (!match) break;
422 if (line.len == 1) {
423 flagcheck = 0;
424 continue;
425 /* Doesn't do continuation lines. _very_ unusual, and worst */
426 /* case one slips through that shouldn't have */
427 } else if (flagcheck && case_startb(line.s,line.len,"content-type:")) {
428 cp = line.s + 13;
429 len = line.len - 14; /* zap '\n' */
430 while (*cp == ' ' || *cp == '\t')
431 { ++cp; --len; }
432 cpstart = cp;
433 if (*cp == '"') { /* quoted */
434 ++cp; cpstart = cp;
435 while (len && *cp != '"')
436 { ++cp; --len; }
437 } else { /* not quoted */
438 while (len && *cp != ' ' && *cp != '\t' && *cp != ';') {
439 ++cp; --len;
440 }
441 }
442 if (flagparsemime && constmap(&mimerejectmap,cpstart,cp-cpstart)) {
443 *cp = '\0';
444 logmsg(WHO,100,FATAL,B(ERR_BAD_PART,cpstart,ERR_SIZE_CODE));
445 }
446 } else if (boundary.len && *line.s == '-' && line.len > boundary.len &&
447 !str_diffn(line.s,boundary.s,boundary.len)) {
448 flagcheck = 1;
449 } else {
450 if (!msgsize && flagbody)
451 if (case_startb(line.s,line.len,"subscribe") || case_startb(line.s,line.len,"unsubscribe"))
452 logmsg(WHO,100,FATAL,ERR_BODYCOMMAND);
453 if (!flagcheck) {
454 msgsize += line.len;
455 if (maxmsgsize && msgsize > maxmsgsize) {
456 strnum[fmt_ulong(strnum,maxmsgsize)] = 0;
457 logmsg(WHO,100,FATAL,B(ERR_MAX_SIZE,strnum," bytes",ERR_SIZE_CODE));
458 }
459 }
460 }
461 }
462 if (msgsize < minmsgsize) {
463 strnum[fmt_ulong(strnum,minmsgsize)] = 0;
464 logmsg(WHO,100,FATAL,B(ERR_MIN_SIZE,strnum," bytes",ERR_SIZE_CODE));
465 }
466 _exit(0);
467
468 return 0;
469}
const char auto_version[]
Error messages. If you translate these, I would urge you to keep the English version as well....
#define ERR_BAD_PART
Definition errtxt.h:84
#define ERR_NOMEM
Definition errtxt.h:14
#define ERR_BODYCOMMAND
Definition errtxt.h:82
#define ERR_SIZE_CODE
Definition errtxt.h:78
#define ERR_NO_ADDRESS
Definition errtxt.h:79
#define ERR_READ_INPUT
Definition errtxt.h:26
#define ERR_MAILING_LIST
Definition errtxt.h:57
#define ERR_NO_SUBJECT
Definition errtxt.h:80
#define ERR_SUBCOMMAND
Definition errtxt.h:81
#define ERR_MAX_SIZE
Definition errtxt.h:76
#define ERR_LOOPING
Definition errtxt.h:58
#define ERR_MIN_SIZE
Definition errtxt.h:77
#define ERR_QMAIL_QUEUE
Definition errtxt.h:53
#define ERR_SWITCH
Definition errtxt.h:42
#define ERR_SEEK_INPUT
Definition errtxt.h:27
#define ERR_MIME_QUOTE
Definition errtxt.h:68
#define ERR_JUNK
Definition errtxt.h:85
#define ERR_NOSENDER
Definition errtxt.h:37
#define ERR_TMP_QMAIL_QUEUE
Definition errtxt.h:54
#define ERR_BOUNCE
Definition errtxt.h:38
#define ERR_BAD_TYPE
Definition errtxt.h:83
const char * qmail_close(struct qmail *)
Definition qmail.c:120
void qmail_put(struct qmail *, const char *, int)
Definition qmail.c:87
void qmail_from(struct qmail *, const char *)
Definition qmail.c:103
void qmail_to(struct qmail *, const char *)
Definition qmail.c:113
unsigned long qmail_qp(struct qmail *)
Definition qmail.c:77
int qmail_open(struct qmail *, const stralloc *)
Definition qmail.c:25
charset, outhost, outlocal and flagcd are shared
void die_nomem()
Definition getconf.c:17
int getconf(stralloc *sa, const char *fn, int flagrequired, const char *dir)
Definition getconf.c:21
int getconf_line(stralloc *sa, const char *fn, int flagrequired, const char *dir)
Definition getconf.c:53
#define WHO
Definition author.c:1
char boundary[COOKIE]
Definition ezmlm-get.c:89
stralloc mydtline
Definition ezmlm-get.c:72
buffer bj
Definition ezmlm-get.c:154
unsigned long msgsize
Definition ezmlm-get.c:84
int flagneedsubject
struct constmap mimerejectmap
int flagbody
stralloc mimereject
stralloc mimeremove
struct constmap headerrejectmap
int flagtook
int exitquiet
int flaghavesubject
int flaghavecommand
unsigned int findlocal(stralloc *sa, unsigned int n)
int flagrejectcommands
int flagparsemime
struct constmap mimeremovemap
ssize_t qqwrite(int fd, char *buf, unsigned int len)
int flagcheck
stralloc precd
int flagforward
stralloc headerreject
unsigned int findhost(stralloc *sa, unsigned int n)
int getto(stralloc *sa)
int flagheaderreject
char inbuf[1024]
char * dir
buffer bi
int main()
Definition ezmlm-weed.c:69
int opt
Definition ezmlm-cron.c:53
const char * cp
Definition ezmlm-cron.c:76
unsigned int len
Definition ezmlm-cron.c:68
char buf[256]
Definition install.c:113
int fd
Definition ezmlm-cgi.c:141
stralloc content
Definition ezmlm-cgi.c:128
int match
Definition ezmlm-cgi.c:140
stralloc subject
Definition ezmlm-cgi.c:119
buffer bq
Definition ezmlm-clean.c:81
ssize_t qqwrite(int fd, char *buf, unsigned int len)
Definition ezmlm-clean.c:75
stralloc to
Definition ezmlm-clean.c:97
struct qmail qq
Definition ezmlm-clean.c:73
Definition qmail.h:10
const char * logmsg(const char *dir, unsigned long num, unsigned long listno, unsigned long subs, int done)
Definition loginfo.c:32