Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

370 lines
7.4 KiB
C

// simple tool for sending smtp messages
//
//
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <ctype.h>
#include <time.h>
typedef int bool;
enum { false = 0, true = 1};
struct option rgOptions [] =
{
{"cc", required_argument, NULL, 'c'},
{"help", required_argument, NULL, '?'},
{"to", required_argument, NULL, 't'},
{"from", required_argument, NULL, 'f'},
{"message", required_argument, NULL, 'm'},
{"subject", required_argument, NULL, 's'},
{"host", required_argument, NULL, 'h'},
{"attach", required_argument, NULL, 'a'},
};
typedef struct MailFields
{
const char *szTo;
const char *szFrom;
const char *szHost;
const char *szSubject;
bool fAttachments;
bool fCC;
int cArgs;
char **rgszArgs;
FILE *pfMsg;
} MailFields;
const char *szOptions = "t:f:s:h:c:a:m:?";
void help ()
{
printf (
"Usage: smtp [OPTIONS]\n\n"
"Mandatory arguments:\n"
"\t-t, --to ADDRESS\tspecify destination email address\n"
"\t-f, --from ADDRESS\tspecify sender's email address\n"
"Optional arguments:\n"
"\t-s, --subject SUBJECT\tspecify subject of message\n"
"\t-m, --message FILENAME\tread text of message from FILENAME\n"
"\t-a, --attach FILENAME\tadd FILENAME to message as attachment\n"
"\t-c, --cc ADDRESS\tadd ADDRESS to CC list\n"
"\t-h, --host HOSTNAME\tconnect to smpt server HOSTNAME (default: localhost)\n"
);
}
int GetResponse (FILE *ps)
{
char szLine [1024];
char *psz;
int hr;
fflush (ps);
do
{
fgets (szLine, sizeof (szLine), ps);
for (psz = szLine; isdigit (*psz); psz++)
;
}
while (*psz != '\0' && *psz != ' ');
hr = atol (szLine);
return hr;
}
FILE *TcpOpen (const char *szHost, int nPort)
{
int s;
struct sockaddr_in sa;
struct hostent *he;
struct protoent *pe;
FILE *ps;
pe = getprotobyname ("TCP");
s = socket (PF_INET, SOCK_STREAM, pe->p_proto);
endprotoent ();
bzero ((char *)&sa,sizeof (sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr (szHost);
sa.sin_port = htons (25);
if ((he = gethostbyname (szHost)) != NULL)
bcopy (he->h_addr, (char *)&sa.sin_addr, he->h_length);
else if ((sa.sin_addr.s_addr = inet_addr (szHost)) < 0)
perror ("gethostbyname ()");
if (connect (s, (struct sockaddr *) &sa, 16) == -1)
perror ("connect ()");
else if ((ps = fdopen (s, "r+")) == NULL)
perror ("fdopen ()");
else
return ps;
close (s);
return NULL;
}
void SendMail (const char *szTo, const MailFields *pmf)
{
char rgchBoundary [20];
FILE *ps;
int hr;
ps = TcpOpen (pmf->szHost, 25);
hr = GetResponse (ps);
fprintf (ps, "HELO\r\n");
hr = GetResponse (ps);
fprintf (ps, "MAIL FROM: %s\r\n", pmf->szFrom);
hr = GetResponse (ps);
fprintf (ps, "RCPT TO: %s\r\n", szTo);
hr = GetResponse (ps);
fprintf (ps, "DATA %s\r\n", pmf->szSubject);
hr = GetResponse (ps);
fprintf (ps, "From: %s\r\nTo: %s\r\nSubject: %s\r\n", pmf->szFrom, pmf->szTo, pmf->szSubject);
if (pmf->fCC)
{
bool fFirst = true;
int nOpt, iOpt;
fprintf (ps, "CC:");
optind = 0;
while ((nOpt = getopt_long (pmf->cArgs, pmf->rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
{
if (nOpt == 'c')
{
if (!fFirst)
fprintf (ps, ";");
fprintf (ps, " %s", optarg);
fFirst = false;
}
}
fprintf (ps, "\r\n");
}
if (pmf->fAttachments)
{
int ich;
srand ((int) time (NULL));
for (ich = 0; ich < sizeof (rgchBoundary) - 1; ich ++)
rgchBoundary [ich] = rand () % ('Z'-'A') + 'A';
rgchBoundary [ich] = '\0';
fprintf (ps,
"MIME-Version: 1.0\r\n"
"Content-Type: multipart/mixed; boundary=\"multipart-%s\"\r\n\r\n", rgchBoundary);
fprintf (ps, "--multipart-%s\r\n\r\n", rgchBoundary);
}
if (pmf->pfMsg != NULL)
{
rewind (pmf->pfMsg);
while (!feof (pmf->pfMsg))
{
char strLine [1024];
int cch;
fgets (strLine, sizeof (strLine), pmf->pfMsg);
cch = strlen (strLine);
while (strLine [cch - 1] == '\r' || strLine [cch - 1] == '\n')
cch --;
strLine [cch] = '\0';
if (cch == 1 && strLine [0] == '.')
fputc ('.', ps);
fprintf (ps, "%s\r\n", strLine);
fflush (ps);
}
}
if (pmf->fAttachments)
{
int nOpt, iOpt;
optind = 0;
while ((nOpt = getopt_long (pmf->cArgs, pmf->rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
{
if (nOpt == 'a')
{
const char rgchBase64 [] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
FILE *pf = fopen (optarg, "r");
int cch = 0;
const char *szBasename = (char *) strrchr ((const char *) optarg, '/');
if (szBasename == NULL || szBasename [1] == '\0')
szBasename = optarg;
else
szBasename ++;
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);
while (true)
{
int ch1, ch2, ch3;
if ((ch1 = fgetc (pf)) == -1)
break;
fputc (rgchBase64 [ch1 >> 2], ps);
if ((ch2 = fgetc (pf)) == -1)
{
fputc (rgchBase64 [(ch1 << 4) & 0x30], ps);
fputc ('=', ps);
fputc ('=', ps);
break;
}
fputc (rgchBase64 [(ch2 >> 4) | ((ch1 << 4) & 0x30)], ps);
if ((ch3 = fgetc (pf)) == -1)
{
fputc (rgchBase64 [(ch2 << 2) & 0x3c], ps);
fputc ('=', ps);
break;
}
fputc (rgchBase64 [((ch2 << 2) & 0x3c) | (ch3 >> 6)], ps);
fputc (rgchBase64 [ch3 & 0x3f], ps);
cch += 4;
if (cch >= 76)
fprintf (ps, "\r\n");
}
fclose (pf);
fprintf (ps, "\r\n\r\n");
}
}
}
fprintf (ps, "\r\n.\r\nQUIT\r\n");
hr = GetResponse (ps);
fclose (ps);
}
int main (int cArgs, char *rgszArgs [])
{
int nOpt, iOpt = 0;
MailFields mf;
bzero ((char *) &mf, sizeof (mf));
mf.cArgs = cArgs;
mf.rgszArgs = rgszArgs;
while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
{
switch (nOpt)
{
case 't':
if (mf.szTo != NULL)
goto _default;
mf.szTo = optarg;
break;
case 'f':
if (mf.szFrom != NULL)
goto _default;
mf.szFrom = optarg;
break;
case 's':
if (mf.szSubject != NULL)
goto _default;
mf.szSubject = optarg;
break;
case 'h':
if (mf.szHost != NULL)
goto _default;
mf.szHost = optarg;
break;
case 'c':
mf.fCC = true;
break;
case 'a':
{
FILE *pfTmp = fopen (optarg, "r");
mf.fAttachments = true;
if (pfTmp == NULL)
{
fprintf (stderr, "File not found: %s\n", optarg);
exit (1);
}
fclose (pfTmp);
}
break;
case 'm':
if (mf.pfMsg != NULL)
goto _default;
mf.pfMsg = fopen (optarg, "r");
if (mf.pfMsg == NULL)
{
fprintf (stderr, "File not found: %s\n", optarg);
exit (1);
}
break;
case '?':
default: _default:
printf ("%c: %i\n", nOpt, iOpt);
help ();
return 1;
}
}
if (mf.szHost == NULL)
mf.szHost = "localhost";
if (mf.szSubject == NULL)
mf.szSubject = "";
if (mf.szTo == NULL)
{
perror ("must specify 'to' field");
help ();
return 1;
}
if (mf.szFrom == NULL)
{
perror ("must specify 'from' field");
help ();
return 1;
}
SendMail (mf.szTo, &mf);
if (mf.fCC)
{
optind = 0;
while ((nOpt = getopt_long (cArgs, rgszArgs, szOptions, rgOptions, &iOpt)) != -1)
{
if (nOpt == 'c')
{
int optindTmp = optind;
SendMail (optarg, &mf);
optind = optindTmp;
}
}
}
if (mf.pfMsg != NULL)
fclose (mf.pfMsg);
return 0;
}