]> mj.ucw.cz Git - maildups.git/blob - smail.c
Teach mparse to handle maildirs
[maildups.git] / smail.c
1 /*
2  *      Sendmail Wrapper :)
3  *
4  *      (c) 2006 Martin Mares <mj@ucw.cz>
5  */
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <getopt.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <time.h>
14 #include <sys/wait.h>
15
16 #include "util.h"
17
18 const char progname[] = "smail";
19
20 static char *local_user;
21 static char *local_domain;
22 static char *from_user;
23 static char *from_domain;
24
25 static time_t now;
26 static struct tm *now_tm;
27
28 static pid_t sm_pid;
29 static FILE *sm_fh;
30
31 static void
32 start_sendmail(char **oargv, int oargc)
33 {
34   char **argv = xmalloc(sizeof(char *) * (oargc+4));
35   int argc = 0;
36   argv[argc++] = "/usr/sbin/sendmail";
37
38   char from[strlen(from_user) + strlen(from_domain) + 16];
39   if (from_user)
40     {
41       sprintf(from, "%s+f-%04d%02d%02d@%s", from_user, now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday, from_domain);
42       argv[argc++] = "-f";
43       argv[argc++] = from;
44       verb(1, "Using -f %s", from);
45     }
46
47   memcpy(argv+argc, oargv, sizeof(char *) * oargc);
48   argc += oargc;
49   argv[argc] = NULL;
50
51   // for (uns i=0; argv[i]; i++) verb(2, "Option: <%s>", argv[i]);
52
53   int pfd[2];
54   if (pipe(pfd) < 0)
55     die("pipe failed: %m");
56
57   sm_pid = fork();
58   if (sm_pid < 0)
59     die("fork failed: %m");
60   if (!sm_pid)
61     {
62       dup2(pfd[0], 0);
63       close(pfd[0]);
64       close(pfd[1]);
65       execvp(argv[0], argv);
66       die("Cannot exec %s: %m", argv[0]);
67     }
68   close(pfd[0]);
69   sm_fh = fdopen(pfd[1], "w");
70   if (!sm_fh)
71     die("fdopen() failed: %m");
72   verb(1, "Forked pid %d for %s", sm_pid, argv[0]);
73 }
74
75 static void
76 close_sendmail(void)
77 {
78   if (ferror(sm_fh) || fclose(sm_fh))
79     die("Error occured when writing to sendmail pipe");
80   for (;;)
81     {
82       int st;
83       pid_t p = wait(&st);
84       if (p < 0)
85         die("wait failed: %m");
86       if (p == sm_pid)
87         {
88           verb(1, "Subprocess %d finished with status 0x%x", p, st);
89           if (WIFEXITED(st))
90             {
91               if (WEXITSTATUS(st))
92                 die("Sendmail process failed with exit code %d", WEXITSTATUS(st));
93               return;
94             }
95           if (WIFSIGNALED(st))
96             die("Sendmail process died on signal %d", WIFSIGNALED(st));
97           die("Sendmail process died with unknown status 0x%x", st);
98         }
99     }
100 }
101
102 static void
103 process_header(void)
104 {
105   char hdr[1024];
106
107   for (;;)
108     {
109       if (!fgets(hdr, sizeof(hdr), stdin))
110         die("Malformed mail headers: unexpected EOF");
111       char *nl = strchr(hdr, '\n');
112       if (!nl)
113         die("Malformed mail headers: line too long");
114       if (hdr[0] == '\r' || hdr[0] == '\n')
115         break;
116       if (strncasecmp(hdr, "Message-ID:", 11))
117         {
118           fputs(hdr, sm_fh);
119           *nl = 0;
120         }
121     }
122
123   if (local_user)
124     {
125       char hostname[64];
126       if (gethostname(hostname, sizeof(hostname)))
127         hostname[0] = 0;
128       else
129         hostname[sizeof(hostname)-1] = 0;
130       fprintf(sm_fh, "Message-ID: <%s+md-%04d%02d%02d.%02d%02d%02d.%d.%s@%s>\n",
131               local_user,
132               now_tm->tm_year + 1900, now_tm->tm_mon + 1, now_tm->tm_mday,
133               now_tm->tm_hour, now_tm->tm_min, now_tm->tm_sec,
134               getpid(), hostname,
135               local_domain);
136     }
137   fputs("\n", sm_fh);
138 }
139
140 static void
141 process_body(void)
142 {
143   char buf[1024];
144   int n;
145   while ((n = fread(buf, 1, sizeof(buf), stdin)) > 0)
146     fwrite(buf, 1, n, sm_fh);
147   if (n < 0)
148     die("Malformed mail body: %m");
149 }
150
151 static void NONRET
152 usage(void)
153 {
154   fprintf(stderr, "Usage: smail [<options>] -- <sendmail-options>\n\
155 \n\
156 Options:\n\
157 -f<user>@<domain>\tGenerate envelope sender address for easy identification of bounces\n\
158 -l<user>@<domain>\tAdd Message-ID for detection of messages looped back\n\
159 -v\t\t\tIncrease verbosity\n\
160 \n\
161 MailDups " STR(VERSION) ", (c) " STR(YEAR) " Martin Mares <mj@ucw.cz>\n\
162 It can be freely distributed and used according to the GNU GPL v2.\n\
163 ");
164   exit(1);
165 }
166
167 int
168 main(int argc, char **argv)
169 {
170   int c;
171   while ((c = getopt(argc, argv, "f:l:v")) >= 0)
172     switch (c)
173       {
174       case 'f':
175         {
176           char *c = strchr(optarg, '@');
177           if (!c)
178             usage();
179           *c++ = 0;
180           from_user = optarg;
181           from_domain = c;
182           break;
183         }
184       case 'l':
185         {
186           char *c = strchr(optarg, '@');
187           if (!c)
188             usage();
189           *c++ = 0;
190           local_user = optarg;
191           local_domain = c;
192           break;
193         }
194       case 'v':
195         verbose++;
196         break;
197       default:
198         usage();
199       }
200
201   now = time(NULL);
202   if (!(now_tm = gmtime(&now)))
203     die("gmtime() failed: %m");
204
205   start_sendmail(argv+optind, argc-optind);
206   process_header();
207   process_body();
208   close_sendmail();
209   return 0;
210 }