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