SMTP.c

SMTP.c example details

Function

int send_mail ( char *msg )

performs the send SMTP message state machine. A small buffer for incoming messages is allocated from heap and should be freed any time this function is left. The SMTP gateway is hard-coded in socket_open() function. To make this function more flexible the gateway address should be passed as a parameter.

More information on the exact specification of SMTP and the meanings of the commands and responses can be found in RFC821 at http://www.ietf.org.

/***************************************************************************//**
@file SMTP.c
@brief Send mail to SMTP server
@verbatim
_ _ _
__| | ___(_) ____ _ __ | |_
/ _` | / __| |/ _` | '_ \| __|
| (_| | _ \__ \ | (_| | | | | |_
\__,_|(_) ___/_|\__, |_| |_|\__|
Signalprocessing |___/ Technology
@endverbatim
@author D.SignT GmbH & Co. KG, Claus Hermbusche
@date 2019-06-03
@anchor SMTPTESTEX
@details
More information on the exact specification of SMTP and the meanings of the
commands and responses can be found in RFC821 at http://www.ietf.org.
and at https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol
@cond Software License Agreement
Copyright (C) 2001-2019 D.SignT GmbH & Co. KG - http://www.dsignt.de
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
Neither the name of D.SignT GmbH & Co. KG nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
Disclaimer
THIS SOFTWARE IS PROVIDED BY D.SIGNT GMBH & CO. KG "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL D.SIGNT GMBH & CO. KG BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@endcond
*******************************************************************************/
/*******************************************************************************
include stdtypes.h to avoid data type mismatch
*******************************************************************************/
/*******************************************************************************
include Runtime Source
*******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <inttypes.h>
#include <time.h>
#include <string.h>
/*******************************************************************************
network support functions
*******************************************************************************/
#include <Libs/NETlib/net.h> /* D.Module network support */
/*******************************************************************************
board specific functions
*******************************************************************************/
#include <BoardSupport/inc/BoardSpecific.h> /* board support functions */
/*******************************************************************************
common support functions
*******************************************************************************/
#include <Common/Common.h> /* on exit function */
#include <Common/uartio.h> /* UART support */
#include <Common/timer.h> /* timer setting */
#include <Common/CPrintf.h> /* CPrintf defines */
/*******************************************************************************
network configuration
*******************************************************************************/
#include <BoardSupport/config/netconfig.c> /* network configuration */
#ifdef __cplusplus
extern "C" {
#endif /* !__cplusplus */
void encodeblock( u_int8_t *in, u_int8_t *out, int32_t len );
/*******************************************************************************
local prototypes
*******************************************************************************/
#pragma CODE_SECTION(get_reply_code , ".commontext")
int32_t get_reply_code (SOCKET *mail, char *mail_data, char *text);
#pragma CODE_SECTION(send_mail , ".commontext")
FUNC_RETURN send_mail ( char *text, char *msg );
#pragma CODE_SECTION(wait_for_link , ".commontext")
int wait_for_link ( void );
#ifdef __cplusplus
} // extern "C"
#endif
/*******************************************************************************
* *
* DEFINES *
* *
*******************************************************************************/
#define MAX_DATA 512
/*******************************************************************************
fill in your mail server IP address, name and email
*******************************************************************************/
// #define SMTPGATEWAY "192.168.168.101"
#define SMTPGATEWAY "deepthought"
#define SOURCE_EMAIL_ADDRESS "dsp@dsignt.de"
#define SOURCE_PASS "testpass"
// #define DEST_EMAIL_ADDRESS "you@company.com"
#define DEST_EMAIL_ADDRESS "claus.hermbusche@dsignt.de"
/*******************************************************************************
send mail state's
*******************************************************************************/
typedef enum
{
/*******************************************************************************
main loop state's
*******************************************************************************/
typedef enum {
ML_STOP_STATE = 0, /* (0) stop main loop */
ML_WAIT_LINK_STATE, /* (1) wait for link */
ML_SEND_MAIL_STATE, /* (2) send emails */
ML_IDLE_STATE /* (3) idle network */
} ML_t;
/*******************************************************************************
reply codes
*******************************************************************************/
#define SMTP_SERVICE_READY UINT32_C(220)
#define SMTP_SERVICE_CLOSING UINT32_C(221)
#define SMTP_REQUEST_OK UINT32_C(250)
#define SMTP_AUTHENTICATED UINT32_C(235)
#define SMTP_START_MAIL UINT32_C(354)
#define UTC_PRINTF " Current UTC time: "VT100_BLUE"%s\r"VT100_DEFAULT
/*******************************************************************************
* *
* GLOBALS *
* *
*******************************************************************************/
/*******************************************************************************
program name
*******************************************************************************/
char *program_name = "SMTP";
/*******************************************************************************
predefined messages for communication with mail server
*******************************************************************************/
static char data_msg[] = {"DATA\r\n"};
static char auth_msg[] = {"AUTH LOGIN\r\n"};
static char auth_name[] = {"dsp@dsignt.de"};
static char auth_pass[] = SOURCE_PASS;
static char mail_from[] = {"MAIL FROM: "SOURCE_EMAIL_ADDRESS"\r\n"};
static char rcpt_msg[] = {"RCPT TO: "DEST_EMAIL_ADDRESS"\r\n"};
static char quit_msg[] = {"QUIT\r\n"};
static char cmd_string[120] = {0};
/*******************************************************************************
mail header
*******************************************************************************/
static char from_msg[] = {"From: "SOURCE_EMAIL_ADDRESS"\r\n"};
static char to_msg[] = {"To: "DEST_EMAIL_ADDRESS"\r\n"};
static char sub_msg[] = {"Subject: Test mail from DSP\r\n"};
static char cont_msg[] = {"Content-Type: text/plain\r\n"};
static char prio_msg[] = {"Priority: Urgent\r\n\r\n"};
static char endmail[] = {"\r\n.\r\n"};
/*******************************************************************************
email messages
*******************************************************************************/
const char * eMessages[] = {
"%s started work at %s\r\n",
"%s is alive at %s\r\n" ,
"%s stopped work at %s\r\n",
""
};
char *MailCycleMessage[] = {
" sending start mail ",
" sending alive mail ",
" sending stop mail ",
" SMTP test finished successfully,\r\n entering idle network processing\r\n"
};
/*******************************************************************************
* *
* FUNCTIONS *
* *
*******************************************************************************/
/*******************************************************************************
@brief Wait for reply code from the SMTP server
@param mail - connected mail socket
@param mail_data - data buffer for incoming messages
@return success - reply code
@return error/timeout - -1
@sa net_isq()
@sa net_recv()
@sa atoi()
*******************************************************************************/
int32_t get_reply_code (SOCKET *mail, char *mail_data, char *text)
{
/***************************************************************************
locals
***************************************************************************/
int32_t ret = INT32_C(0);
if ( mail == NULL )
{
/***********************************************************************
invalid socket
***********************************************************************/
return (INT32_C(-1));
}
/***************************************************************************
wait for answer or timeout
***************************************************************************/
ResetTimeOutTimer (&tot, '.', 10000, text);
while ( ret == INT32_C(0) )
{
/***********************************************************************
stop after 5 seconds
***********************************************************************/
if (ProcessTimeout (&tot, 5)) break;
net_isq ();
if ( net_recv (mail, mail_data, MAX_DATA))
{
/*******************************************************************
check events
if NULL is passed as pLog parameter, no message is printed to output
use net_recv_event_handler() to determine the data length waiting
in the receive buffer
*******************************************************************/
ret = net_recv_event_handler (mail, NULL); /* without messages */
if (ret == INT32_C(0))
{
/***************************************************************
receive error
***************************************************************/
return (INT32_C(-1));
}
}
} // while
/***************************************************************************
cut descriptive string; we are interested in return code only
***************************************************************************/
mail_data[3] = 0;
#if (defined(_TMS320C6X) )
return (atoi (mail_data));
#else
return (atol (mail_data));
#endif
}
/*******************************************************************************
@brief Send mail
@param *msg - message to send
@return FR_OK - success
@return FR_ERROR - error
@return FR_TIMEDOUT - connection timed out
*******************************************************************************/
FUNC_RETURN send_mail ( char *text, char *msg )
{
/***************************************************************************
locals
***************************************************************************/
SOCKET *mail; /* socket descriptor 1 for transmit */
char *mail_data; /* data buffer */
MAILSTATE_t ms = MS_IDLE_STATE; /* state */
FUNC_RETURN f_result = FR_ERROR; /* result */
u_int8_t in[3], out[4];
int i;
int32_t len;
char *p, *t;
/***************************************************************************
malloc some space for data
***************************************************************************/
mail_data = (char *) malloc (MAX_DATA * sizeof(char));
if ( mail_data == NULL )
{
return (f_result);
}
/***************************************************************************
open socket:
***************************************************************************/
// 25, // traditional SMTP port
587, // standard port for SMTP
if ( mail == INVALID_SOCKET )
{
/***********************************************************************
couldn't get a free socket, free memory and return
***********************************************************************/
free (mail_data);
return (f_result);
} // if
/***************************************************************************
set the receive buffer size
***************************************************************************/
set_recv_buffer (mail, mail_data, MAX_DATA);
/***************************************************************************
try to connect to mail server
***************************************************************************/
ResetTimeOutTimer (&tot, '.', 10000, " connecting mail server ");
for (;;)
{
/***********************************************************************
stop after 6 seconds
***********************************************************************/
if (ProcessTimeout (&tot, 6))
{
/*******************************************************************
connect error, free memory and return
*******************************************************************/
socket_close (mail);
free (mail_data);
return (FR_TIMEDOUT);
}
if ( connect (mail, mail_data, UINT32_C(1), SMTP_CONNECT_TIMEOUT) == TRUE ) break;
}
/***************************************************************************
start state machine
***************************************************************************/
while ( ms > MS_IDLE_STATE ) // while not in idle
{
net_isq (); // process net ISQ
switch ( ms )
{
case MS_IDLE_STATE: // exit while loop
break;
case MS_HELO_STATE: // send HELO message
if ( get_reply_code (mail, mail_data, text) == SMTP_SERVICE_READY )
{
/***********************************************************
build HELO message
***********************************************************/
strncpy (cmd_string, "HELO ", UINT32_C(5));
/***********************************************************
append IP own address and trailing CRLF
***********************************************************/
strcat (cmd_string, "\r");
strcat (cmd_string, "\n");
/***********************************************************
Send: HELO
***********************************************************/
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == SMTP_REQUEST_OK )
{
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == UINT32_C(334) )
{
/***********************************************************
Send: AUTH
***********************************************************/
/***********************************************************
walk through source string and encode
***********************************************************/
p = (char *)&auth_name[0];
t = (char *)&cmd_string[0];
while ( (*p & 0xff) != 0)
{
len = UINT32_C(0);
for ( i=0;i<3 ;i++ )
{
if (*p & 0xff)
{
in[i] = (*p++) & 0xff;
len++;
}
else
{
in[i] = 0;
}
}
if (len)
{
encodeblock( in, out, len );
*t++ = out[0] & 0xff;
*t++ = out[1] & 0xff;
*t++ = out[2] & 0xff;
*t++ = out[3] & 0xff;
}
}
/***********************************************************
terminate encoded string
***********************************************************/
*t++ = '\r';
*t++ = '\n';
*t++ = 0;
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == UINT32_C(334) )
{
/***********************************************************
walk through source string and encode
***********************************************************/
p = (char *)&auth_pass[0];
t = (char *)&cmd_string[0];
while ( (*p & 0xff) != 0)
{
len = UINT32_C(0);
for ( i=0;i<3 ;i++ )
{
if (*p & 0xff)
{
in[i] = (*p++) & 0xff;
len++;
}
else
{
in[i] = 0;
}
}
if (len)
{
encodeblock( in, out, len );
*t++ = out[0] & 0xff;
*t++ = out[1] & 0xff;
*t++ = out[2] & 0xff;
*t++ = out[3] & 0xff;
}
}
/***********************************************************
terminate encoded string
***********************************************************/
*t++ = '\r';
*t++ = '\n';
*t++ = 0;
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == SMTP_AUTHENTICATED )
{
/***********************************************************
Send: MAIL FROM
***********************************************************/
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == SMTP_REQUEST_OK )
{
/***********************************************************
Send: RCPT TO
***********************************************************/
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == SMTP_REQUEST_OK )
{
/***********************************************************
Send: DATA
***********************************************************/
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == SMTP_START_MAIL )
{
/***********************************************************
Send out the header first
***********************************************************/
/***********************************************************
send mail
***********************************************************/
net_send_string (mail, msg);
/***********************************************************
Send: mail-end
***********************************************************/
/***********************************************************
goto next state
***********************************************************/
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
if ( get_reply_code (mail, mail_data, text) == SMTP_REQUEST_OK )
{
/***********************************************************
Send: QUIT
***********************************************************/
if ( get_reply_code (mail, mail_data, text) == SMTP_SERVICE_CLOSING )
{
/*******************************************************
shutdown connection
*******************************************************/
}
/***********************************************************
exit state machine
***********************************************************/
/***********************************************************
set return value to success
***********************************************************/
f_result = FR_OK;
}
else
{
/***********************************************************
exit state machine
***********************************************************/
}
break;
} // switch
} // while
/***************************************************************************
free memory; close socket
***************************************************************************/
socket_close (mail);
free (mail_data);
return (f_result);
}
/*******************************************************************************
@brief monitor link status and IP address assignment
@param -
@return 0 - no link or no IP address assigned
@return 1 - link established and valid IP address assigned
*******************************************************************************/
int wait_for_link ( void )
{
/***************************************************************************
locals
***************************************************************************/
/* - */
/***************************************************************
perform progress bar animation each 200 milli second
***************************************************************/
CPrintProgressBarText ('.', 200000, 16, " waiting for link ");
/***************************************************************
monitor link status
***************************************************************/
/***************************************************************
try to detect IP assignment
if DHCP is used, the assigned IP address may change
***************************************************************/
/***************************************************************
in case of a valid link and IP address advance to next state
***************************************************************/
if ((_link != 0) && (get_ip_address (0) != UINT32_C(0)))
{
return (1);
}
return (0);
}
/*******************************************************************************
@brief Main application
@param -
@return never
*******************************************************************************/
#pragma CODE_SECTION(main , ".commontext");
int main ( void )
{
/***************************************************************************
locals
***************************************************************************/
ML_t ml = ML_WAIT_LINK_STATE; /* main loop switch */
timeval stamp1, stamp2, delta; /* used to determine startup time */
int i=0; /* loop counter */
timeval CurrentTime; /* time stamp */
struct tm *local_time; /* structure to hold time and date */
FUNC_RETURN f_result; /* function result */
char *message; /* email message */
/***************************************************************************
initialize application (e.g. timer clocks, PLL settings, EMIF etc.)
(ref. \Common\Common.c)
***************************************************************************/
/***************************************************************************
select output device for CPrintf (ref. \Common\cprintf.c)
possible settings:
CPRINTF_UART_OUTPUT -> output to UART
CPRINTF_CCS_OUTPUT -> output to CCS
CPRINTF_UART_OUTPUT | CPRINTF_CCS_OUTPUT -> output to UART and CCS
***************************************************************************/
/***************************************************************************
print a start up message
***************************************************************************/
/**************************************************************************/
// CPrintfProgress (" Heap check ");
// at least 0x2000 bytes required for this app
/**************************************************************************/
// CPrintfProgressSuccess();
/**************************************************************************/
CPrintfProgress (" Setup system time ");
// 10 milli seconds resolution
/**************************************************************************/
CPrintf (" *** timer %d mapped to CPU int %d ***\r\n",
/**************************************************************************/
CPrintfProgress (" Enable interrupts ");
/**************************************************************************/
/**************************************************************************/
CPrintfProgress (" Start system timer ");
/**************************************************************************/
CPrintf (" *** timer %d running at %"PRId32" Hz ***\r\n", SystemTimerDev, RES_SECONDS/GetSystemTimerRes());
/***************************************************************************
malloc space for data
***************************************************************************/
CPrintfProgress (" Try to allocate data buffer ");
message = (char *) malloc (TCP_MAX_PACKET_SIZE * sizeof(char));
if ( message == NULL )
{
prg_exit ("out of memory error");
}
/***************************************************************************
measure network initialization time
***************************************************************************/
stamp1 = GetTimeStamp();
/**************************************************************************/
CPrintfProgress (" Initialize network ");
/**************************************************************************/
InitializeNetwork ( 64); // 64 bytes for ping
stamp2 = GetTimeStamp();
tv_interval (&delta, &stamp1, &stamp2);
CPuts (" network startup time [sec]: ");
"%"PRId32".%03"PRId32"\r\n"
delta.tv_sec,
delta.tv_usec/1000);
local_time = localtime ((const time_t *)&RecentEpoch);
CPrintf (UTC_PRINTF, asctime (local_time));
while (ml)
{
net_isq ();
switch (ml)
{
/***************************************************************
stop program
***************************************************************/
break;
if ( wait_for_link ())
{
/*******************************************************
if link and IP are valid, go to next state
*******************************************************/
}
break;
/***************************************************************
build email message
***************************************************************/
local_time = localtime ((const time_t *)&RecentEpoch);
sprintf ( message, eMessages[i], module_str, asctime (local_time));
/***************************************************************
send email
***************************************************************/
f_result = send_mail (MailCycleMessage[i], message);
/***************************************************************
print result
***************************************************************/
if (f_result == FR_OK)
{
CPrintf(FRMessages[f_result]);
}
else
{
CPrintfProgress (" connecting mail server ");
CPrintf(FRMessages[f_result]);
}
/***************************************************************
take time
***************************************************************/
CurrentTime = GetTimeStamp();
break;
if (i == 2)
{
i++;
}
if (i < 2)
{
/***********************************************************
restart send state after 5 seconds
***********************************************************/
if ( tv_elapsed (&CurrentTime, UINT32_C(5), UINT32_C(0), TRUE) )
{
i++;
}
}
/***************************************************************
show that the program is running, perform symbol animation
***************************************************************/
break;
}
}
/***************************************************************************
exit program, shut down peripherals
***************************************************************************/
free (message);
return (0);
}