URL moved and use the raw format for certdata
[mono.git] / mcs / tools / tinderbox / smtp.c
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>
9
10 #include <unistd.h>
11 #include <stdlib.h>
12 #define _GNU_SOURCE
13 #include <getopt.h>
14 #include <ctype.h>
15 #include <time.h>
16
17 typedef int bool;
18 enum { false = 0, true = 1};
19
20 struct option rgOptions [] =
21 {
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'},
30 };
31
32 typedef struct MailFields
33 {
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;
44
45 const char *szOptions = "t:f:s:h:c:a:m:?";
46
47 void help ()
48 {
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"
60         );
61 }
62
63 int GetResponse (FILE *ps)
64 {
65         char szLine [1024];
66         char *psz;
67         int hr;
68
69         fflush (ps);
70
71         do
72         {
73                 fgets (szLine, sizeof (szLine), ps);
74
75                 for (psz = szLine; isdigit (*psz); psz++)
76                         ;
77         }
78         while (*psz != '\0' && *psz != ' ');
79
80         hr = atol (szLine);
81         return hr;
82 }
83
84 FILE *TcpOpen (const char *szHost, int nPort)
85 {
86         int s;
87         struct sockaddr_in sa;
88         struct hostent *he;
89         struct protoent *pe;
90         FILE *ps;
91
92         pe = getprotobyname ("TCP");
93         s = socket (PF_INET, SOCK_STREAM, pe->p_proto);
94         endprotoent ();
95
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);
100
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 ()");
105
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;
112
113         close (s);
114         return NULL;
115 }
116
117
118 void SendMail (const char *szTo, const MailFields *pmf)
119 {
120         char rgchBoundary [20];
121         FILE *ps;
122         int hr;
123
124         ps = TcpOpen (pmf->szHost, 25);
125
126         hr = GetResponse (ps);
127
128         fprintf (ps, "HELO\r\n");
129         hr = GetResponse (ps);
130
131         fprintf (ps, "MAIL FROM: %s\r\n", pmf->szFrom);
132         hr = GetResponse (ps);
133
134         fprintf (ps, "RCPT TO: %s\r\n", szTo);
135         hr = GetResponse (ps);
136
137         fprintf (ps, "DATA %s\r\n", pmf->szSubject);
138         hr = GetResponse (ps);
139
140         fprintf (ps, "From: %s\r\nTo: %s\r\nSubject: %s\r\n", pmf->szFrom, pmf->szTo, pmf->szSubject);
141
142         if (pmf->fCC)
143         {
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)
149                 {
150                         if (nOpt == 'c')
151                         {
152                                 if (!fFirst)
153                                         fprintf (ps, ";");
154                                 fprintf (ps, " %s", optarg);
155                                 fFirst = false;
156                         }
157                 }
158                 fprintf (ps, "\r\n");
159         }
160
161         if (pmf->fAttachments)
162         {
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';
168
169                 fprintf (ps, 
170                         "MIME-Version: 1.0\r\n"
171                         "Content-Type: multipart/mixed; boundary=\"multipart-%s\"\r\n\r\n", rgchBoundary);
172
173                 fprintf (ps, "--multipart-%s\r\n\r\n", rgchBoundary);
174         }
175
176         if (pmf->pfMsg != NULL)
177         {
178                 rewind (pmf->pfMsg);
179                 while (!feof (pmf->pfMsg))
180                 {
181                         char strLine [1024];
182                         int cch;
183
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';
189
190                         if (cch == 1 && strLine [0] == '.')
191                                 fputc ('.', ps);
192
193                         fprintf (ps, "%s\r\n", strLine);
194                         fflush (ps);
195                 }
196         }
197
198         if (pmf->fAttachments)
199         {
200                 int nOpt, iOpt;
201                 optind = 0;
202                 while ((nOpt = getopt_long (pmf->cArgs, pmf->rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
203                 {
204                         if (nOpt == 'a')
205                         {
206                                 const char rgchBase64 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
207                                 FILE *pf = fopen (optarg, "r");
208                                 int cch = 0;
209
210                                 const char *szBasename = (char *) strrchr ((const char *) optarg, '/');
211                                 if (szBasename == NULL || szBasename [1] == '\0')
212                                         szBasename = optarg;
213                                 else
214                                         szBasename ++;
215
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);
217
218                                 while (true)
219                                 {
220                                         int ch1, ch2, ch3;
221                                         if ((ch1 = fgetc (pf)) == -1)
222                                                 break;
223
224                                         fputc (rgchBase64 [ch1 >> 2], ps);
225
226                                         if ((ch2 = fgetc (pf)) == -1)
227                                         {
228                                                 fputc (rgchBase64 [(ch1 << 4) & 0x30], ps);
229                                                 fputc ('=', ps);
230                                                 fputc ('=', ps);
231                                                 break;
232                                         }
233
234                                         fputc (rgchBase64 [(ch2 >> 4) | ((ch1 << 4) & 0x30)], ps);
235
236                                         if ((ch3 = fgetc (pf)) == -1)
237                                         {
238                                                 fputc (rgchBase64 [(ch2 << 2) & 0x3c], ps);
239                                                 fputc ('=', ps);
240                                                 break;
241                                         }
242
243                                         fputc (rgchBase64 [((ch2 << 2) & 0x3c) | (ch3 >> 6)], ps);
244                                         fputc (rgchBase64 [ch3 & 0x3f], ps);
245                                         
246                                         cch += 4;
247                                         if (cch >= 76)
248                                                 fprintf (ps, "\r\n");
249                                 }
250                                 fclose (pf);
251                                 fprintf (ps, "\r\n\r\n");
252                         }
253                 }
254         }
255
256         fprintf (ps, "\r\n.\r\nQUIT\r\n");
257         hr = GetResponse (ps);
258
259         fclose (ps);
260 }
261
262 int main (int cArgs, char *rgszArgs [])
263 {
264         int nOpt, iOpt = 0;
265         MailFields mf;
266         bzero ((char *) &mf, sizeof (mf));
267         mf.cArgs = cArgs;
268         mf.rgszArgs = rgszArgs;
269
270         while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
271         {
272                 switch (nOpt)
273                 {
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':
298                                 {
299                                         FILE *pfTmp = fopen (optarg, "r");
300                                         mf.fAttachments = true;
301                                         if (pfTmp == NULL)
302                                         {
303                                                 fprintf (stderr, "File not found: %s\n", optarg);
304                                                 exit (1);
305                                         }
306                                         fclose (pfTmp);
307                                 }
308                                 break;
309                         case 'm':
310                                 if (mf.pfMsg != NULL)
311                                         goto _default;
312
313                                 mf.pfMsg = fopen (optarg, "r");
314                                 if (mf.pfMsg == NULL)
315                                 {
316                                         fprintf (stderr, "File not found: %s\n", optarg);
317                                         exit (1);
318                                 }
319                                 break;
320                         case '?':
321                         default: _default:
322                                 printf ("%c: %i\n", nOpt, iOpt);
323                                 help ();
324                                 return 1;
325                 }
326         }
327         
328         if (mf.szHost == NULL)
329                 mf.szHost = "localhost";
330
331         if (mf.szSubject == NULL)
332                 mf.szSubject = "";
333
334         if (mf.szTo == NULL)
335         {
336                 perror ("must specify 'to' field");
337                 help ();
338                 return 1;
339         }
340
341         if (mf.szFrom == NULL)
342         {
343                 perror ("must specify 'from' field");
344                 help ();
345                 return 1;
346         }
347
348         SendMail (mf.szTo, &mf);
349
350         if (mf.fCC)
351         {
352                 optind = 0;
353                 while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
354                 {
355                         if (nOpt == 'c')
356                         {
357                                 int optindTmp = optind;
358                                 SendMail (optarg, &mf);
359                                 optind = optindTmp;
360                         }
361                 }
362         }
363
364         if (mf.pfMsg != NULL)
365                 fclose (mf.pfMsg);
366
367         return 0;
368 }
369