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
)