2010-06-04 Jb Evain <jbevain@novell.com>
[mcs.git] / tools / tinderbox / smtp.c
bloba4f0b03c208f2549c5e4be6ead288b47ee489c67
1 // simple tool for sending smtp messages
2 //
3 //
4 #include <stdio.h>
5 #include <sys/socket.h>
6 #include <arpa/inet.h>
7 #include <string.h>
8 #include <netdb.h>
10 #include <unistd.h>
11 #include <stdlib.h>
12 #define _GNU_SOURCE
13 #include <getopt.h>
14 #include <ctype.h>
15 #include <time.h>
17 typedef int bool;
18 enum { false = 0, true = 1};
20 struct option rgOptions [] =
22 {"cc", required_argument, NULL, 'c'},
23 {"help", required_argument, NULL, '?'},
24 {"to", required_argument, NULL, 't'},
25 {"from", required_argument, NULL, 'f'},
26 {"message", required_argument, NULL, 'm'},
27 {"subject", required_argument, NULL, 's'},
28 {"host", required_argument, NULL, 'h'},
29 {"attach", required_argument, NULL, 'a'},
32 typedef struct MailFields
34 const char *szTo;
35 const char *szFrom;
36 const char *szHost;
37 const char *szSubject;
38 bool fAttachments;
39 bool fCC;
40 int cArgs;
41 char **rgszArgs;
42 FILE *pfMsg;
43 } MailFields;
45 const char *szOptions = "t:f:s:h:c:a:m:?";
47 void help ()
49 printf (
50 "Usage: smtp [OPTIONS]\n\n"
51 "Mandatory arguments:\n"
52 "\t-t, --to ADDRESS\tspecify destination email address\n"
53 "\t-f, --from ADDRESS\tspecify sender's email address\n"
54 "Optional arguments:\n"
55 "\t-s, --subject SUBJECT\tspecify subject of message\n"
56 "\t-m, --message FILENAME\tread text of message from FILENAME\n"
57 "\t-a, --attach FILENAME\tadd FILENAME to message as attachment\n"
58 "\t-c, --cc ADDRESS\tadd ADDRESS to CC list\n"
59 "\t-h, --host HOSTNAME\tconnect to smpt server HOSTNAME (default: localhost)\n"
63 int GetResponse (FILE *ps)
65 char szLine [1024];
66 char *psz;
67 int hr;
69 fflush (ps);
73 fgets (szLine, sizeof (szLine), ps);
75 for (psz = szLine; isdigit (*psz); psz++)
78 while (*psz != '\0' && *psz != ' ');
80 hr = atol (szLine);
81 return hr;
84 FILE *TcpOpen (const char *szHost, int nPort)
86 int s;
87 struct sockaddr_in sa;
88 struct hostent *he;
89 struct protoent *pe;
90 FILE *ps;
92 pe = getprotobyname ("TCP");
93 s = socket (PF_INET, SOCK_STREAM, pe->p_proto);
94 endprotoent ();
96 bzero ((char *)&sa,sizeof (sa));
97 sa.sin_family = AF_INET;
98 sa.sin_addr.s_addr = inet_addr (szHost);
99 sa.sin_port = htons (25);
101 if ((he = gethostbyname (szHost)) != NULL)
102 bcopy (he->h_addr, (char *)&sa.sin_addr, he->h_length);
103 else if ((sa.sin_addr.s_addr = inet_addr (szHost)) < 0)
104 perror ("gethostbyname ()");
106 if (connect (s, (struct sockaddr *) &sa, 16) == -1)
107 perror ("connect ()");
108 else if ((ps = fdopen (s, "r+")) == NULL)
109 perror ("fdopen ()");
110 else
111 return ps;
113 close (s);
114 return NULL;
118 void SendMail (const char *szTo, const MailFields *pmf)
120 char rgchBoundary [20];
121 FILE *ps;
122 int hr;
124 ps = TcpOpen (pmf->szHost, 25);
126 hr = GetResponse (ps);
128 fprintf (ps, "HELO\r\n");
129 hr = GetResponse (ps);
131 fprintf (ps, "MAIL FROM: %s\r\n", pmf->szFrom);
132 hr = GetResponse (ps);
134 fprintf (ps, "RCPT TO: %s\r\n", szTo);
135 hr = GetResponse (ps);
137 fprintf (ps, "DATA %s\r\n", pmf->szSubject);
138 hr = GetResponse (ps);
140 fprintf (ps, "From: %s\r\nTo: %s\r\nSubject: %s\r\n", pmf->szFrom, pmf->szTo, pmf->szSubject);
142 if (pmf->fCC)
144 bool fFirst = true;
145 int nOpt, iOpt;
146 fprintf (ps, "CC:");
147 optind = 0;
148 while ((nOpt = getopt_long (pmf->cArgs, pmf->rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
150 if (nOpt == 'c')
152 if (!fFirst)
153 fprintf (ps, ";");
154 fprintf (ps, " %s", optarg);
155 fFirst = false;
158 fprintf (ps, "\r\n");
161 if (pmf->fAttachments)
163 int ich;
164 srand ((int) time (NULL));
165 for (ich = 0; ich < sizeof (rgchBoundary) - 1; ich ++)
166 rgchBoundary [ich] = rand () % ('Z'-'A') + 'A';
167 rgchBoundary [ich] = '\0';
169 fprintf (ps,
170 "MIME-Version: 1.0\r\n"
171 "Content-Type: multipart/mixed; boundary=\"multipart-%s\"\r\n\r\n", rgchBoundary);
173 fprintf (ps, "--multipart-%s\r\n\r\n", rgchBoundary);
176 if (pmf->pfMsg != NULL)
178 rewind (pmf->pfMsg);
179 while (!feof (pmf->pfMsg))
181 char strLine [1024];
182 int cch;
184 fgets (strLine, sizeof (strLine), pmf->pfMsg);
185 cch = strlen (strLine);
186 while (strLine [cch - 1] == '\r' || strLine [cch - 1] == '\n')
187 cch --;
188 strLine [cch] = '\0';
190 if (cch == 1 && strLine [0] == '.')
191 fputc ('.', ps);
193 fprintf (ps, "%s\r\n", strLine);
194 fflush (ps);
198 if (pmf->fAttachments)
200 int nOpt, iOpt;
201 optind = 0;
202 while ((nOpt = getopt_long (pmf->cArgs, pmf->rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
204 if (nOpt == 'a')
206 const char rgchBase64 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
207 FILE *pf = fopen (optarg, "r");
208 int cch = 0;
210 const char *szBasename = (char *) strrchr ((const char *) optarg, '/');
211 if (szBasename == NULL || szBasename [1] == '\0')
212 szBasename = optarg;
213 else
214 szBasename ++;
216 fprintf (ps, "--multipart-%s\r\nContent-Type: text/plain; name=\"%s\"\r\nContent-Disposition: attachment; name=\"%s\"\r\nContent-Transfer-Encoding: base64\r\n\r\n", rgchBoundary, szBasename, szBasename);
218 while (true)
220 int ch1, ch2, ch3;
221 if ((ch1 = fgetc (pf)) == -1)
222 break;
224 fputc (rgchBase64 [ch1 >> 2], ps);
226 if ((ch2 = fgetc (pf)) == -1)
228 fputc (rgchBase64 [(ch1 << 4) & 0x30], ps);
229 fputc ('=', ps);
230 fputc ('=', ps);
231 break;
234 fputc (rgchBase64 [(ch2 >> 4) | ((ch1 << 4) & 0x30)], ps);
236 if ((ch3 = fgetc (pf)) == -1)
238 fputc (rgchBase64 [(ch2 << 2) & 0x3c], ps);
239 fputc ('=', ps);
240 break;
243 fputc (rgchBase64 [((ch2 << 2) & 0x3c) | (ch3 >> 6)], ps);
244 fputc (rgchBase64 [ch3 & 0x3f], ps);
246 cch += 4;
247 if (cch >= 76)
248 fprintf (ps, "\r\n");
250 fclose (pf);
251 fprintf (ps, "\r\n\r\n");
256 fprintf (ps, "\r\n.\r\nQUIT\r\n");
257 hr = GetResponse (ps);
259 fclose (ps);
262 int main (int cArgs, char *rgszArgs [])
264 int nOpt, iOpt = 0;
265 MailFields mf;
266 bzero ((char *) &mf, sizeof (mf));
267 mf.cArgs = cArgs;
268 mf.rgszArgs = rgszArgs;
270 while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
272 switch (nOpt)
274 case 't':
275 if (mf.szTo != NULL)
276 goto _default;
277 mf.szTo = optarg;
278 break;
279 case 'f':
280 if (mf.szFrom != NULL)
281 goto _default;
282 mf.szFrom = optarg;
283 break;
284 case 's':
285 if (mf.szSubject != NULL)
286 goto _default;
287 mf.szSubject = optarg;
288 break;
289 case 'h':
290 if (mf.szHost != NULL)
291 goto _default;
292 mf.szHost = optarg;
293 break;
294 case 'c':
295 mf.fCC = true;
296 break;
297 case 'a':
299 FILE *pfTmp = fopen (optarg, "r");
300 mf.fAttachments = true;
301 if (pfTmp == NULL)
303 fprintf (stderr, "File not found: %s\n", optarg);
304 exit (1);
306 fclose (pfTmp);
308 break;
309 case 'm':
310 if (mf.pfMsg != NULL)
311 goto _default;
313 mf.pfMsg = fopen (optarg, "r");
314 if (mf.pfMsg == NULL)
316 fprintf (stderr, "File not found: %s\n", optarg);
317 exit (1);
319 break;
320 case '?':
321 default: _default:
322 printf ("%c: %i\n", nOpt, iOpt);
323 help ();
324 return 1;
328 if (mf.szHost == NULL)
329 mf.szHost = "localhost";
331 if (mf.szSubject == NULL)
332 mf.szSubject = "";
334 if (mf.szTo == NULL)
336 perror ("must specify 'to' field");
337 help ();
338 return 1;
341 if (mf.szFrom == NULL)
343 perror ("must specify 'from' field");
344 help ();
345 return 1;
348 SendMail (mf.szTo, &mf);
350 if (mf.fCC)
352 optind = 0;
353 while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
355 if (nOpt == 'c')
357 int optindTmp = optind;
358 SendMail (optarg, &mf);
359 optind = optindTmp;
364 if (mf.pfMsg != NULL)
365 fclose (mf.pfMsg);
367 return 0;