s/qmail 4.2.29a
Next generation secure email transport
Loading...
Searching...
No Matches
qmail-send.c
Go to the documentation of this file.
1#include <sys/types.h>
2#include <sys/stat.h>
3#include <string.h>
4#include <unistd.h>
5#include <utime.h>
6#include "error.h"
7#include "sig.h"
8#include "direntry.h"
9#include "control.h"
10#include "select.h"
11#include "open.h"
12#include "seek.h"
13#include "exit.h"
14#include "lock.h"
15#include "ndelay.h"
16#include "now.h"
17#include "getln.h"
18#include "buffer.h"
19#include "alloc.h"
20#include "genalloc.h"
21#include "stralloc.h"
22#include "logmsg.h"
23#include "str.h"
24#include "byte.h"
25#include "fmt.h"
26#include "scan.h"
27#include "case.h"
28#include "auto_qmail.h"
29#include "trigger.h"
30#include "newfield.h"
31#include "quote.h"
32#include "qmail.h"
33#include "qsutil.h"
34#include "prioq.h"
35#include "constmap.h"
36#include "fmtqfn.h"
37#include "readsubdir.h"
38#include "sendtodo.h"
39
40int lifetime = 604800;
42
43stralloc percenthack = {0};
45stralloc locals = {0};
47stralloc vdoms = {0};
49stralloc envnoathost = {0};
50stralloc bouncefrom = {0};
51stralloc bouncehost = {0};
52stralloc doublebounceto = {0};
53stralloc doublebouncehost = {0};
54
55char strnum2[FMT_ULONG];
56char strnum3[FMT_ULONG];
57
58#define CHANNELS 2
59char *chanaddr[CHANNELS] = { "local/", "remote/" };
60char *chanstatusmsg[CHANNELS] = { " local ", " remote " };
61char *tochan[CHANNELS] = { " to local ", " to remote " };
62int chanfdout[CHANNELS] = { 1, 3 };
63int chanfdin[CHANNELS] = { 2, 4 };
64int chanskip[CHANNELS] = { 10, 20 };
65
66int flagexitasap = 0; void sigterm() { flagexitasap = 1; }
67int flagrunasap = 0; void sigalrm() { flagrunasap = 1; }
68int flagreadasap = 0; void sighup() { flagreadasap = 1; }
69
70void cleandied()
71{
72 log1s("alert: lost connection to qmail-clean ... exiting\n");
73 flagexitasap = 1;
74}
75
77
78void spawndied(int c)
79{
80 log1s("alert: oh no! lost spawn connection! dying...\n");
81 flagspawnalive[c] = 0;
82 flagexitasap = 1;
83}
84
85#define REPORTMAX 10000
86
88
89
90/* this file is too long ---------------------------------------- FILE CREATE */
91
92stralloc fn = {0};
93stralloc fn2 = {0};
94char fnmake_strnum[FMT_ULONG];
95
97{
98 while (!stralloc_ready(&fn,FMTQFN)) nomem();
99 while (!stralloc_ready(&fn2,FMTQFN)) nomem();
100}
101
102void fnmake_info(unsigned long id) { fn.len = fmtqfn(fn.s,"info/",id,1); }
103void fnmake_todo(unsigned long id) { fn.len = fmtqfn(fn.s,"todo/",id,1); }
104void fnmake_mess(unsigned long id) { fn.len = fmtqfn(fn.s,"mess/",id,1); }
105void fnmake_foop(unsigned long id) { fn.len = fmtqfn(fn.s,"foop/",id,0); }
106void fnmake_split(unsigned long id) { fn.len = fmtqfn(fn.s,"",id,1); }
107void fnmake2_bounce(unsigned long id) { fn2.len = fmtqfn(fn2.s,"bounce/",id,0); }
108void fnmake_chanaddr(unsigned long id,int c) { fn.len = fmtqfn(fn.s,chanaddr[c],id,1); }
109
110
111/* this file is too long ----------------------------------------- REWRITING */
112
113void senderadd(stralloc *sa,char *sender,char *recip)
114{
115 int i;
116 int j;
117 int k;
118
119 i = str_len(sender);
120 if (i >= 4)
121 if (str_equal(sender + i - 4,"-@[]")) {
122 j = byte_rchr(sender,i - 4,'@');
123 k = str_rchr(recip,'@');
124 if (recip[k] && (j + 5 <= i)) {
125 /* owner-@host-@[] -> owner-recipbox=reciphost@host */
126 while (!stralloc_catb(sa,sender,j)) nomem();
127 while (!stralloc_catb(sa,recip,k)) nomem();
128 while (!stralloc_cats(sa,"=")) nomem();
129 while (!stralloc_cats(sa,recip + k + 1)) nomem();
130 while (!stralloc_cats(sa,"@")) nomem();
131 while (!stralloc_catb(sa,sender + j + 1,i - 5 - j)) nomem();
132 return;
133 }
134 }
135 while (!stralloc_cats(sa,sender)) nomem();
136}
137
138
139/* this file is too long ---------------------------------------------- INFO */
140
141int getinfo(stralloc *sa,datetime_sec *dt,unsigned long id)
142{
143 int fdnumber;
144 struct stat st;
145 static stralloc line = {0};
146 int match;
147 char buf[128];
148 buffer b;
149
150 fnmake_info(id);
151 fdnumber = open_read(fn.s);
152 if (fdnumber == -1) return 0;
153 if (fstat(fdnumber,&st) == -1) { close(fdnumber); return 0; }
154 buffer_init(&b,read,fdnumber,buf,sizeof(buf));
155 if (getln(&b,&line,&match,'\0') == -1) { close(fdnumber); return 0; }
156 close(fdnumber);
157 if (!match) return 0;
158 if (line.s[0] != 'F') return 0;
159
160 *dt = st.st_mtime;
161 while (!stralloc_copys(sa,line.s + 1)) nomem();
162 while (!stralloc_0(sa)) nomem();
163 return 1;
164}
165
166
167/* this file is too long ------------------------------------- COMMUNICATION */
168
169buffer toqc; char toqcbuf[1024];
170buffer fromqc; char fromqcbuf[1024];
171stralloc comm_buf[CHANNELS] = { {0}, {0} };
173
175{
176 int c;
177
178 buffer_init(&toqc,write,5,toqcbuf,sizeof(toqcbuf));
179 buffer_init(&fromqc,read,6,fromqcbuf,sizeof(fromqcbuf));
180 for (c = 0; c < CHANNELS; ++c)
181 if (ndelay_on(chanfdout[c]) == -1)
182 /* this is so stupid: NDELAY semantics should be default on write */
183 spawndied(c); /* drastic, but better than risking deadlock */
184}
185
187{
188 /* XXX: could allow a bigger buffer; say 10 recipients */
189 if (comm_buf[c].s && comm_buf[c].len) return 0;
190 return 1;
191}
192
193void comm_write(int c,int delnum,unsigned long id,char *sender,char *recip)
194{
195 char ch;
196
197 if (comm_buf[c].s && comm_buf[c].len) return;
198 while (!stralloc_copys(&comm_buf[c],"")) nomem();
199 ch = delnum;
200 while (!stralloc_append(&comm_buf[c],&ch)) nomem();
201 fnmake_split(id);
202 while (!stralloc_cats(&comm_buf[c],fn.s)) nomem();
203 while (!stralloc_0(&comm_buf[c])) nomem();
205 while (!stralloc_0(&comm_buf[c])) nomem();
206 while (!stralloc_cats(&comm_buf[c],recip)) nomem();
207 while (!stralloc_0(&comm_buf[c])) nomem();
208 comm_pos[c] = 0;
209}
210
211void comm_selprep(int *nfds,fd_set *wfds)
212{
213 int c;
214
215 for (c = 0; c < CHANNELS; ++c)
216 if (flagspawnalive[c])
217 if (comm_buf[c].s && comm_buf[c].len) {
218 FD_SET(chanfdout[c],wfds);
219 if (*nfds <= chanfdout[c])
220 *nfds = chanfdout[c] + 1;
221 }
222}
223
224void comm_do(fd_set *wfds)
225{
226 int c;
227
228 for (c = 0; c < CHANNELS; ++c)
229 if (flagspawnalive[c])
230 if (comm_buf[c].s && comm_buf[c].len)
231 if (FD_ISSET(chanfdout[c],wfds)) {
232 int w;
233 int len;
234 len = comm_buf[c].len;
235
236 w = write(chanfdout[c],comm_buf[c].s + comm_pos[c],len - comm_pos[c]);
237 if (w <= 0) {
238 if ((w == -1) && (errno == EPIPE))
239 spawndied(c);
240 else
241 continue; /* kernel select() bug; can't avoid busy-looping */
242 } else {
243 comm_pos[c] += w;
244 if (comm_pos[c] == len)
245 comm_buf[c].len = 0;
246 }
247 }
248}
249
250
251/* this file is too long ------------------------------------------ CLEANUPS */
252
253int flagcleanup; /* if 1, cleanupdir is initialized and ready */
256
258{
259 flagcleanup = 0;
260 cleanuptime = now();
261}
262
264{
265 if (flagcleanup) *wakeup = 0;
266 if (*wakeup > cleanuptime) *wakeup = cleanuptime;
267}
268
270{
271 char ch;
272 struct stat st;
273 unsigned long id;
274
275 if (!flagcleanup) {
276 if (recent < cleanuptime) return;
278 flagcleanup = 1;
279 }
280
281 switch (readsubdir_next(&cleanupdir,&id)) {
282 case 1: break;
284 default: return;
285 }
286
287 fnmake_mess(id);
288 if (stat(fn.s,&st) == -1) return; /* probably qmail-queue deleted it */
289 if (recent <= st.st_atime + OSSIFIED) return;
290
291 fnmake_info(id);
292 if (stat(fn.s,&st) == 0) return;
293 if (errno != ENOENT) return;
294
295 fnmake_todo(id);
296 if (stat(fn.s,&st) == 0) return;
297 if (errno != ENOENT) return;
298
299 fnmake_foop(id);
300 if (buffer_putflush(&toqc,fn.s,fn.len) == -1) { cleandied(); return; }
301 if (buffer_get(&fromqc,&ch,1) != 1) { cleandied(); return; }
302 if (ch != '+')
303 log3s("warning: qmail-clean unable to clean up ",fn.s,"\n");
304}
305
306
307/* this file is too long ----------------------------------- PRIORITY QUEUES */
308
309prioq pqdone = {0}; /* -todo +info; HOPEFULLY -local -remote */
310prioq pqchan[CHANNELS] = { {0}, {0} };
311/* pqchan 0: -todo +info +local ?remote */
312/* pqchan 1: -todo +info ?local +remote */
313prioq pqfail = {0}; /* stat() failure; has to be pqadded again */
314
315void pqadd(unsigned long id)
316{
317 struct prioq_elt pe;
318 struct prioq_elt pechan[CHANNELS];
319 int flagchan[CHANNELS];
320 struct stat st;
321 int c;
322
323#define CHECKSTAT if (errno != ENOENT) goto FAIL;
324
325 fnmake_info(id);
326 if (stat(fn.s,&st) == -1) {
328 return; /* someone yanking our chain */
329 }
330
331 fnmake_todo(id);
332 if (stat(fn.s,&st) != -1) return; /* look, ma, dad crashed writing info! */
334
335 for (c = 0; c < CHANNELS; ++c) {
336 fnmake_chanaddr(id,c);
337 if (stat(fn.s,&st) == -1) { flagchan[c] = 0; CHECKSTAT }
338 else { flagchan[c] = 1; pechan[c].id = id; pechan[c].dt = st.st_mtime; }
339 }
340
341 for (c = 0; c < CHANNELS; ++c)
342 if (flagchan[c])
343 while (!prioq_insert(&pqchan[c],&pechan[c])) nomem();
344
345 for (c = 0; c < CHANNELS; ++c)
346 if (flagchan[c]) break;
347
348 if (c == CHANNELS) {
349 pe.id = id; pe.dt = now();
350 while (!prioq_insert(&pqdone,&pe)) nomem();
351 }
352
353 return;
354
355 FAIL:
356 log3s("warning: unable to stat ",fn.s,"; will try again later\n");
357 pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
358 while (!prioq_insert(&pqfail,&pe)) nomem();
359}
360
362{
364 int x;
365 unsigned long id;
366
367 readsubdir_init(&rs,"info",pausedir);
368
369 while ((x = readsubdir_next(&rs,&id)))
370 if (x > 0) pqadd(id);
371}
372
374{
375 int c;
376 struct prioq_elt pe;
377 time_t ut[2]; /* XXX: more portable than utimbuf, but still worrisome */
378
379 for (c = 0; c < CHANNELS; ++c)
380 while (prioq_min(&pqchan[c],&pe)) {
382 fnmake_chanaddr(pe.id,c);
383 ut[0] = ut[1] = pe.dt;
384 if (utime(fn.s,ut) == -1)
385 log3s("warning: unable to utime ",fn.s,"; message will be retried too soon\n");
386 }
387}
388
389void pqrun()
390{
391 int c;
392 int i;
393
394 for (c = 0; c < CHANNELS; ++c)
395 if (pqchan[c].p)
396 if (pqchan[c].len)
397 for (i = 0; i < pqchan[c].len; ++i)
398 pqchan[c].p[i].dt = recent;
399}
400
401
402/* this file is too long ---------------------------------------------- JOBS */
403
404struct job
405{
406 int refs; /* if 0, this struct is unused */
407 unsigned long id;
410 stralloc sender;
414};
415
417struct job *jo;
418
420{
421 int j;
422
423 while (!(jo = (struct job *) alloc(numjobs * sizeof(struct job)))) nomem();
424 for (j = 0; j < numjobs; ++j) {
425 jo[j].refs = 0;
426 jo[j].sender.s = 0;
427 }
428}
429
431{
432 int j;
433
434 for (j = 0; j < numjobs; ++j)
435 if (!jo[j].refs) return 1;
436 return 0;
437}
438
439int job_open(unsigned long id,int channel)
440{
441 int j;
442
443 for (j = 0; j < numjobs; ++j)
444 if (!jo[j].refs) break;
445 if (j == numjobs) return -1;
446 jo[j].refs = 1;
447 jo[j].id = id;
448 jo[j].channel = channel;
449 jo[j].numtodo = 0;
450 jo[j].flaghiteof = 0;
451 return j;
452}
453
454void job_close(int j)
455{
456 struct prioq_elt pe;
457 struct stat st;
458 int c;
459
460 if (0 < --jo[j].refs) return;
461
462 pe.id = jo[j].id;
463 pe.dt = jo[j].retry;
464
465 if (jo[j].flaghiteof && !jo[j].numtodo) {
466 fnmake_chanaddr(jo[j].id,jo[j].channel);
467 if (unlink(fn.s) == -1) {
468 log3s("warning: unable to unlink ",fn.s,"; will try again later\n");
469 pe.dt = now() + SLEEP_SYSFAIL;
470 } else {
471 for (c = 0; c < CHANNELS; ++c) if (c != jo[j].channel) {
472 fnmake_chanaddr(jo[j].id,c);
473 if (stat(fn.s,&st) == 0) return; /* more channels going */
474 if (errno != ENOENT) {
475 log3s("warning: unable to stat ",fn.s,"\n");
476 break; /* this is the only reason for HOPEFULLY */
477 }
478 }
479 pe.dt = now();
480 while (!prioq_insert(&pqdone,&pe)) nomem();
481 return;
482 }
483 }
484
485 while (!prioq_insert(&pqchan[jo[j].channel],&pe)) nomem();
486}
487
488
489/* this file is too long ------------------------------------------- BOUNCES */
490
492{
493 int i;
494 char *domain;
495 int domainlen;
496 char *prepend;
497
498 i = str_rchr(recip,'@');
499 if (!recip[i]) return recip;
500 domain = recip + i + 1;
501 domainlen = str_len(domain);
502
503 for (i = 0; i <= domainlen; ++i)
504 if ((i == 0) || (i == domainlen) || (domain[i] == '.'))
505 if ((prepend = constmap(&mapvdoms,domain + i,domainlen - i))) {
506 if (!*prepend) break;
507 i = str_len(prepend);
508 if (str_diffn(recip,prepend,i)) break;
509 if (recip[i] != '-') break;
510 return recip + i + 1;
511 }
512
513 return recip;
514}
515
516stralloc bouncetext = {0};
517
518void addbounce(unsigned long id,char *recip,char *report)
519{
520 int fd;
521 int pos;
522 int w;
523
524 while (!stralloc_copys(&bouncetext,"<")) nomem();
525 while (!stralloc_cats(&bouncetext,stripvdomprepend(recip))) nomem();
526
527 for (pos = 0; pos < bouncetext.len; ++pos)
528 if (bouncetext.s[pos] == '\n')
529 bouncetext.s[pos] = '_';
530
531 while (!stralloc_cats(&bouncetext,">:\n")) nomem();
532 while (!stralloc_cats(&bouncetext,report)) nomem();
533
534 if (report[0])
535 if (report[str_len(report) - 1] != '\n')
536 while (!stralloc_cats(&bouncetext,"\n")) nomem();
537
538 for (pos = bouncetext.len - 2; pos > 0; --pos)
539 if (bouncetext.s[pos] == '\n')
540 if (bouncetext.s[pos - 1] == '\n')
541 bouncetext.s[pos] = '/';
542
543 while (!stralloc_cats(&bouncetext,"\n")) nomem();
544 fnmake2_bounce(id);
545
546 for (;;) {
547 fd = open_append(fn2.s);
548 if (fd != -1) break;
549 log1s("alert: unable to append to bounce message; HELP! sleeping...\n");
550 sleep(10);
551 }
552
553 pos = 0;
554
555 while (pos < bouncetext.len) {
556 w = write(fd,bouncetext.s + pos,bouncetext.len - pos);
557 if (w <= 0) {
558 log1s("alert: unable to append to bounce message; HELP! sleeping...\n");
559 sleep(10);
560 }
561 else
562 pos += w;
563 }
564 close(fd);
565}
566
567int injectbounce(unsigned long id)
568{
569 struct qmail qqt;
570 struct stat st;
571 char *bouncesender;
572 char *bouncerecip;
573 int r;
574 int fd;
575 buffer bi;
576 char buf[128];
577 char inbuf[128];
578 static stralloc sender = {0};
579 static stralloc quoted = {0};
581 unsigned long qp;
582 int bytestogo;
583 int bytestoget;
584
585 if (!getinfo(&sender,&birth,id)) return 0; /* XXX: print warning */
586
587 /* owner-@host-@[] -> owner-@host */
588 if (sender.len >= 5)
589 if (str_equal(sender.s + sender.len - 5,"-@[]")) {
590 sender.len -= 4;
591 sender.s[sender.len - 1] = 0;
592 }
593
594 fnmake2_bounce(id);
595 fnmake_mess(id);
596
597 if (stat(fn2.s,&st) == -1) {
598 if (errno == ENOENT) return 1;
599 log3s("warning: unable to stat ",fn2.s,"\n");
600 return 0;
601 }
602
603 if (str_equal(sender.s,"#@[]"))
604 log3s("triple bounce: discarding ",fn2.s,"\n");
605 else if (!*sender.s && *doublebounceto.s == '@')
606 log3s("double bounce: discarding ",fn2.s,"\n");
607 else {
608 if (qmail_open(&qqt) == -1)
609 { log1s("warning: unable to start qmail-queue, will try later\n"); return 0; }
610 qp = qmail_qp(&qqt);
611
612 if (*sender.s) { bouncesender = ""; bouncerecip = sender.s; }
613 else { bouncesender = "#@[]"; bouncerecip = doublebounceto.s; }
614
615 while (!newfield_datemake(now())) nomem();
617 qmail_puts(&qqt,"From: ");
618 while (!quote(&quoted,&bouncefrom)) nomem();
619 qmail_put(&qqt,quoted.s,quoted.len);
620 qmail_puts(&qqt,"@");
622 qmail_puts(&qqt,"\nTo: ");
623 while (!quote2(&quoted,bouncerecip)) nomem();
624 qmail_put(&qqt,quoted.s,quoted.len);
625 qmail_puts(&qqt,"\n\
626Subject: failure notice\n\
627\n\
628Hi. This is the qmail-send program at ");
630 qmail_puts(&qqt,*sender.s ? ".\n\
631I'm afraid I wasn't able to deliver your message to the following addresses.\n\
632This is a permanent error; I've given up. Sorry it didn't work out.\n\
633\n\
634" : ".\n\
635I tried to deliver a bounce message to this address, but the bounce bounced!\n\
636\n\
637");
638
639 fd = open_read(fn2.s);
640 if (fd == -1)
641 qmail_fail(&qqt);
642 else {
643 buffer_init(&bi,read,fd,inbuf,sizeof(inbuf));
644 while ((r = buffer_get(&bi,buf,sizeof(buf))) > 0)
645 qmail_put(&qqt,buf,r);
646
647 close(fd);
648 if (r == -1) qmail_fail(&qqt);
649 }
650
651 qmail_puts(&qqt,*sender.s ? "--- Below this line is a copy of the message.\n\n" : "--- Below this line is the original bounce.\n\n");
652 qmail_puts(&qqt,"Return-Path: <");
653 while (!quote2(&quoted,sender.s)) nomem();
654 qmail_put(&qqt,quoted.s,quoted.len);
655 qmail_puts(&qqt,">\n");
656
657 fd = open_read(fn.s);
658 if (fd == -1)
659 qmail_fail(&qqt);
660 else {
661 if (bouncemaxbytes) {
662 bytestogo = bouncemaxbytes;
663 bytestoget = (bytestogo < sizeof(buf)) ? bytestogo : sizeof(buf);
664
665 buffer_init(&bi,read,fd,inbuf,sizeof(inbuf));
666
667 while (bytestoget > 0 && (r = buffer_get(&bi,buf,bytestoget)) > 0) {
668 qmail_put(&qqt,buf,r);
669 bytestogo -= bytestoget;
670 bytestoget = (bytestogo < sizeof(buf)) ? bytestogo : sizeof(buf);
671 }
672 if (r > 0)
673 qmail_puts(&qqt,"\n\n--- Rest of message truncated.\n");
674 } else { /* preserve default behavior */
675 buffer_init(&bi,read,fd,inbuf,sizeof(inbuf));
676
677 while ((r = buffer_get(&bi,buf,sizeof(buf))) > 0)
678 qmail_put(&qqt,buf,r);
679 }
680 close(fd);
681 if (r == -1) qmail_fail(&qqt);
682 }
683
684 qmail_from(&qqt,bouncesender);
685 qmail_to(&qqt,bouncerecip);
686
687 if (*qmail_close(&qqt)) {
688 log1s("warning: trouble injecting bounce message, will try later\n");
689 return 0;
690 }
691
692 strnum2[fmt_ulong(strnum2,id)] = 0;
693 log2s("bounce msg ",strnum2);
694 strnum2[fmt_ulong(strnum2,qp)] = 0;
695 log3s(" qp ",strnum2,"\n");
696 }
697
698 if (unlink(fn2.s) != 0) {
699 log3s("warning: unable to unlink ",fn2.s,"\n");
700 return 0;
701 }
702
703 return 1;
704}
705
706
707/* this file is too long ---------------------------------------- DELIVERIES */
708
709struct del {
710 int used;
711 int j;
712 unsigned long delid;
713 seek_pos mpos;
714 stralloc recip;
715};
716
717unsigned long masterdelid = 1;
718unsigned int concurrency[CHANNELS] = { 10, 20 };
719unsigned int concurrencyused[CHANNELS] = { 0, 0 };
720struct del *d[CHANNELS];
721stralloc dline[CHANNELS];
722char delbuf[2048];
723
725{
726 int c;
727
728 log1s("status:");
729 for (c = 0; c < CHANNELS; ++c) {
730 strnum2[fmt_ulong(strnum2,(unsigned long) concurrencyused[c])] = 0;
731 strnum3[fmt_ulong(strnum3,(unsigned long) concurrency[c])] = 0;
733 log2s("/",strnum3);
734 }
735 if (flagexitasap) log1s(" exitasap");
736 log1s("\n");
737}
738
740{
741 int c;
742 int i;
743
744 for (c = 0; c < CHANNELS; ++c) {
745 flagspawnalive[c] = 1;
746 while (!(d[c] = (struct del *) alloc(concurrency[c] * sizeof(struct del))))
747 nomem();
748 for (i = 0; i < concurrency[c]; ++i)
749 { d[c][i].used = 0; d[c][i].recip.s = 0; }
750 dline[c].s = 0;
751 while (!stralloc_copys(&dline[c],"")) nomem();
752 }
753
754 del_status();
755}
756
758{
759 int c;
760
761 for (c = 0; c < CHANNELS; ++c)
762 if (flagspawnalive[c]) /* if dead, nothing we can do about its jobs */
763 if (concurrencyused[c]) return 0;
764 return 1;
765}
766
767int del_avail(int c)
768{
770}
771
772void del_start(int j,seek_pos mpos,char *recip)
773{
774 int i;
775 int c;
776
777 c = jo[j].channel;
778 if (!flagspawnalive[c]) return;
779 if (!comm_canwrite(c)) return;
780
781 for (i = 0; i < concurrency[c]; ++i)
782 if (!d[c][i].used) break;
783 if (i == concurrency[c]) return;
784
785 if (!stralloc_copys(&d[c][i].recip,recip)) { nomem(); return; }
786 if (!stralloc_0(&d[c][i].recip)) { nomem(); return; }
787 d[c][i].j = j; ++jo[j].refs;
788 d[c][i].delid = masterdelid++;
789 d[c][i].mpos = mpos;
790 d[c][i].used = 1; ++concurrencyused[c];
791
792 comm_write(c,i,jo[j].id,jo[j].sender.s,recip);
793
794 strnum2[fmt_ulong(strnum2,d[c][i].delid)] = 0;
795 strnum3[fmt_ulong(strnum3,jo[j].id)] = 0;
796 log2s("starting delivery ",strnum2);
797 log3s(": msg ",strnum3,tochan[c]);
798 logsafe(recip);
799 log1s("\n");
800 del_status();
801}
802
803void markdone(int c,unsigned long id,seek_pos pos)
804{
805 struct stat st;
806 int fd;
807
808 fnmake_chanaddr(id,c);
809
810 for (;;) {
811 fd = open_write(fn.s);
812 if (fd == -1) break;
813 if (fstat(fd,&st) == -1) { close(fd); break; }
814 if (seek_set(fd,pos) == -1) { close(fd); break; }
815 if (write(fd,"D",1) != 1) { close(fd); break; }
816 /* further errors -> double delivery without us knowing about it, oh well */
817 close(fd);
818 return;
819 }
820 log3s("warning: trouble marking ",fn.s,"; message will be delivered twice!\n");
821}
822
823void del_dochan(int c)
824{
825 int r;
826 char ch;
827 int i;
828 int delnum;
829
830 r = read(chanfdin[c],delbuf,sizeof(delbuf));
831 if (r == -1) return;
832 if (r == 0) { spawndied(c); return; }
833
834 for (i = 0; i < r; ++i) {
835 ch = delbuf[i];
836 while (!stralloc_append(&dline[c],&ch)) nomem();
837
838 if (dline[c].len > REPORTMAX)
839 dline[c].len = REPORTMAX;
840 /* qmail-lspawn and qmail-rspawn are responsible for keeping it short */
841 /* but from a security point of view, we don't trust rspawn */
842
843 if (!ch && (dline[c].len > 1)) {
844 delnum = (unsigned int) (unsigned char) dline[c].s[0];
845 if ((delnum < 0) || (delnum >= concurrency[c]) || !d[c][delnum].used)
846 log1s("warning: internal error: delivery report out of range\n");
847 else {
848 strnum3[fmt_ulong(strnum3,d[c][delnum].delid)] = 0;
849 if (dline[c].s[1] == 'Z')
850 if (jo[d[c][delnum].j].flagdying) {
851 dline[c].s[1] = 'D';
852 --dline[c].len;
853 while (!stralloc_cats(&dline[c],"I'm not going to try again; this message has been in the queue too long.\n")) nomem();
854 while (!stralloc_0(&dline[c])) nomem();
855 }
856
857 switch (dline[c].s[1]) {
858 case 'K':
859 log3s("delivery ",strnum3,": success: ");
860 logsafe(dline[c].s + 2);
861 log1s("\n");
862 markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
863 --jo[d[c][delnum].j].numtodo;
864 break;
865 case 'Z':
866 log3s("delivery ",strnum3,": deferral: ");
867 logsafe(dline[c].s + 2);
868 log1s("\n");
869 break;
870 case 'D':
871 log3s("delivery ",strnum3,": failure: ");
872 logsafe(dline[c].s + 2);
873 log1s("\n");
874 addbounce(jo[d[c][delnum].j].id,d[c][delnum].recip.s,dline[c].s + 2);
875 markdone(c,jo[d[c][delnum].j].id,d[c][delnum].mpos);
876 --jo[d[c][delnum].j].numtodo;
877 break;
878 default:
879 log3s("delivery ",strnum3,": report mangled, will defer\n");
880 }
881
882 job_close(d[c][delnum].j);
883 d[c][delnum].used = 0; --concurrencyused[c];
884 del_status();
885
886 }
887 dline[c].len = 0;
888 }
889 }
890}
891
892void del_selprep(int *nfds,fd_set *rfds)
893{
894 int c;
895
896 for (c = 0; c < CHANNELS; ++c)
897 if (flagspawnalive[c]) {
898 FD_SET(chanfdin[c],rfds);
899 if (*nfds <= chanfdin[c])
900 *nfds = chanfdin[c] + 1;
901 }
902}
903
904void del_do(fd_set *rfds)
905{
906 int c;
907
908 for (c = 0; c < CHANNELS; ++c)
909 if (flagspawnalive[c])
910 if (FD_ISSET(chanfdin[c],rfds))
911 del_dochan(c);
912}
913
914
915/* this file is too long -------------------------------------------- PASSES */
916
917struct
918{
919 unsigned long id; /* if 0, need a new pass */
920 int j; /* defined if id; job number */
921 int fd; /* defined if id; reading from {local,remote} */
922 seek_pos mpos; /* defined if id; mark position */
923 buffer b;
924 char buf[128];
925}
927
929{
930 int c;
931
932 for (c = 0; c < CHANNELS; ++c) pass[c].id = 0;
933}
934
936{
937 int c;
938 struct prioq_elt pe;
939 if (flagexitasap) return;
940
941 for (c = 0; c < CHANNELS; ++c)
942 if (pass[c].id)
943 if (del_avail(c))
944 { *wakeup = 0; return; }
945
946 if (job_avail())
947 for (c = 0; c < CHANNELS; ++c)
948 if (!pass[c].id)
949 if (prioq_min(&pqchan[c],&pe))
950 if (*wakeup > pe.dt) *wakeup = pe.dt;
951
952 if (prioq_min(&pqfail,&pe))
953 if (*wakeup > pe.dt)
954 *wakeup = pe.dt;
955
956 if (prioq_min(&pqdone,&pe))
957 if (*wakeup > pe.dt)*wakeup = pe.dt;
958}
959
960static datetime_sec squareroot(datetime_sec x) /* result^2 <= x < (result + 1)^2 ; assuming: >= 0 */
961{
962 datetime_sec y;
963 datetime_sec yy;
964 datetime_sec y21;
965 int j;
966
967 y = 0; yy = 0;
968 for (j = 15; j >= 0; --j) {
969 y21 = (y << (j + 1)) + (1 << (j + j));
970 if (y21 <= x - yy) { y += (1 << j); yy += y21; }
971 }
972 return y;
973}
974
976{
977 int n;
978
979 if (birth > recent) n = 0;
980 else n = squareroot(recent - birth); /* no need to add fuzz to recent */
981
982 n += chanskip[c];
983 return birth + n * n;
984}
985
986void pass_dochan(int c)
987{
989 struct prioq_elt pe;
990 static stralloc line = {0};
991 int match;
992
993 if (flagexitasap) return;
994
995 if (!pass[c].id) {
996 if (!job_avail()) return;
997 if (!prioq_min(&pqchan[c],&pe)) return;
998 if (pe.dt > recent) return;
999 fnmake_chanaddr(pe.id,c);
1000
1002 pass[c].mpos = 0;
1003 pass[c].fd = open_read(fn.s);
1004 if (pass[c].fd == -1) goto trouble;
1005 if (!getinfo(&line,&birth,pe.id)) { close(pass[c].fd); goto trouble; }
1006 pass[c].id = pe.id;
1007 buffer_init(&pass[c].b,read,pass[c].fd,pass[c].buf,sizeof(pass[c].buf));
1008 pass[c].j = job_open(pe.id,c);
1009 jo[pass[c].j].retry = nextretry(birth,c);
1010 jo[pass[c].j].flagdying = (recent > birth + lifetime);
1011 while (!stralloc_copy(&jo[pass[c].j].sender,&line)) nomem();
1012 }
1013
1014 if (!del_avail(c)) return;
1015
1016 if (getln(&pass[c].b,&line,&match,'\0') == -1) {
1017 fnmake_chanaddr(pass[c].id,c);
1018 log3s("warning: trouble reading ",fn.s,"; will try again later\n");
1019 close(pass[c].fd);
1020 job_close(pass[c].j);
1021 pass[c].id = 0;
1022 return;
1023 }
1024
1025 if (!match) {
1026 close(pass[c].fd);
1027 jo[pass[c].j].flaghiteof = 1;
1028 job_close(pass[c].j);
1029 pass[c].id = 0;
1030 return;
1031 }
1032
1033 switch (line.s[0]) {
1034 case 'T':
1035 ++jo[pass[c].j].numtodo;
1036 del_start(pass[c].j,pass[c].mpos,line.s + 1);
1037 break;
1038 case 'D':
1039 break;
1040 default:
1041 fnmake_chanaddr(pass[c].id,c);
1042 log3s("warning: unknown record type in ",fn.s,"!\n");
1043 close(pass[c].fd);
1044 job_close(pass[c].j);
1045 pass[c].id = 0;
1046 return;
1047 }
1048
1049 pass[c].mpos += line.len;
1050 return;
1051
1052 trouble:
1053 log3s("warning: trouble opening ",fn.s,"; will try again later\n");
1054 pe.dt = recent + SLEEP_SYSFAIL;
1055 while (!prioq_insert(&pqchan[c],&pe)) nomem();
1056}
1057
1058void messdone(unsigned long id)
1059{
1060 char ch;
1061 int c;
1062 struct prioq_elt pe;
1063 struct stat st;
1064
1065 for (c = 0; c < CHANNELS; ++c) {
1066 fnmake_chanaddr(id,c);
1067 if (stat(fn.s,&st) == 0) return; /* false alarm; consequence of HOPEFULLY */
1068 if (errno != ENOENT) {
1069 log3s("warning: unable to stat ",fn.s,"; will try again later\n");
1070 goto FAIL;
1071 }
1072 }
1073
1074 fnmake_todo(id);
1075 if (stat(fn.s,&st) == 0) return;
1076 if (errno != ENOENT) {
1077 log3s("warning: unable to stat ",fn.s,"; will try again later\n");
1078 goto FAIL;
1079 }
1080
1081 fnmake_info(id);
1082 if (stat(fn.s,&st) == -1) {
1083 if (errno == ENOENT) return;
1084 log3s("warning: unable to stat ",fn.s,"; will try again later\n");
1085 goto FAIL;
1086 }
1087
1088 /* -todo +info -local -remote ?bounce */
1089 if (!injectbounce(id))
1090 goto FAIL; /* injectbounce() produced error message */
1091
1092 strnum3[fmt_ulong(strnum3,id)] = 0;
1093 log3s("end msg ",strnum3,"\n");
1094
1095 /* -todo +info -local -remote -bounce */
1096 fnmake_info(id);
1097 if (unlink(fn.s) == -1) {
1098 log3s("warning: unable to unlink ",fn.s,"; will try again later\n");
1099 goto FAIL;
1100 }
1101
1102 /* -todo -info -local -remote -bounce; we can relax */
1103 fnmake_foop(id);
1104 if (buffer_putflush(&toqc,fn.s,fn.len) == -1) { cleandied(); return; }
1105 if (buffer_get(&fromqc,&ch,1) != 1) { cleandied(); return; }
1106 if (ch != '+') log3s("warning: qmail-clean unable to clean up ",fn.s,"\n");
1107
1108 return;
1109
1110 FAIL:
1111 pe.id = id; pe.dt = now() + SLEEP_SYSFAIL;
1112 while (!prioq_insert(&pqdone,&pe)) nomem();
1113}
1114
1116{
1117 int c;
1118 struct prioq_elt pe;
1119
1120 for (c = 0; c < CHANNELS; ++c)
1121 pass_dochan(c);
1122
1123 if (prioq_min(&pqfail,&pe))
1124 if (pe.dt <= recent) {
1126 pqadd(pe.id);
1127 }
1128
1129 if (prioq_min(&pqdone,&pe))
1130 if (pe.dt <= recent) {
1132 messdone(pe.id);
1133 }
1134}
1135
1136
1137/* this file is too long ------------------------------------- EXTERNAL TODO */
1138
1139stralloc todoline = {0};
1140char todobuf[2048];
1144
1145void tododied() {
1146 log1s("alert: lost connection to qmail-todo ... exiting\n");
1147 flagexitasap = 1;
1148 flagtodoalive = 0;
1149}
1150
1152{
1153 todofdout = 7;
1154 todofdin = 8;
1155 flagtodoalive = 1;
1156 /* sync with external todo */
1157 if (write(todofdout,"S",1) != 1) tododied();
1158
1159 return;
1160}
1161
1162void todo_selprep(int *nfds,fd_set *rfds,datetime_sec *wakeup)
1163{
1164 if (flagexitasap) {
1165 if (flagtodoalive) {
1166 write(todofdout,"X",1);
1167 }
1168 }
1169 if (flagtodoalive) {
1170 FD_SET(todofdin,rfds);
1171 if (*nfds <= todofdin)
1172 *nfds = todofdin + 1;
1173 }
1174}
1175
1176void todo_del(char* s)
1177{
1178 int flagchan[CHANNELS];
1179 struct prioq_elt pe;
1180 unsigned long id;
1181 unsigned int len;
1182 int c;
1183
1184 for (c = 0; c < CHANNELS; ++c)
1185 flagchan[c] = 0;
1186
1187 switch (*s++) {
1188 case 'L':
1189 flagchan[0] = 1;
1190 break;
1191 case 'R':
1192 flagchan[1] = 1;
1193 break;
1194 case 'B':
1195 flagchan[0] = 1;
1196 flagchan[1] = 1;
1197 break;
1198 case 'X':
1199 break;
1200 default:
1201 log1s("warning: qmail-send unable to understand qmail-todo\n");
1202 return;
1203 }
1204
1205 len = scan_ulong(s,&id);
1206 if (!len || s[len]) {
1207 log1s("warning: qmail-send unable to understand qmail-todo\n");
1208 return;
1209 }
1210
1211 pe.id = id; pe.dt = now();
1212 for (c = 0; c < CHANNELS; ++c)
1213 if (flagchan[c])
1214 while (!prioq_insert(&pqchan[c],&pe)) nomem();
1215
1216 for (c = 0; c < CHANNELS; ++c)
1217 if (flagchan[c]) break;
1218
1219 if (c == CHANNELS)
1220 while (!prioq_insert(&pqdone,&pe)) nomem();
1221
1222 return;
1223}
1224
1225void todo_do(fd_set *rfds)
1226{
1227 int r;
1228 char ch;
1229 int i;
1230
1231 if (!flagtodoalive) return;
1232 if (!FD_ISSET(todofdin,rfds)) return;
1233
1234 r = read(todofdin,todobuf,sizeof(todobuf));
1235 if (r == -1) return;
1236 if (r == 0) {
1237 if (flagexitasap)
1238 flagtodoalive = 0;
1239 else
1240 tododied();
1241 return;
1242 }
1243
1244 for (i = 0; i < r; ++i) {
1245 ch = todobuf[i];
1246 while (!stralloc_append(&todoline,&ch)) nomem();
1247 if (todoline.len > REPORTMAX)
1248 todoline.len = REPORTMAX;
1249 /* qmail-todo is responsible for keeping it short */
1250 if (!ch && (todoline.len > 1)) {
1251 switch (todoline.s[0]) {
1252 case 'D':
1253 if (flagexitasap) break;
1254 todo_del(todoline.s + 1);
1255 break;
1256 case 'L':
1257 log1s(todoline.s + 1);
1258 break;
1259 case 'X':
1260 if (flagexitasap)
1261 flagtodoalive = 0;
1262 else
1263 tododied();
1264 break;
1265 default:
1266 log1s("warning: qmail-send unable to understand qmail-todo: report mangled\n");
1267 break;
1268 }
1269 todoline.len = 0;
1270 }
1271 }
1272}
1273
1274/* this file is too long ---------------------------------------------- MAIN */
1275
1277{
1278 if (control_init() == -1) return 0;
1279 if (control_readint(&lifetime,"control/queuelifetime") == -1) return 0;
1280 if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) return 0;
1281 if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) return 0;
1282 if (control_rldef(&envnoathost,"control/envnoathost",1,"envnoathost") != 1) return 0;
1283 if (control_rldef(&bouncefrom,"control/bouncefrom",0,"MAILER-DAEMON") != 1) return 0;
1284 if (control_rldef(&bouncehost,"control/bouncehost",1,"bouncehost") != 1) return 0;
1285 if (control_readint(&bouncemaxbytes,"control/bouncemaxbytes") == -1) return 0;
1286 if (control_rldef(&doublebouncehost,"control/doublebouncehost",1,"doublebouncehost") != 1) return 0;
1287 if (control_rldef(&doublebounceto,"control/doublebounceto",0,"postmaster") != 1) return 0;
1288 if (!stralloc_cats(&doublebounceto,"@")) return 0;
1289 if (!stralloc_cat(&doublebounceto,&doublebouncehost)) return 0;
1290 if (!stralloc_0(&doublebounceto)) return 0;
1291 if (control_readfile(&locals,"control/locals",1) != 1) return 0;
1292 if (!constmap_init(&maplocals,locals.s,locals.len,0)) return 0;
1293 switch (control_readfile(&percenthack,"control/percenthack",0)) {
1294 case -1: return 0;
1295 case 0: if (!constmap_init(&mappercenthack,"",0,0)) return 0; break;
1296 case 1: if (!constmap_init(&mappercenthack,percenthack.s,percenthack.len,0)) return 0; break;
1297 }
1298 switch (control_readfile(&vdoms,"control/virtualdomains",0)) {
1299 case -1: return 0;
1300 case 0: if (!constmap_init(&mapvdoms,"",0,1)) return 0; break;
1301 case 1: if (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) return 0; break;
1302 }
1303 return 1;
1304}
1305
1306stralloc newlocals = {0};
1307stralloc newvdoms = {0};
1308
1310{
1311 int r;
1312
1313 if (control_readfile(&newlocals,"control/locals",1) != 1) { log1s("alert: unable to reread control/locals\n"); return; }
1314 if (control_readint(&concurrency[0],"control/concurrencylocal") == -1) { log1s("alert: unable to reread control/concurrencylocal\n"); return; }
1315 if (control_readint(&concurrency[1],"control/concurrencyremote") == -1) { log1s("alert: unable to reread control/concurrencyremote\n"); return; }
1316 if (control_readint(&lifetime,"control/queuelifetime") == -1) { log1s("alert: unable to reread control/queuelifetime\n"); return; }
1317
1318 r = control_readfile(&newvdoms,"control/virtualdomains",0);
1319 if (r == -1) { log1s("alert: unable to reread control/virtualdomains\n"); return; }
1320
1323
1324 while (!stralloc_copy(&locals,&newlocals)) nomem();
1325 while (!constmap_init(&maplocals,locals.s,locals.len,0)) nomem();
1326
1327 if (r) {
1328 while (!stralloc_copy(&vdoms,&newvdoms)) nomem();
1329 while (!constmap_init(&mapvdoms,vdoms.s,vdoms.len,1)) nomem();
1330 } else
1331 while (!constmap_init(&mapvdoms,"",0,1)) nomem();
1332}
1333
1335{
1336 if (chdir(auto_qmail) == -1) {
1337 log1s("alert: unable to reread controls: unable to switch to home directory\n");
1338 return;
1339 }
1340 write(todofdout,"H",1);
1341 regetcontrols();
1342
1343 while (chdir("queue") == -1) {
1344 log1s("alert: unable to switch back to queue directory; HELP! sleeping...\n");
1345 sleep(10);
1346 }
1347}
1348
1349int main()
1350{
1351 int fd;
1352 datetime_sec wakeup;
1353 fd_set rfds;
1354 fd_set wfds;
1355 int nfds;
1356 struct timeval tv;
1357 int c;
1358 int u;
1359 int r;
1360 char ch;
1361
1362 if (chdir(auto_qmail) == -1) { log1s("alert: cannot start: unable to switch to home directory\n"); _exit(110); }
1363 if (!getcontrols()) { log1s("alert: cannot start: unable to read controls\n"); _exit(111); }
1364 if (chdir("queue") == -1) { log1s("alert: cannot start: unable to switch to queue directory\n"); _exit(110); }
1365 sig_pipeignore();
1366 sig_termcatch(sigterm);
1367 sig_alarmcatch(sigalrm);
1368 sig_hangupcatch(sighup);
1369 sig_childdefault();
1370 umask(077);
1371
1372 fd = open_write("lock/sendmutex");
1373 if (fd == -1) { log1s("alert: cannot start: unable to open mutex\n"); _exit(111); }
1374 if (lock_exnb(fd) == -1) { log1s("alert: cannot start: qmail-send is already running\n"); _exit(111); }
1375
1376 numjobs = 0;
1377 for (c = 0;c < CHANNELS;++c) {
1378 do
1379 r = read(chanfdin[c],&ch,1);
1380
1381 while ((r == -1) && (errno == EINTR));
1382 if (r < 1) { log1s("alert: cannot start: hath the daemon spawn no fire?\n"); _exit(111); }
1383
1384 u = (unsigned int) (unsigned char) ch;
1385 if (concurrency[c] > u) concurrency[c] = u;
1386 numjobs += concurrency[c];
1387 }
1388
1389 fnmake_init();
1390
1391 comm_init();
1392
1393 pqstart();
1394 job_init();
1395 del_init();
1396 pass_init();
1397 todo_init();
1398 cleanup_init();
1399
1400 while (!flagexitasap || !del_canexit() || flagtodoalive) {
1401 recent = now();
1402
1403 if (flagrunasap) { flagrunasap = 0; pqrun(); }
1404 if (flagreadasap) { flagreadasap = 0; reread(); }
1405
1406 wakeup = recent + SLEEP_FOREVER;
1407 FD_ZERO(&rfds);
1408 FD_ZERO(&wfds);
1409 nfds = 1;
1410
1411 comm_selprep(&nfds,&wfds);
1412 del_selprep(&nfds,&rfds);
1413 pass_selprep(&wakeup);
1414 todo_selprep(&nfds,&rfds,&wakeup);
1415 cleanup_selprep(&wakeup);
1416
1417 if (wakeup <= recent) tv.tv_sec = 0;
1418 else tv.tv_sec = wakeup - recent + SLEEP_FUZZ;
1419 tv.tv_usec = 0;
1420
1421 if (select(nfds,&rfds,&wfds,(fd_set *) 0,&tv) == -1)
1422 if (errno == EINTR)
1423 ;
1424 else
1425 log1s("warning: trouble in select\n");
1426 else {
1427 recent = now();
1428
1429 comm_do(&wfds);
1430 del_do(&rfds);
1431 todo_do(&rfds);
1432 pass_do();
1433 cleanup_do();
1434 }
1435 }
1436
1437 pqfinish();
1438 log1s("status: exiting\n");
1439 _exit(0);
1440}
char inbuf[256]
Definition: auto-gid.c:9
char auto_qmail[]
void nomem()
Definition: columnt.c:16
struct qmail qqt
Definition: condredirect.c:14
buffer bi
Definition: condredirect.c:23
int constmap_init(struct constmap *cm, char *s, int len, int flagcolon)
Definition: constmap.c:35
void constmap_free()
int control_readint(int *i, char *fn)
Definition: control.c:71
int control_rldef(stralloc *sa, char *fn, int flagme, char *def)
Definition: control.c:41
int control_readfile(stralloc *sa, char *fn, int flagme)
Definition: control.c:86
int control_init(void)
Definition: control.c:32
long datetime_sec
Definition: datetime.h:15
int stralloc_copys(stralloc *, char const *)
stralloc sa
Definition: dnscname.c:11
void _exit()
char qp[FMT_ULONG]
Definition: fastforward.c:53
stralloc sender
Definition: fastforward.c:71
unsigned int fmtqfn(char *s, char *dirslash, unsigned long id, int flagsplit)
Definition: fmtqfn.c:5
#define FMTQFN
Definition: fmtqfn.h:6
void c(char *, char *, char *, int, int, int)
Definition: install.c:57
void p(char *, char *, int, int, int)
Definition: install.c:39
stralloc line
Definition: maildir2mbox.c:27
ulongalloc birth
Definition: matchup.c:63
int match
Definition: matchup.c:195
stralloc newfield_date
Definition: newfield.c:9
int newfield_datemake()
datetime_sec now()
Definition: now.c:5
void prioq_delmin()
int prioq_min()
struct utmpx * ut
Definition: qbiff.c:39
int
Definition: qmail-mrtg.c:26
readsubdir rs
Definition: qmail-qread.c:17
struct datetime dt
Definition: qmail-queue.c:33
stralloc recip
Definition: qmail-remote.c:99
char toqcbuf[1024]
Definition: qmail-send.c:169
datetime_sec recent
Definition: qmail-send.c:87
char fnmake_strnum[FMT_ULONG]
Definition: qmail-send.c:94
prioq pqdone
Definition: qmail-send.c:309
void pqfinish()
Definition: qmail-send.c:373
char strnum2[FMT_ULONG]
Definition: qmail-send.c:55
void cleandied()
Definition: qmail-send.c:70
void pqadd(unsigned long id)
Definition: qmail-send.c:315
void todo_init()
Definition: qmail-send.c:1151
prioq pqfail
Definition: qmail-send.c:313
#define CHANNELS
Definition: qmail-send.c:58
int flagreadasap
Definition: qmail-send.c:68
int del_avail(int c)
Definition: qmail-send.c:767
void fnmake_todo(unsigned long id)
Definition: qmail-send.c:103
void fnmake_foop(unsigned long id)
Definition: qmail-send.c:105
void messdone(unsigned long id)
Definition: qmail-send.c:1058
void fnmake_info(unsigned long id)
Definition: qmail-send.c:102
int job_open(unsigned long id, int channel)
Definition: qmail-send.c:439
buffer toqc
Definition: qmail-send.c:169
stralloc fn
Definition: qmail-send.c:92
unsigned int concurrency[CHANNELS]
Definition: qmail-send.c:718
void sigalrm()
Definition: qmail-send.c:67
int getinfo(stralloc *sa, datetime_sec *dt, unsigned long id)
Definition: qmail-send.c:141
int j
Definition: qmail-send.c:920
stralloc dline[CHANNELS]
Definition: qmail-send.c:721
void markdone(int c, unsigned long id, seek_pos pos)
Definition: qmail-send.c:803
stralloc bouncehost
Definition: qmail-send.c:51
stralloc doublebouncehost
Definition: qmail-send.c:53
char * chanaddr[CHANNELS]
Definition: qmail-send.c:59
void pqstart()
Definition: qmail-send.c:361
int bouncemaxbytes
Definition: qmail-send.c:41
stralloc vdoms
Definition: qmail-send.c:47
void fnmake_split(unsigned long id)
Definition: qmail-send.c:106
int getcontrols()
Definition: qmail-send.c:1276
int flagexitasap
Definition: qmail-send.c:66
void pass_do()
Definition: qmail-send.c:1115
struct job * jo
Definition: qmail-send.c:417
void cleanup_selprep(datetime_sec *wakeup)
Definition: qmail-send.c:263
void tododied()
Definition: qmail-send.c:1145
void todo_del(char *s)
Definition: qmail-send.c:1176
void pass_dochan(int c)
Definition: qmail-send.c:986
int injectbounce(unsigned long id)
Definition: qmail-send.c:567
void addbounce(unsigned long id, char *recip, char *report)
Definition: qmail-send.c:518
void job_close(int j)
Definition: qmail-send.c:454
void regetcontrols()
Definition: qmail-send.c:1309
stralloc envnoathost
Definition: qmail-send.c:49
buffer b
Definition: qmail-send.c:923
stralloc todoline
Definition: qmail-send.c:1139
int flagrunasap
Definition: qmail-send.c:67
int fd
Definition: qmail-send.c:921
stralloc locals
Definition: qmail-send.c:45
void spawndied(int c)
Definition: qmail-send.c:78
void cleanup_do()
Definition: qmail-send.c:269
void del_status()
Definition: qmail-send.c:724
char todobuf[2048]
Definition: qmail-send.c:1140
stralloc percenthack
Definition: qmail-send.c:43
struct @1 pass[CHANNELS]
stralloc bouncetext
Definition: qmail-send.c:516
stralloc fn2
Definition: qmail-send.c:93
struct constmap mappercenthack
Definition: qmail-send.c:44
void del_do(fd_set *rfds)
Definition: qmail-send.c:904
seek_pos mpos
Definition: qmail-send.c:922
void del_init()
Definition: qmail-send.c:739
void comm_write(int c, int delnum, unsigned long id, char *sender, char *recip)
Definition: qmail-send.c:193
int chanfdin[CHANNELS]
Definition: qmail-send.c:63
datetime_sec cleanuptime
Definition: qmail-send.c:255
int job_avail()
Definition: qmail-send.c:430
int chanskip[CHANNELS]
Definition: qmail-send.c:64
void sighup()
Definition: qmail-send.c:68
void fnmake_mess(unsigned long id)
Definition: qmail-send.c:104
void todo_do(fd_set *rfds)
Definition: qmail-send.c:1225
void pass_selprep(datetime_sec *wakeup)
Definition: qmail-send.c:935
stralloc doublebounceto
Definition: qmail-send.c:52
int flagspawnalive[CHANNELS]
Definition: qmail-send.c:76
int flagcleanup
Definition: qmail-send.c:253
void comm_do(fd_set *wfds)
Definition: qmail-send.c:224
void comm_selprep(int *nfds, fd_set *wfds)
Definition: qmail-send.c:211
stralloc newlocals
Definition: qmail-send.c:1306
int del_canexit()
Definition: qmail-send.c:757
char fromqcbuf[1024]
Definition: qmail-send.c:170
char buf[128]
Definition: qmail-send.c:924
char strnum3[FMT_ULONG]
Definition: qmail-send.c:56
void comm_init()
Definition: qmail-send.c:174
buffer fromqc
Definition: qmail-send.c:170
void todo_selprep(int *nfds, fd_set *rfds, datetime_sec *wakeup)
Definition: qmail-send.c:1162
void reread()
Definition: qmail-send.c:1334
stralloc newvdoms
Definition: qmail-send.c:1307
void job_init()
Definition: qmail-send.c:419
void senderadd(stralloc *sa, char *sender, char *recip)
Definition: qmail-send.c:113
struct del * d[CHANNELS]
Definition: qmail-send.c:720
readsubdir cleanupdir
Definition: qmail-send.c:254
char * stripvdomprepend(char *recip)
Definition: qmail-send.c:491
char * tochan[CHANNELS]
Definition: qmail-send.c:61
unsigned long masterdelid
Definition: qmail-send.c:717
void del_start(int j, seek_pos mpos, char *recip)
Definition: qmail-send.c:772
void pqrun()
Definition: qmail-send.c:389
char * chanstatusmsg[CHANNELS]
Definition: qmail-send.c:60
datetime_sec nextretry(datetime_sec birth, int c)
Definition: qmail-send.c:975
int comm_pos[CHANNELS]
Definition: qmail-send.c:172
struct constmap maplocals
Definition: qmail-send.c:46
unsigned int concurrencyused[CHANNELS]
Definition: qmail-send.c:719
stralloc comm_buf[CHANNELS]
Definition: qmail-send.c:171
void fnmake_init()
Definition: qmail-send.c:96
prioq pqchan[CHANNELS]
Definition: qmail-send.c:310
int lifetime
Definition: qmail-send.c:40
void del_dochan(int c)
Definition: qmail-send.c:823
int numjobs
Definition: qmail-send.c:416
void fnmake_chanaddr(unsigned long id, int c)
Definition: qmail-send.c:108
void pass_init()
Definition: qmail-send.c:928
stralloc bouncefrom
Definition: qmail-send.c:50
int main()
Definition: qmail-send.c:1349
void cleanup_init()
Definition: qmail-send.c:257
int todofdout
Definition: qmail-send.c:1142
#define REPORTMAX
Definition: qmail-send.c:85
unsigned long id
Definition: qmail-send.c:919
void del_selprep(int *nfds, fd_set *rfds)
Definition: qmail-send.c:892
char delbuf[2048]
Definition: qmail-send.c:722
struct constmap mapvdoms
Definition: qmail-send.c:48
void fnmake2_bounce(unsigned long id)
Definition: qmail-send.c:107
int chanfdout[CHANNELS]
Definition: qmail-send.c:62
#define CHECKSTAT
int todofdin
Definition: qmail-send.c:1141
int flagtodoalive
Definition: qmail-send.c:1143
void sigterm()
Definition: qmail-send.c:66
int comm_canwrite(void)
Definition: qmail-todo.c:192
void qmail_to(struct qmail *, char *)
Definition: qmail.c:83
void qmail_from(struct qmail *, char *)
Definition: qmail.c:73
void qmail_put(struct qmail *, char *, int)
Definition: qmail.c:63
char * qmail_close(struct qmail *)
Definition: qmail.c:90
unsigned long qmail_qp(struct qmail *)
Definition: qmail.c:53
void qmail_puts(struct qmail *, char *)
Definition: qmail.c:68
int qmail_open(struct qmail *)
Definition: qmail.c:21
void qmail_fail(struct qmail *)
Definition: qmail.c:58
stralloc quoted
Definition: qreceipt.c:63
void log1s(char *)
Definition: qsutil.c:17
void logsafe(char *)
Definition: qsutil.c:72
void pausedir(char *)
Definition: qsutil.c:58
void log2s(char *, char *)
Definition: qsutil.c:22
void log3s(char *, char *, char *)
Definition: qsutil.c:28
int quote(stralloc *, stralloc *)
Definition: quote.c:62
int quote2(stralloc *, char *)
Definition: quote.c:70
int readsubdir_next()
void readsubdir_init()
#define SLEEP_FOREVER
Definition: sendtodo.h:9
#define SLEEP_CLEANUP
Definition: sendtodo.h:10
#define SLEEP_SYSFAIL
Definition: sendtodo.h:11
#define OSSIFIED
Definition: sendtodo.h:12
#define SLEEP_FUZZ
Definition: sendtodo.h:8
uint32_t k[64]
Definition: sha256.c:26
int delnum
Definition: spawn.c:72
void report()
stralloc domain
Definition: spf.c:34
stralloc recip
Definition: qmail-send.c:714
int j
Definition: qmail-send.c:711
int used
Definition: qmail-send.c:710
seek_pos mpos
Definition: qmail-send.c:713
unsigned long delid
Definition: qmail-send.c:712
datetime_sec retry
Definition: qmail-send.c:409
int refs
Definition: qmail-send.c:406
int numtodo
Definition: qmail-send.c:411
stralloc sender
Definition: qmail-send.c:410
int flagdying
Definition: qmail-send.c:413
int channel
Definition: qmail-send.c:408
int flaghiteof
Definition: qmail-send.c:412
unsigned long id
Definition: qmail-send.c:407
Definition: prioq.h:7
datetime_sec dt
Definition: prioq.h:7
unsigned long id
Definition: prioq.h:7
Definition: qmail.h:6
void write()