1 // simple tool for sending smtp messages
5 #include <sys/socket.h>
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
37 const char *szSubject;
45 const char *szOptions = "t:f:s:h:c:a:m:?";
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)
73 fgets (szLine, sizeof (szLine), ps);
75 for (psz = szLine; isdigit (*psz); psz++)
78 while (*psz != '\0' && *psz != ' ');
84 FILE *TcpOpen (const char *szHost, int nPort)
87 struct sockaddr_in sa;
92 pe = getprotobyname ("TCP");
93 s = socket (PF_INET, SOCK_STREAM, pe->p_proto);
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 ()");
118 void SendMail (const char *szTo, const MailFields *pmf)
120 char rgchBoundary [20];
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);
148 while ((nOpt = getopt_long (pmf->cArgs, pmf->rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
154 fprintf (ps, " %s", optarg);
158 fprintf (ps, "\r\n");
161 if (pmf->fAttachments)
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';
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)
179 while (!feof (pmf->pfMsg))
184 fgets (strLine, sizeof (strLine), pmf->pfMsg);
185 cch = strlen (strLine);
186 while (strLine [cch - 1] == '\r' || strLine [cch - 1] == '\n')
188 strLine [cch] = '\0';
190 if (cch == 1 && strLine [0] == '.')
193 fprintf (ps, "%s\r\n", strLine);
198 if (pmf->fAttachments)
202 while ((nOpt = getopt_long (pmf->cArgs, pmf->rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
206 const char rgchBase64 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
207 FILE *pf = fopen (optarg, "r");
210 const char *szBasename = (char *) strrchr ((const char *) optarg, '/');
211 if (szBasename == NULL || szBasename [1] == '\0')
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);
221 if ((ch1 = fgetc (pf)) == -1)
224 fputc (rgchBase64 [ch1 >> 2], ps);
226 if ((ch2 = fgetc (pf)) == -1)
228 fputc (rgchBase64 [(ch1 << 4) & 0x30], ps);
234 fputc (rgchBase64 [(ch2 >> 4) | ((ch1 << 4) & 0x30)], ps);
236 if ((ch3 = fgetc (pf)) == -1)
238 fputc (rgchBase64 [(ch2 << 2) & 0x3c], ps);
243 fputc (rgchBase64 [((ch2 << 2) & 0x3c) | (ch3 >> 6)], ps);
244 fputc (rgchBase64 [ch3 & 0x3f], ps);
248 fprintf (ps, "\r\n");
251 fprintf (ps, "\r\n\r\n");
256 fprintf (ps, "\r\n.\r\nQUIT\r\n");
257 hr = GetResponse (ps);
262 int main (int cArgs, char *rgszArgs [])
266 bzero ((char *) &mf, sizeof (mf));
268 mf.rgszArgs = rgszArgs;
270 while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
280 if (mf.szFrom != NULL)
285 if (mf.szSubject != NULL)
287 mf.szSubject = optarg;
290 if (mf.szHost != NULL)
299 FILE *pfTmp = fopen (optarg, "r");
300 mf.fAttachments = true;
303 fprintf (stderr, "File not found: %s\n", optarg);
310 if (mf.pfMsg != NULL)
313 mf.pfMsg = fopen (optarg, "r");
314 if (mf.pfMsg == NULL)
316 fprintf (stderr, "File not found: %s\n", optarg);
322 printf ("%c: %i\n", nOpt, iOpt);
328 if (mf.szHost == NULL)
329 mf.szHost = "localhost";
331 if (mf.szSubject == NULL)
336 perror ("must specify 'to' field");
341 if (mf.szFrom == NULL)
343 perror ("must specify 'from' field");
348 SendMail (mf.szTo, &mf);
353 while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
357 int optindTmp = optind;
358 SendMail (optarg, &mf);
364 if (mf.pfMsg != NULL)