Ejemplo n.º 1
0
/*****************************************************************************
  Function:
	void SMTPTask(void)

  Summary:
	Performs any pending SMTP client tasks

  Description:
	This function handles periodic tasks associated with the SMTP client,
	such as processing initial connections and command sequences.

  Precondition:
	None

  Parameters:
	None

  Returns:
	None

  Remarks:
	This function acts as a task (similar to one in an RTOS).  It
	performs its task in a co-operative manner, and the main application
	must call this function repeatedly to ensure that all open or new
	connections are served in a timely fashion.
  ***************************************************************************/
void SMTPTask(void)
{
	BYTE			i;
	WORD			w;
	BYTE			vBase64Buffer[4];
	static DWORD	Timer;
	static BYTE		RXBuffer[4];
	static ROM BYTE *ROMStrPtr, *ROMStrPtr2;
	static BYTE 	*RAMStrPtr;
	static WORD		wAddressLength;

 WORD tmp;

	switch(TransportState)
	{
		case TRANSPORT_HOME:
			// SMTPBeginUsage() is the only function which will kick 
			// the state machine into the next state
			break;

		case TRANSPORT_BEGIN:
			// Wait for the user to program all the pointers and then 
			// call SMTPSendMail()
			if(!SMTPFlags.bits.ReadyToStart)
				break;

			// Obtain ownership of the DNS resolution module
			if(!DNSBeginUsage())
				break;

			// Obtain the IP address associated with the SMTP mail server
			if(SMTPClient.Server.szRAM || SMTPClient.Server.szROM)
			{
				if(SMTPClient.ROMPointers.Server)
					DNSResolveROM(SMTPClient.Server.szROM, DNS_TYPE_A);
				else
					DNSResolve(SMTPClient.Server.szRAM, DNS_TYPE_A);
			}
			else
			{
				// If we don't have a mail server, try to send the mail 
				// directly to the destination SMTP server
				if(SMTPClient.To.szRAM && !SMTPClient.ROMPointers.To)
				{
					SMTPClient.Server.szRAM = (BYTE*)strchr((char*)SMTPClient.To.szRAM, '@');
					SMTPClient.ROMPointers.Server = 0;
				}
				else if(SMTPClient.To.szROM && SMTPClient.ROMPointers.To)
				{
					SMTPClient.Server.szROM = (ROM BYTE*)strchrpgm((ROM char*)SMTPClient.To.szROM, '@');
					SMTPClient.ROMPointers.Server = 1;
				}

				if(!(SMTPClient.Server.szRAM || SMTPClient.Server.szROM))
				{
					if(SMTPClient.CC.szRAM && !SMTPClient.ROMPointers.CC)
					{
						SMTPClient.Server.szRAM = (BYTE*)strchr((char*)SMTPClient.CC.szRAM, '@');
						SMTPClient.ROMPointers.Server = 0;
					}
					else if(SMTPClient.CC.szROM && SMTPClient.ROMPointers.CC)
					{
						SMTPClient.Server.szROM = (ROM BYTE*)strchrpgm((ROM char*)SMTPClient.CC.szROM, '@');
						SMTPClient.ROMPointers.Server = 1;
					}
				}

				if(!(SMTPClient.Server.szRAM || SMTPClient.Server.szROM))
				{
					if(SMTPClient.BCC.szRAM && !SMTPClient.ROMPointers.BCC)
					{
						SMTPClient.Server.szRAM = (BYTE*)strchr((char*)SMTPClient.BCC.szRAM, '@');
						SMTPClient.ROMPointers.Server = 0;
					}
					else if(SMTPClient.BCC.szROM && SMTPClient.ROMPointers.BCC)
					{
						SMTPClient.Server.szROM = (ROM BYTE*)strchrpgm((ROM char*)SMTPClient.BCC.szROM, '@');
						SMTPClient.ROMPointers.Server = 1;
					}
				}

				// See if we found a hostname anywhere which we could resolve
				if(!(SMTPClient.Server.szRAM || SMTPClient.Server.szROM))
				{
					DNSEndUsage();
					ResponseCode = SMTP_RESOLVE_ERROR;
					TransportState = TRANSPORT_HOME;
					break;
				}

				// Skip over the @ sign and resolve the host name
				if(SMTPClient.ROMPointers.Server)
				{
					SMTPClient.Server.szROM++;
					DNSResolveROM(SMTPClient.Server.szROM, DNS_TYPE_MX);
				}
				else
				{
					SMTPClient.Server.szRAM++;
					DNSResolve(SMTPClient.Server.szRAM, DNS_TYPE_MX);
				}
			}
			
			Timer = TickGet();
			TransportState++;
			break;

		case TRANSPORT_NAME_RESOLVE:
			// Wait for the DNS server to return the requested IP address
			if(!DNSIsResolved(&SMTPServer))
			{
				// Timeout after 6 seconds of unsuccessful DNS resolution
				if(TickGet() - Timer > 6*TICK_SECOND)
				{
					ResponseCode = SMTP_RESOLVE_ERROR;
					TransportState = TRANSPORT_HOME;
					DNSEndUsage();
				}
				break;
			}

			// Release the DNS module, we no longer need it
			if(!DNSEndUsage())
			{
				// An invalid IP address was returned from the DNS 
				// server.  Quit and fail permanantly if host is not valid.
				ResponseCode = SMTP_RESOLVE_ERROR;
				TransportState = TRANSPORT_HOME;
				break;
			}

			TransportState++;
			// No need to break here

		case TRANSPORT_OBTAIN_SOCKET:
			// Connect a TCP socket to the remote SMTP server
			MySocket = TCPOpen(SMTPServer.Val, TCP_OPEN_IP_ADDRESS, SMTPClient.ServerPort, TCP_PURPOSE_DEFAULT);
			
			// Abort operation if no TCP sockets are available
			// If this ever happens, add some more 
			// TCP_PURPOSE_DEFAULT sockets in TCPIPConfig.h
			if(MySocket == INVALID_SOCKET)
				break;

			TransportState++;
			Timer = TickGet();
			// No break; fall into TRANSPORT_SOCKET_OBTAINED
			
		#if defined(STACK_USE_SSL_CLIENT)
		case TRANSPORT_SECURING_SOCKET:		
			if(!TCPIsConnected(MySocket))
			{
				// Don't stick around in the wrong state if the
				// server was connected, but then disconnected us.
				// Also time out if we can't establish the connection 
				// to the SMTP server
				if((LONG)(TickGet()-Timer) > (LONG)(SMTP_SERVER_REPLY_TIMEOUT))
				{
					ResponseCode = SMTP_CONNECT_ERROR;
					TransportState = TRANSPORT_CLOSE;
				}

				break;
			}
			SMTPFlags.bits.ConnectedOnce = TRUE;
			
			// Start SSL if needed for this connection
			if(SMTPClient.UseSSL && !TCPStartSSLClient(MySocket,NULL))
				break;
			
			// Move on to main state
			Timer = TickGet();
			TransportState++;
			break;		
		#endif

		case TRANSPORT_SOCKET_OBTAINED:
			if(!TCPIsConnected(MySocket))
			{
				// Don't stick around in the wrong state if the
				// server was connected, but then disconnected us.
				// Also time out if we can't establish the connection 
				// to the SMTP server
				if(SMTPFlags.bits.ConnectedOnce || ((LONG)(TickGet()-Timer) > (LONG)(SMTP_SERVER_REPLY_TIMEOUT)))
				{
					ResponseCode = SMTP_CONNECT_ERROR;
					TransportState = TRANSPORT_CLOSE;
				}

				break;
			}
			SMTPFlags.bits.ConnectedOnce = TRUE;
			
			#if defined(STACK_USE_SSL_CLIENT)
			// Make sure the SSL handshake has completed
			if(SMTPClient.UseSSL && TCPSSLIsHandshaking(MySocket))
				break;
			#endif

			// See if the server sent us anything
			while(TCPIsGetReady(MySocket))
			{
				TCPGet(MySocket, &i);
				switch(RXParserState)
				{
					case RX_BYTE_0:
					case RX_BYTE_1:
					case RX_BYTE_2:
						RXBuffer[RXParserState] = i;
						RXParserState++;
						break;
	
					case RX_BYTE_3:
						switch(i)
						{
							case ' ':
								SMTPFlags.bits.RXSkipResponse = FALSE;
								RXParserState++;
								break;
							case '-':
								SMTPFlags.bits.RXSkipResponse = TRUE;
								RXParserState++;
								break;
							case '\r':
								RXParserState = RX_SEEK_LF;
								break;
						}
						break;
	
					case RX_SEEK_CR:
						if(i == '\r')
							RXParserState++;
						break;
	
					case RX_SEEK_LF:
						// If we received the whole command
						if(i == '\n')
						{
							RXParserState = RX_BYTE_0;

							if(!SMTPFlags.bits.RXSkipResponse)
							{
								// The server sent us a response code
								// Null terminate the ASCII reponse code so we can convert it to an integer
								RXBuffer[3] = 0;
								ResponseCode = atoi((char*)RXBuffer);

								// Handle the response
								switch(SMTPState)
								{
									case SMTP_HELO_ACK:
										if(ResponseCode >= 200u && ResponseCode <= 299u)
										{
											if(SMTPClient.Username.szRAM || SMTPClient.Username.szROM)
												SMTPState = SMTP_AUTH_LOGIN;
											else
												SMTPState = SMTP_MAILFROM;
										}
										else
											SMTPState = SMTP_QUIT_INIT;
										break;


									case SMTP_AUTH_LOGIN_ACK:
									case SMTP_AUTH_USERNAME_ACK:
										if(ResponseCode == 334u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;

									case SMTP_AUTH_PASSWORD_ACK:
										if(ResponseCode == 235u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;

									case SMTP_HOME:
									case SMTP_MAILFROM_ACK:
									case SMTP_RCPTTO_ACK:
									case SMTP_RCPTTOCC_ACK:
									case SMTP_RCPTTOBCC_ACK:
                                                                            tmp = SMTPState;
										if(ResponseCode >= 200u && ResponseCode <= 299u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;
							
									case SMTP_DATA_ACK:
										if(ResponseCode == 354u)
											SMTPState++;
										else
											SMTPState = SMTP_QUIT_INIT;
										break;
							
									case SMTP_DATA_BODY_ACK:
										if(ResponseCode >= 200u && ResponseCode <= 299u)
											SMTPFlags.bits.SentSuccessfully = TRUE;
							
										SMTPState = SMTP_QUIT_INIT;
										break;

									// Default case needed to supress compiler diagnostics
									default:
										break;
								}
							}
						}
						else if(i != '\r')
							RXParserState--;
	
						break;
				}
			}

			// Generate new data in the TX buffer, as needed, if possible
			if(TCPIsPutReady(MySocket) < 64u)
				break;

			switch(SMTPState)
			{
				case SMTP_HELO:
					if(SMTPClient.Username.szROM == NULL)
						TCPPutROMString(MySocket, (ROM BYTE*)"HELO MCHPBOARD\r\n");
					else
						TCPPutROMString(MySocket, (ROM BYTE*)"EHLO MCHPBOARD\r\n");
					TCPFlush(MySocket);
					SMTPState++;
					break;

				case SMTP_AUTH_LOGIN:
					// Note: This state is only entered from SMTP_HELO_ACK if the application 
					// has specified a Username to use (either SMTPClient.Username.szROM or 
					// SMTPClient.Username.szRAM is non-NULL)
					TCPPutROMString(MySocket, (ROM BYTE*)"AUTH LOGIN\r\n");
					TCPFlush(MySocket);
					SMTPState++;
					break;

				case SMTP_AUTH_USERNAME:
					// Base 64 encode and transmit the username.
					if(SMTPClient.ROMPointers.Username)
					{
						ROMStrPtr = SMTPClient.Username.szROM;
						w = strlenpgm((ROM char*)ROMStrPtr);
					}
					else
					{
						RAMStrPtr = SMTPClient.Username.szRAM;
						w = strlen((char*)RAMStrPtr);
					}

					while(w)
					{
						i = 0;
						while((i < w) && (i < sizeof(vBase64Buffer)*3/4))
						{
							if(SMTPClient.ROMPointers.Username)
								vBase64Buffer[i] = *ROMStrPtr++;
							else
								vBase64Buffer[i] = *RAMStrPtr++;
							i++;
						}
						w -= i;					
						Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer));
						TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer));
					}
					TCPPutROMString(MySocket, (ROM BYTE*)"\r\n");
					TCPFlush(MySocket);
					SMTPState++;
					break;

				case SMTP_AUTH_PASSWORD:
					// Base 64 encode and transmit the password
					if(SMTPClient.ROMPointers.Password)
					{
						ROMStrPtr = SMTPClient.Password.szROM;
						w = strlenpgm((ROM char*)ROMStrPtr);
					}
					else
					{
						RAMStrPtr = SMTPClient.Password.szRAM;
						w = strlen((char*)RAMStrPtr);
					}

					while(w)
					{
						i = 0;
						while((i < w) && (i < sizeof(vBase64Buffer)*3/4))
						{
							if(SMTPClient.ROMPointers.Password)
								vBase64Buffer[i] = *ROMStrPtr++;
							else
								vBase64Buffer[i] = *RAMStrPtr++;
							i++;
						}
						w -= i;					
						Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer));
						TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer));
					}
					TCPPutROMString(MySocket, (ROM BYTE*)"\r\n");
					TCPFlush(MySocket);
					SMTPState++;
					break;

				case SMTP_MAILFROM:
					// Send MAIL FROM header.  Note that this is for the SMTP server validation, 
					// not what actually will be displayed in the recipients mail client as a 
					// return address.
					TCPPutROMString(MySocket, (ROM BYTE*)"MAIL FROM:<");
					if(SMTPClient.ROMPointers.From)
					{
						ROMStrPtr = FindROMEmailAddress(SMTPClient.From.szROM, &wAddressLength);
						TCPPutROMArray(MySocket, ROMStrPtr, wAddressLength);
					}
					else
					{
						RAMStrPtr = FindEmailAddress(SMTPClient.From.szRAM, &wAddressLength);
						TCPPutArray(MySocket, RAMStrPtr, wAddressLength);
					}
					TCPPutROMString(MySocket, (ROM BYTE*)">\r\n");
					TCPFlush(MySocket);
					SMTPState++;
					break;

				case SMTP_RCPTTO_INIT:
					// See if there are any (To) recipients to process
					if(SMTPClient.To.szRAM && !SMTPClient.ROMPointers.To)
					{
						RAMStrPtr = FindEmailAddress(SMTPClient.To.szRAM, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTO;
							break;
						}
					}
					if(SMTPClient.To.szROM && SMTPClient.ROMPointers.To)
					{
						ROMStrPtr = FindROMEmailAddress(SMTPClient.To.szROM, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTO;
							break;
						}
					}
					
					SMTPState = SMTP_RCPTTOCC_INIT;
					break;

				case SMTP_RCPTTO:
				case SMTP_RCPTTOCC:
				case SMTP_RCPTTOBCC:
					TCPPutROMString(MySocket, (ROM BYTE*)"RCPT TO:<");
					if(	(SMTPClient.ROMPointers.To  && (SMTPState == SMTP_RCPTTO)) || 
						(SMTPClient.ROMPointers.CC  && (SMTPState == SMTP_RCPTTOCC)) || 
						(SMTPClient.ROMPointers.BCC && (SMTPState == SMTP_RCPTTOBCC)) )
						TCPPutROMArray(MySocket, ROMStrPtr, wAddressLength);
					else
						TCPPutArray(MySocket, RAMStrPtr, wAddressLength);
					TCPPutROMString(MySocket, (ROM BYTE*)">\r\n");
					TCPFlush(MySocket);
					SMTPState++;
					break;

				case SMTP_RCPTTO_ISDONE:
					// See if we have any more (To) recipients to process
					// If we do, we must roll back a couple of states
					if(SMTPClient.ROMPointers.To)
						ROMStrPtr = FindROMEmailAddress(ROMStrPtr+wAddressLength, &wAddressLength);
					else
						RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);
	
					if(wAddressLength)
					{
						SMTPState = SMTP_RCPTTO;
						break;
					}

					// All done with To field
					SMTPState++;
					//No break

				case SMTP_RCPTTOCC_INIT:
					// See if there are any Carbon Copy (CC) recipients to process
					if(SMTPClient.CC.szRAM && !SMTPClient.ROMPointers.CC)
					{
						RAMStrPtr = FindEmailAddress(SMTPClient.CC.szRAM, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTOCC;
							break;
						}
					}
					if(SMTPClient.CC.szROM && SMTPClient.ROMPointers.CC)
					{
						ROMStrPtr = FindROMEmailAddress(SMTPClient.CC.szROM, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTOCC;
							break;
						}
					}
					
					SMTPState = SMTP_RCPTTOBCC_INIT;
					break;

				case SMTP_RCPTTOCC_ISDONE:
					// See if we have any more Carbon Copy (CC) recipients to process
					// If we do, we must roll back a couple of states
					if(SMTPClient.ROMPointers.CC)
						ROMStrPtr = FindROMEmailAddress(ROMStrPtr+wAddressLength, &wAddressLength);
					else
						RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);

					if(wAddressLength)
					{
						SMTPState = SMTP_RCPTTOCC;
						break;
					}

					// All done with CC field
					SMTPState++;
					//No break

				case SMTP_RCPTTOBCC_INIT:
					// See if there are any Blind Carbon Copy (BCC) recipients to process
					if(SMTPClient.BCC.szRAM && !SMTPClient.ROMPointers.BCC)
					{
						RAMStrPtr = FindEmailAddress(SMTPClient.BCC.szRAM, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTOBCC;
							break;
						}
					}
					if(SMTPClient.BCC.szROM && SMTPClient.ROMPointers.BCC)
					{
						ROMStrPtr = FindROMEmailAddress(SMTPClient.BCC.szROM, &wAddressLength);
						if(wAddressLength)
						{
							SMTPState = SMTP_RCPTTOBCC;
							break;
						}
					}

					// All done with BCC field
					SMTPState = SMTP_DATA;
					break;

				case SMTP_RCPTTOBCC_ISDONE:
					// See if we have any more Blind Carbon Copy (CC) recipients to process
					// If we do, we must roll back a couple of states
					if(SMTPClient.ROMPointers.BCC)
						ROMStrPtr = FindROMEmailAddress(ROMStrPtr+wAddressLength, &wAddressLength);
					else
						RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);

					if(wAddressLength)
					{
						SMTPState = SMTP_RCPTTOBCC;
						break;
					}

					// All done with BCC field
					SMTPState++;
					//No break

				case SMTP_DATA:
					TCPPutROMString(MySocket, (ROM BYTE*)"DATA\r\n");
					SMTPState++;
					PutHeadersState = PUTHEADERS_FROM_INIT;
					TCPFlush(MySocket);
					break;

				case SMTP_DATA_HEADER:
					while((PutHeadersState != PUTHEADERS_DONE) && (TCPIsPutReady(MySocket) > 64u))
					{
						switch(PutHeadersState)
						{
							case PUTHEADERS_FROM_INIT:
								if(SMTPClient.From.szRAM || SMTPClient.From.szROM)
								{
									PutHeadersState = PUTHEADERS_FROM;
									TCPPutROMString(MySocket, (ROM BYTE*)"From: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_TO_INIT;
								}
								break;
								
							case PUTHEADERS_FROM:
								if(SMTPClient.ROMPointers.From)
								{
									SMTPClient.From.szROM = TCPPutROMString(MySocket, SMTPClient.From.szROM);
									if(*SMTPClient.From.szROM == 0u)
										PutHeadersState = PUTHEADERS_TO_INIT;
								}
								else
								{
									SMTPClient.From.szRAM = TCPPutString(MySocket, SMTPClient.From.szRAM);
									if(*SMTPClient.From.szRAM == 0u)
										PutHeadersState = PUTHEADERS_TO_INIT;
								}
								break;

							case PUTHEADERS_TO_INIT:
								if(SMTPClient.To.szRAM || SMTPClient.To.szROM)
								{
									PutHeadersState = PUTHEADERS_TO;
									TCPPutROMString(MySocket, (ROM BYTE*)"\r\nTo: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_CC_INIT;
								}
								break;
								
							case PUTHEADERS_TO:
								if(SMTPClient.ROMPointers.To)
								{
									SMTPClient.To.szROM = TCPPutROMString(MySocket, SMTPClient.To.szROM);
									if(*SMTPClient.To.szROM == 0u)
										PutHeadersState = PUTHEADERS_CC_INIT;
								}
								else
								{
									SMTPClient.To.szRAM = TCPPutString(MySocket, SMTPClient.To.szRAM);
									if(*SMTPClient.To.szRAM == 0u)
										PutHeadersState = PUTHEADERS_CC_INIT;
								}
								break;

							case PUTHEADERS_CC_INIT:
								if(SMTPClient.CC.szRAM || SMTPClient.CC.szROM)
								{
									PutHeadersState = PUTHEADERS_CC;
									TCPPutROMString(MySocket, (ROM BYTE*)"\r\nCC: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_SUBJECT_INIT;
								}
								break;
								
							case PUTHEADERS_CC:
								if(SMTPClient.ROMPointers.CC)
								{
									SMTPClient.CC.szROM = TCPPutROMString(MySocket, SMTPClient.CC.szROM);
									if(*SMTPClient.CC.szROM == 0u)
										PutHeadersState = PUTHEADERS_SUBJECT_INIT;
								}
								else
								{
									SMTPClient.CC.szRAM = TCPPutString(MySocket, SMTPClient.CC.szRAM);
									if(*SMTPClient.CC.szRAM == 0u)
										PutHeadersState = PUTHEADERS_SUBJECT_INIT;
								}
								break;

							case PUTHEADERS_SUBJECT_INIT:
								if(SMTPClient.Subject.szRAM || SMTPClient.Subject.szROM)
								{
									PutHeadersState = PUTHEADERS_SUBJECT;
									TCPPutROMString(MySocket, (ROM BYTE*)"\r\nSubject: ");
								}
								else
								{
									PutHeadersState = PUTHEADERS_OTHER_INIT;
								}
								break;
								
							case PUTHEADERS_SUBJECT:
								if(SMTPClient.ROMPointers.Subject)
								{
									SMTPClient.Subject.szROM = TCPPutROMString(MySocket, SMTPClient.Subject.szROM);
									if(*SMTPClient.Subject.szROM == 0u)
										PutHeadersState = PUTHEADERS_OTHER_INIT;
								}
								else
								{
									SMTPClient.Subject.szRAM = TCPPutString(MySocket, SMTPClient.Subject.szRAM);
									if(*SMTPClient.Subject.szRAM == 0u)
										PutHeadersState = PUTHEADERS_OTHER_INIT;
								}
								break;

							case PUTHEADERS_OTHER_INIT:
								TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2);
								if(SMTPClient.OtherHeaders.szRAM || SMTPClient.OtherHeaders.szROM)
								{
									PutHeadersState = PUTHEADERS_OTHER;
								}
								else
								{
									TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2);
									PutHeadersState = PUTHEADERS_DONE;
									SMTPState++;
								}
								break;
								
							case PUTHEADERS_OTHER:
								if(SMTPClient.ROMPointers.OtherHeaders)
								{
									SMTPClient.OtherHeaders.szROM = TCPPutROMString(MySocket, SMTPClient.OtherHeaders.szROM);
									if(*SMTPClient.OtherHeaders.szROM == 0u)
									{
										TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2);
										PutHeadersState = PUTHEADERS_DONE;
										SMTPState++;
									}
								}
								else
								{
									SMTPClient.OtherHeaders.szRAM = TCPPutString(MySocket, SMTPClient.OtherHeaders.szRAM);
									if(*SMTPClient.OtherHeaders.szRAM == 0u)
									{
										TCPPutROMArray(MySocket, (ROM BYTE*)"\r\n", 2);
										PutHeadersState = PUTHEADERS_DONE;
										SMTPState++;
									}
								}
								break;
							
							// Default case needed to supress compiler diagnostics
							default:
								break;
						}
					}
					TCPFlush(MySocket);
					break;
		
				case SMTP_DATA_BODY_INIT:
					SMTPState++;
					RAMStrPtr = SMTPClient.Body.szRAM;
					ROMStrPtr2 = (ROM BYTE*)"\r\n.\r\n";
					CRPeriod.Pos = NULL;
					if(RAMStrPtr)
						CRPeriod.Pos = (BYTE*)strstrrampgm((char*)RAMStrPtr, (ROM char*)"\r\n.");
					// No break here
		
				case SMTP_DATA_BODY:
					if(SMTPClient.Body.szRAM || SMTPClient.Body.szROM)
					{
						if(*ROMStrPtr2)
						{
							// Put the application data, doing the transparancy replacement of "\r\n." with "\r\n.."
							while(CRPeriod.Pos)
							{
								CRPeriod.Pos += 3;
								RAMStrPtr += TCPPutArray(MySocket, RAMStrPtr, CRPeriod.Pos-RAMStrPtr);
								if(RAMStrPtr == CRPeriod.Pos)
								{
									if(!TCPPut(MySocket, '.'))
									{
										CRPeriod.Pos -= 3;
										break;
									}
								}
								else
								{
									CRPeriod.Pos -= 3;
									break;
								}
								CRPeriod.Pos = (BYTE*)strstrrampgm((char*)RAMStrPtr, (ROM char*)"\r\n.");
							}
							
							// If we get down here, either all replacements have been made or there is no remaining space in the TCP output buffer
							RAMStrPtr = TCPPutString(MySocket, RAMStrPtr);
							ROMStrPtr2 = TCPPutROMString(MySocket, ROMStrPtr2);
							TCPFlush(MySocket);
						}
					}
					else
					{
						if(SMTPFlags.bits.ReadyToFinish)
						{
							if(*ROMStrPtr2)
							{
								ROMStrPtr2 = TCPPutROMString(MySocket, ROMStrPtr2);
								TCPFlush(MySocket);
							}
		
						}
					}

					if(*ROMStrPtr2 == 0u)
					{
						SMTPState++;
					}
					break;
		
				case SMTP_QUIT_INIT:
					SMTPState++;
					ROMStrPtr = (ROM BYTE*)"QUIT\r\n";
					// No break here

				case SMTP_QUIT:
					if(*ROMStrPtr)
					{
						ROMStrPtr = TCPPutROMString(MySocket, ROMStrPtr);
						TCPFlush(MySocket);
					}

					if(*ROMStrPtr == 0u)
					{
						TransportState = TRANSPORT_CLOSE;
					}
					break;
				
				// Default case needed to supress compiler diagnostics
				default:
					break;
			}
			break;

		case TRANSPORT_CLOSE:
			// Close the socket so it can be used by other modules
			TCPDisconnect(MySocket);
			MySocket = INVALID_SOCKET;

			// Go back to doing nothing
			TransportState = TRANSPORT_HOME;
			break;
	}
}
Ejemplo n.º 2
0
/*****************************************************************************
  Function:
    void GenericSSLClient(void)

  Summary:
    Implements a simple HTTP client (over TCP).

  Description:
    This function implements a simple HTTP client, which operates over TCP.  
    The function is called periodically by the stack, and waits for BUTTON1 
    to be pressed.  When the button is pressed, the application opens a TCP
    connection to an Internet search engine, performs a search for the word
    "Microchip" on "microchip.com", and prints the resulting HTML page to
    the UART.  
    
    To add this to an existing application, make the call to GenericSSLClient 
    from StackTasks.
    
    This example can be used as a model for many TCP and HTTP client
    applications.

  Precondition:
    TCP is initialized.

  Parameters:
    None

  Returns:
    None
  ***************************************************************************/
void GenericSSLClient(void)
{
    uint8_t             i;
    uint16_t            w;
    DNS_RESULT          dnsRes;
    uint8_t             vBuffer[9];
    static TCPIP_NET_HANDLE    netH;
    static uint32_t     clientTimer;
    static TCP_SOCKET   MySocket = INVALID_SOCKET;
    static enum _GenericTCPExampleState
    {
        SM_HOME = 0,
        SM_WAIT_DNS,
        SM_DNS_RESOLVED,
        SM_SOCKET_OBTAINED,
        SM_START_SSL,
        SM_PROCESS_RESPONSE,
        SM_DISCONNECT,
        SM_DONE
    } GenericTCPExampleState = SM_DONE;

    switch(GenericTCPExampleState)
    {

        case SM_HOME:
            
            netH = TCPIP_STACK_GetDefaultNet();
            dnsRes = DNSBeginUsage(netH);
            if(dnsRes != DNS_RES_OK)
                break;
            DNSResolve(SSLServerName, DNS_TYPE_A);
            GenericTCPExampleState++;
            break;

        case SM_WAIT_DNS:
            
            dnsRes = DNSIsResolved(SSLServerName, &serverIP_SSL);
            if(dnsRes == DNS_RES_PENDING)
            {   // ongoing operation;
                break;
            }
            else if(dnsRes < 0)
            {   // some DNS error occurred; retry
                SYS_CONSOLE_MESSAGE((const char*)"\r\n\r\nDNS name resolving failed...\r\n");
                TCPClose(MySocket);
                MySocket = INVALID_SOCKET;
                GenericTCPExampleState = SM_HOME;
            }
            else
            {
                clientTimer = SYS_TICK_Get();
                GenericTCPExampleState++;
            }
            DNSEndUsage(netH);
            break;

        case SM_DNS_RESOLVED:
            // Connect the socket to the remote TCP server
            MySocket = TCPOpenClient(IP_ADDRESS_TYPE_IPV4, SSLServerPort, (IP_MULTI_ADDRESS*)&serverIP_SSL);
            
			// Abort operation if no TCP socket could be opened.
			// If this ever happens, you need to update your tcp_config.h
            if(MySocket == INVALID_SOCKET)
            {   // retry
                break;
            }

            SYS_CONSOLE_MESSAGE((const char*)"\r\n\r\nConnecting using Microchip TCP API...\r\n");

            GenericTCPExampleState++;
            clientTimer = SYS_TICK_Get();
            break;

        case SM_SOCKET_OBTAINED:

            // Wait for the remote server to accept our connection request
            if(!TCPIsConnected(MySocket))
            {
                // Time out if more than 5 seconds is spent in this state
                if((SYS_TICK_Get()-clientTimer) > 5 * SYS_TICK_TicksPerSecondGet() )
                {
                    // Close the socket so it can be used by other modules
                    TCPClose(MySocket);
                    MySocket = INVALID_SOCKET;
                    GenericTCPExampleState--;
                    SYS_CONSOLE_MESSAGE((const char*)"\r\n\r\nFailed connecting to the remote server...\r\n");
                }
                break;
            }

            clientTimer = SYS_TICK_Get();

            if(!TCPStartSSLClient(MySocket,(uint8_t *)"thishost"))
                break;

            GenericTCPExampleState++;
            break;

        case SM_START_SSL:
            if (TCPSSLIsHandshaking(MySocket)) 
            {
                // Handshaking may fail if the SSL_RSA_CLIENT_SIZE is not large enough
                // for the server’s certificate
                if(SYS_TICK_Get()-clientTimer > 10*SYS_TICK_TicksPerSecondGet())
                {
                    // Close the socket so it can be used by other modules
                    TCPClose(MySocket);
                    MySocket = INVALID_SOCKET;
                    GenericTCPExampleState=SM_HOME;
                }
                break;
            }


            // Make certain the socket can be written to
            if(TCPIsPutReady(MySocket) < 125u)
                break;
            
            // Place the application protocol data into the transmit buffer.  For this example, we are connected to an HTTP server, so we'll send an HTTP GET request.
            TCPPutString(MySocket, (const uint8_t*)"GET ");
            TCPPutString(MySocket, SSLRemoteURL);
            TCPPutString(MySocket, (const uint8_t*)" HTTP/1.0\r\nHost: ");
            TCPPutString(MySocket, (const uint8_t*)SSLServerName);
            TCPPutString(MySocket, (const uint8_t*)"\r\nConnection: close\r\n\r\n");

            // Send the packet
            TCPFlush(MySocket);
            GenericTCPExampleState++;
            break;

        case SM_PROCESS_RESPONSE:
            // Check to see if the remote node has disconnected from us or sent us any application data
            // If application data is available, write it to the UART
            if(!TCPIsConnected(MySocket))
            {
                GenericTCPExampleState = SM_DISCONNECT;
                // Do not break;  We might still have data in the TCP RX FIFO waiting for us
            }
    
            // Get count of RX bytes waiting
            w = TCPIsGetReady(MySocket);    
    
            // Obtian and print the server reply
            i = sizeof(vBuffer)-1;
            vBuffer[i] = '\0';
            while(w)
            {
                if(w < i)
                {
                    i = w;
                    vBuffer[i] = '\0';
                }
                w -= TCPGetArray(MySocket, vBuffer, i);
                SYS_CONSOLE_MESSAGE((char*)vBuffer);
                
                // SYS_CONSOLE_MESSAGE is a blocking call which will slow down the rest of the stack 
                // if we shovel the whole TCP RX FIFO into the serial port all at once.  
                // Therefore, let's break out after only one chunk most of the time.  The 
                // only exception is when the remote node disconncets from us and we need to 
                // use up all the data before changing states.
                if(GenericTCPExampleState == SM_PROCESS_RESPONSE)
                    break;
            }
    
            break;
    
        case SM_DISCONNECT:
            // Close the socket so it can be used by other modules
            // For this application, we wish to stay connected, but this state will still get entered if the remote server decides to disconnect
            TCPClose(MySocket);
            MySocket = INVALID_SOCKET;
            GenericTCPExampleState = SM_DONE;
            break;
    
        case SM_DONE:
            // Do nothing unless the user pushes BUTTON1 and wants to restart the whole connection/download process
            if(BUTTON1_IO == 0u)
                GenericTCPExampleState = SM_HOME;
            break;
    }
}
Ejemplo n.º 3
0
void GenericTCPClient(void)
{
	BYTE 				i;
	WORD				w;
	BYTE				vBuffer[9];
	static DWORD		Timer;
	static TCP_SOCKET	MySocket = INVALID_SOCKET;
	static enum _GenericTCPExampleState GenericTCPExampleState = SM_DONE;


	switch(GenericTCPExampleState)
	{
		case SM_HOME:
			// Connect a socket to the remote TCP server
			MySocket = TCPOpen((DWORD)&ServerName[0], TCP_OPEN_RAM_HOST, ServerPort, TCP_PURPOSE_GENERIC_TCP_CLIENT);
			
			// Abort operation if no TCP socket of type TCP_PURPOSE_GENERIC_TCP_CLIENT is available
			// If this ever happens, you need to go add one to TCPIPConfig.h
			if(MySocket == INVALID_SOCKET)
				break;

			#if defined(STACK_USE_UART)
			putrsUART((ROM char*)"\r\n\r\nConnecting using Microchip TCP API...\r\n");
			#endif

			GenericTCPExampleState++;
			Timer = TickGet();
			break;

		case SM_SOCKET_OBTAINED:
			// Wait for the remote server to accept our connection request
			if(!TCPIsConnected(MySocket))
			{
				// Time out if too much time is spent in this state
				if(TickGet()-Timer > 5*TICK_SECOND)
				{
					// Close the socket so it can be used by other modules
					TCPDisconnect(MySocket);
					MySocket = INVALID_SOCKET;
					GenericTCPExampleState--;
				}
				break;
			}

			Timer = TickGet();
			
    #if defined (STACK_USE_SSL_CLIENT)
            if(!TCPStartSSLClient(MySocket,(void *)"thishost"))
                break;
			GenericTCPExampleState++;
			break;

        case SM_START_SSL:
            if (TCPSSLIsHandshaking(MySocket)) 
            {
				if(TickGet()-Timer > 10*TICK_SECOND)
				{
					// Close the socket so it can be used by other modules
					TCPDisconnect(MySocket);
					MySocket = INVALID_SOCKET;
					GenericTCPExampleState=SM_HOME;
				}
                break;
            }
    #endif

			// Make certain the socket can be written to
			if(TCPIsPutReady(MySocket) < 125u)
				break;
			
			// Place the application protocol data into the transmit buffer.  For this example, we are connected to an HTTP server, so we'll send an HTTP GET request.
			TCPPutROMString(MySocket, (ROM BYTE*)"GET ");
			TCPPutROMString(MySocket, RemoteURL);
			TCPPutROMString(MySocket, (ROM BYTE*)" HTTP/1.0\r\nHost: ");
			TCPPutString(MySocket, ServerName);
			TCPPutROMString(MySocket, (ROM BYTE*)"\r\nConnection: close\r\n\r\n");

			// Send the packet
			TCPFlush(MySocket);
			GenericTCPExampleState++;
			break;

		case SM_PROCESS_RESPONSE:
			// Check to see if the remote node has disconnected from us or sent us any application data
			// If application data is available, write it to the UART
			if(!TCPIsConnected(MySocket))
			{
				GenericTCPExampleState = SM_DISCONNECT;
				// Do not break;  We might still have data in the TCP RX FIFO waiting for us
			}
	
			// Get count of RX bytes waiting
			w = TCPIsGetReady(MySocket);	
	
			// Obtian and print the server reply
			i = sizeof(vBuffer)-1;
			vBuffer[i] = '\0';
			while(w)
			{
				if(w < i)
				{
					i = w;
					vBuffer[i] = '\0';
				}
				w -= TCPGetArray(MySocket, vBuffer, i);
				#if defined(STACK_USE_UART)
				putsUART((char*)vBuffer);
				#endif
				
				// putsUART is a blocking call which will slow down the rest of the stack 
				// if we shovel the whole TCP RX FIFO into the serial port all at once.  
				// Therefore, let's break out after only one chunk most of the time.  The 
				// only exception is when the remote node disconncets from us and we need to 
				// use up all the data before changing states.
				if(GenericTCPExampleState == SM_PROCESS_RESPONSE)
					break;
			}
	
			break;
	
		case SM_DISCONNECT:
			// Close the socket so it can be used by other modules
			// For this application, we wish to stay connected, but this state will still get entered if the remote server decides to disconnect
			TCPDisconnect(MySocket);
			MySocket = INVALID_SOCKET;
			GenericTCPExampleState = SM_DONE;
			break;
	
		case SM_DONE:
			// Do nothing unless the user pushes BUTTON1 and wants to restart the whole connection/download process
			if(BUTTON1_IO == 0u)
				GenericTCPExampleState = SM_HOME;
			break;
	}
}
Ejemplo n.º 4
0
/*****************************************************************************
  Function:
	void SMTPClientTask(void)

  Summary:
	Performs any pending SMTP client tasks

  Description:
	This function handles periodic tasks associated with the SMTP client,
	such as processing initial connections and command sequences.

  Precondition:
	None

  Parameters:
	None

  Returns:
	None

  Remarks:
	This function acts as a task (similar to one in an RTOS).  It
	performs its task in a co-operative manner, and the main application
	must call this function repeatedly to ensure that all open or new
	connections are served in a timely fashion.
  ***************************************************************************/
void SMTPClientTask(void)
{
    uint8_t			i;
    uint16_t			w;
    uint8_t			vBase64Buffer[4];
    static SYS_TICK	SMTPTimer;
    static uint8_t		RXBuffer[4];
    static const uint8_t *ROMStrPtr, *ROMStrPtr2;
    static const uint8_t *RAMStrPtr;
    static uint16_t		wAddressLength;
    DNS_RESULT      dnsRes;

    switch(TransportState)
    {
    case TRANSPORT_HOME:
        // SMTPBeginUsage() is the only function which will kick
        // the state machine into the next state
        break;

    case TRANSPORT_BEGIN:
        // Wait for the user to program all the pointers and then
        // call SMTPSendMail()
        if(!SMTPFlags.bits.ReadyToStart)
            break;

        // Obtain ownership of the DNS resolution module
        if(DNSBeginUsage(0) != DNS_RES_OK)
        {
            break;
        }

        // Obtain the IP address associated with the SMTP mail server
        if(SMTPClient.Server)
        {
            DNSResolve((const char*)SMTPClient.Server, DNS_TYPE_A);
        }
        else
        {
            // If we don't have a mail server, try to send the mail
            // directly to the destination SMTP server
            if(SMTPClient.To)
            {
                SMTPClient.Server = strchr((char*)SMTPClient.To, '@');
            }

            if(!(SMTPClient.Server))
            {
                if(SMTPClient.CC)
                {
                    SMTPClient.Server = strchr((char*)SMTPClient.CC, '@');
                }
            }

            if(!(SMTPClient.Server))
            {
                if(SMTPClient.BCC)
                {
                    SMTPClient.Server = strchr((char*)SMTPClient.BCC, '@');
                }
            }

            // See if we found a hostname anywhere which we could resolve
            if(!(SMTPClient.Server))
            {
                DNSEndUsage(0);
                ResponseCode = SMTP_RESOLVE_ERROR;
                TransportState = TRANSPORT_HOME;
                break;
            }

            // Skip over the @ sign and resolve the host name
            SMTPClient.Server++;
            DNSResolve((const char*)SMTPClient.Server, DNS_TYPE_MX);
        }

        SMTPTimer = SYS_TICK_Get();
        TransportState++;
        break;

    case TRANSPORT_NAME_RESOLVE:
        // Wait for the DNS server to return the requested IP address
        dnsRes = DNSIsResolved((const char*)SMTPClient.Server, &SMTPServer);

        if(dnsRes == DNS_RES_PENDING)
        {
            break;
        }

        // Release the DNS module
        DNSEndUsage(0);

        if(dnsRes < 0)
        {   // some error occurred
            ResponseCode = SMTP_RESOLVE_ERROR;
            TransportState = TRANSPORT_HOME;
            break;
        }

        // DNS_RES_OK
        TransportState++;
    // No need to break here

    case TRANSPORT_OBTAIN_SOCKET:
        // Connect a TCP socket to the remote SMTP server
        MySocket = TCPOpenClient(IP_ADDRESS_TYPE_IPV4, SMTPClient.ServerPort, (IP_MULTI_ADDRESS*)&SMTPServer.Val);

        // Abort operation if no TCP socket could be opened.
        // If this ever happens, you need to update your tcp_config.h
        if(MySocket == INVALID_SOCKET)
            break;

        TransportState++;
        SMTPTimer = SYS_TICK_Get();
        // No break; fall into TRANSPORT_SOCKET_OBTAINED

#if defined(TCPIP_STACK_USE_SSL_CLIENT)
    case TRANSPORT_SECURING_SOCKET:
        if(!TCPIsConnected(MySocket))
        {
            // Don't stick around in the wrong state if the
            // server was connected, but then disconnected us.
            // Also time out if we can't establish the connection
            // to the SMTP server
            if((SYS_TICK_Get()-SMTPTimer) > (SMTP_SERVER_REPLY_TIMEOUT * SYS_TICK_TicksPerSecondGet()))
            {
                ResponseCode = SMTP_CONNECT_ERROR;
                TransportState = TRANSPORT_CLOSE;
            }

            break;
        }
        SMTPFlags.bits.ConnectedOnce = true;

        // Start SSL if needed for this connection
        if(SMTPClient.UseSSL && !TCPStartSSLClient(MySocket,NULL))
            break;

        // Move on to main state
        SMTPTimer = SYS_TICK_Get();
        TransportState++;
        break;
#endif

    case TRANSPORT_SOCKET_OBTAINED:
        if(!TCPIsConnected(MySocket))
        {
            // Don't stick around in the wrong state if the
            // server was connected, but then disconnected us.
            // Also time out if we can't establish the connection
            // to the SMTP server
            if(SMTPFlags.bits.ConnectedOnce || ((SYS_TICK_Get()-SMTPTimer) > (SMTP_SERVER_REPLY_TIMEOUT * SYS_TICK_TicksPerSecondGet())))
            {
                ResponseCode = SMTP_CONNECT_ERROR;
                TransportState = TRANSPORT_CLOSE;
            }

            break;
        }
        SMTPFlags.bits.ConnectedOnce = true;

#if defined(TCPIP_STACK_USE_SSL_CLIENT)
        // Make sure the SSL handshake has completed
        if(SMTPClient.UseSSL && TCPSSLIsHandshaking(MySocket))
            break;
#endif

        // See if the server sent us anything
        while(TCPIsGetReady(MySocket))
        {
            TCPGet(MySocket, &i);
            switch(RXParserState)
            {
            case RX_BYTE_0:
            case RX_BYTE_1:
            case RX_BYTE_2:
                RXBuffer[RXParserState] = i;
                RXParserState++;
                break;

            case RX_BYTE_3:
                switch(i)
                {
                case ' ':
                    SMTPFlags.bits.RXSkipResponse = false;
                    RXParserState++;
                    break;
                case '-':
                    SMTPFlags.bits.RXSkipResponse = true;
                    RXParserState++;
                    break;
                case '\r':
                    RXParserState = RX_SEEK_LF;
                    break;
                }
                break;

            case RX_SEEK_CR:
                if(i == '\r')
                    RXParserState++;
                break;

            case RX_SEEK_LF:
                // If we received the whole command
                if(i == '\n')
                {
                    RXParserState = RX_BYTE_0;

                    if(!SMTPFlags.bits.RXSkipResponse)
                    {
                        // The server sent us a response code
                        // Null terminate the ASCII reponse code so we can convert it to an integer
                        RXBuffer[3] = 0;
                        ResponseCode = atoi((char*)RXBuffer);

                        // Handle the response
                        switch(SMTPState)
                        {
                        case SMTP_HELO_ACK:
                            if(ResponseCode >= 200u && ResponseCode <= 299u)
                            {
                                if(SMTPClient.Username)
                                    SMTPState = SMTP_AUTH_LOGIN;
                                else
                                    SMTPState = SMTP_MAILFROM;
                            }
                            else
                                SMTPState = SMTP_QUIT_INIT;
                            break;


                        case SMTP_AUTH_LOGIN_ACK:
                        case SMTP_AUTH_USERNAME_ACK:
                            if(ResponseCode == 334u)
                                SMTPState++;
                            else
                                SMTPState = SMTP_QUIT_INIT;
                            break;

                        case SMTP_AUTH_PASSWORD_ACK:
                            if(ResponseCode == 235u)
                                SMTPState++;
                            else
                                SMTPState = SMTP_QUIT_INIT;
                            break;

                        case SMTP_HOME:
                        case SMTP_MAILFROM_ACK:
                        case SMTP_RCPTTO_ACK:
                        case SMTP_RCPTTOCC_ACK:
                        case SMTP_RCPTTOBCC_ACK:
                            if(ResponseCode >= 200u && ResponseCode <= 299u)
                                SMTPState++;
                            else
                                SMTPState = SMTP_QUIT_INIT;
                            break;

                        case SMTP_DATA_ACK:
                            if(ResponseCode == 354u)
                                SMTPState++;
                            else
                                SMTPState = SMTP_QUIT_INIT;
                            break;

                        case SMTP_DATA_BODY_ACK:
                            if(ResponseCode >= 200u && ResponseCode <= 299u)
                                SMTPFlags.bits.SentSuccessfully = true;

                            SMTPState = SMTP_QUIT_INIT;
                            break;

                        // Default case needed to supress compiler diagnostics
                        default:
                            break;
                        }
                    }
                }
                else if(i != '\r')
                    RXParserState--;

                break;
            }
        }

        // Generate new data in the TX buffer, as needed, if possible
        if(TCPIsPutReady(MySocket) < 64u)
            break;

        switch(SMTPState)
        {
        case SMTP_HELO:
            if(SMTPClient.Username == NULL)
                TCPPutString(MySocket, (uint8_t*)"HELO MCHPBOARD\r\n");
            else
                TCPPutString(MySocket, (uint8_t*)"EHLO MCHPBOARD\r\n");
            TCPFlush(MySocket);
            SMTPState++;
            break;

        case SMTP_AUTH_LOGIN:
            // Note: This state is only entered from SMTP_HELO_ACK if the application
            // has specified a Username to use (SMTPClient.Username is non-NULL)
            TCPPutString(MySocket, (uint8_t*)"AUTH LOGIN\r\n");
            TCPFlush(MySocket);
            SMTPState++;
            break;

        case SMTP_AUTH_USERNAME:
            // Base 64 encode and transmit the username.
            RAMStrPtr = (uint8_t*)SMTPClient.Username;
            w = strlen((char*)RAMStrPtr);

            while(w)
            {
                i = 0;
                while((i < w) && (i < sizeof(vBase64Buffer)*3/4))
                {
                    vBase64Buffer[i] = *RAMStrPtr++;
                    i++;
                }
                w -= i;
                TCPIP_Helper_Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer));
                TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer));
            }
            TCPPutString(MySocket, (uint8_t*)"\r\n");
            TCPFlush(MySocket);
            SMTPState++;
            break;

        case SMTP_AUTH_PASSWORD:
            // Base 64 encode and transmit the password
            RAMStrPtr = (uint8_t*)SMTPClient.Password;
            w = strlen((char*)RAMStrPtr);

            while(w)
            {
                i = 0;
                while((i < w) && (i < sizeof(vBase64Buffer)*3/4))
                {
                    vBase64Buffer[i] = *RAMStrPtr++;
                    i++;
                }
                w -= i;
                TCPIP_Helper_Base64Encode(vBase64Buffer, i, vBase64Buffer, sizeof(vBase64Buffer));
                TCPPutArray(MySocket, vBase64Buffer, sizeof(vBase64Buffer));
            }
            TCPPutString(MySocket, (uint8_t*)"\r\n");
            TCPFlush(MySocket);
            SMTPState++;
            break;

        case SMTP_MAILFROM:
            // Send MAIL FROM header.  Note that this is for the SMTP server validation,
            // not what actually will be displayed in the recipients mail client as a
            // return address.
            TCPPutString(MySocket, (uint8_t*)"MAIL FROM:<");
            RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.From, &wAddressLength);
            TCPPutArray(MySocket, RAMStrPtr, wAddressLength);
            TCPPutString(MySocket, (uint8_t*)">\r\n");
            TCPFlush(MySocket);
            SMTPState++;
            break;

        case SMTP_RCPTTO_INIT:
            // See if there are any (To) recipients to process
            if(SMTPClient.To)
            {
                RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.To, &wAddressLength);
                if(wAddressLength)
                {
                    SMTPState = SMTP_RCPTTO;
                    break;
                }
            }

            SMTPState = SMTP_RCPTTOCC_INIT;
            break;

        case SMTP_RCPTTO:
        case SMTP_RCPTTOCC:
        case SMTP_RCPTTOBCC:
            TCPPutString(MySocket, (uint8_t*)"RCPT TO:<");
            TCPPutArray(MySocket, RAMStrPtr, wAddressLength);
            TCPPutString(MySocket, (uint8_t*)">\r\n");
            TCPFlush(MySocket);
            SMTPState++;
            break;

        case SMTP_RCPTTO_ISDONE:
            // See if we have any more (To) recipients to process
            // If we do, we must roll back a couple of states
            RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);

            if(wAddressLength)
            {
                SMTPState = SMTP_RCPTTO;
                break;
            }

            // All done with To field
            SMTPState++;
        //No break

        case SMTP_RCPTTOCC_INIT:
            // See if there are any Carbon Copy (CC) recipients to process
            if(SMTPClient.CC)
            {
                RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.CC, &wAddressLength);
                if(wAddressLength)
                {
                    SMTPState = SMTP_RCPTTOCC;
                    break;
                }
            }

            SMTPState = SMTP_RCPTTOBCC_INIT;
            break;

        case SMTP_RCPTTOCC_ISDONE:
            // See if we have any more Carbon Copy (CC) recipients to process
            // If we do, we must roll back a couple of states
            RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);

            if(wAddressLength)
            {
                SMTPState = SMTP_RCPTTOCC;
                break;
            }

            // All done with CC field
            SMTPState++;
        //No break

        case SMTP_RCPTTOBCC_INIT:
            // See if there are any Blind Carbon Copy (BCC) recipients to process
            if(SMTPClient.BCC)
            {
                RAMStrPtr = FindEmailAddress((uint8_t*)SMTPClient.BCC, &wAddressLength);
                if(wAddressLength)
                {
                    SMTPState = SMTP_RCPTTOBCC;
                    break;
                }
            }

            // All done with BCC field
            SMTPState = SMTP_DATA;
            break;

        case SMTP_RCPTTOBCC_ISDONE:
            // See if we have any more Blind Carbon Copy (CC) recipients to process
            // If we do, we must roll back a couple of states
            RAMStrPtr = FindEmailAddress(RAMStrPtr+wAddressLength, &wAddressLength);

            if(wAddressLength)
            {
                SMTPState = SMTP_RCPTTOBCC;
                break;
            }

            // All done with BCC field
            SMTPState++;
        //No break

        case SMTP_DATA:
            TCPPutString(MySocket, (uint8_t*)"DATA\r\n");
            SMTPState++;
            PutHeadersState = PUTHEADERS_FROM_INIT;
            TCPFlush(MySocket);
            break;

        case SMTP_DATA_HEADER:
            while((PutHeadersState != PUTHEADERS_DONE) && (TCPIsPutReady(MySocket) > 64u))
            {
                switch(PutHeadersState)
                {
                case PUTHEADERS_FROM_INIT:
                    if(SMTPClient.From)
                    {
                        PutHeadersState = PUTHEADERS_FROM;
                        TCPPutString(MySocket, (uint8_t*)"From: ");
                    }
                    else
                    {
                        PutHeadersState = PUTHEADERS_TO_INIT;
                    }
                    break;

                case PUTHEADERS_FROM:
                    SMTPClient.From = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.From);
                    if(*SMTPClient.From == 0u)
                        PutHeadersState = PUTHEADERS_TO_INIT;
                    break;

                case PUTHEADERS_TO_INIT:
                    if(SMTPClient.To)
                    {
                        PutHeadersState = PUTHEADERS_TO;
                        TCPPutString(MySocket, (uint8_t*)"\r\nTo: ");
                    }
                    else
                    {
                        PutHeadersState = PUTHEADERS_CC_INIT;
                    }
                    break;

                case PUTHEADERS_TO:
                    SMTPClient.To = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.To);
                    if(*SMTPClient.To == 0u)
                        PutHeadersState = PUTHEADERS_CC_INIT;
                    break;

                case PUTHEADERS_CC_INIT:
                    if(SMTPClient.CC)
                    {
                        PutHeadersState = PUTHEADERS_CC;
                        TCPPutString(MySocket, (uint8_t*)"\r\nCC: ");
                    }
                    else
                    {
                        PutHeadersState = PUTHEADERS_SUBJECT_INIT;
                    }
                    break;

                case PUTHEADERS_CC:
                    SMTPClient.CC = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.CC);
                    if(*SMTPClient.CC == 0u)
                        PutHeadersState = PUTHEADERS_SUBJECT_INIT;
                    break;

                case PUTHEADERS_SUBJECT_INIT:
                    if(SMTPClient.Subject)
                    {
                        PutHeadersState = PUTHEADERS_SUBJECT;
                        TCPPutString(MySocket, (uint8_t*)"\r\nSubject: ");
                    }
                    else
                    {
                        PutHeadersState = PUTHEADERS_OTHER_INIT;
                    }
                    break;

                case PUTHEADERS_SUBJECT:
                    SMTPClient.Subject = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.Subject);
                    if(*SMTPClient.Subject == 0u)
                        PutHeadersState = PUTHEADERS_OTHER_INIT;
                    break;

                case PUTHEADERS_OTHER_INIT:
                    TCPPutArray(MySocket, (uint8_t*)"\r\n", 2);
                    if(SMTPClient.OtherHeaders)
                    {
                        PutHeadersState = PUTHEADERS_OTHER;
                    }
                    else
                    {
                        TCPPutArray(MySocket, (uint8_t*)"\r\n", 2);
                        PutHeadersState = PUTHEADERS_DONE;
                        SMTPState++;
                    }
                    break;

                case PUTHEADERS_OTHER:
                    SMTPClient.OtherHeaders = (char*)TCPPutString(MySocket, (uint8_t*)SMTPClient.OtherHeaders);
                    if(*SMTPClient.OtherHeaders == 0u)
                    {
                        TCPPutArray(MySocket, (uint8_t*)"\r\n", 2);
                        PutHeadersState = PUTHEADERS_DONE;
                        SMTPState++;
                    }
                    break;

                // Default case needed to supress compiler diagnostics
                default:
                    break;
                }
            }
            TCPFlush(MySocket);
            break;

        case SMTP_DATA_BODY_INIT:
            SMTPState++;
            RAMStrPtr = (uint8_t*)SMTPClient.Body;
            ROMStrPtr2 = (const uint8_t*)"\r\n.\r\n";
            CRPeriod.Pos = NULL;
            if(RAMStrPtr)
                CRPeriod.Pos = (uint8_t*)strstr((char*)RAMStrPtr, (const char*)"\r\n.");
        // No break here

        case SMTP_DATA_BODY:
            if(SMTPClient.Body)
            {
                if(*ROMStrPtr2)
                {
                    // Put the application data, doing the transparancy replacement of "\r\n." with "\r\n.."
                    while(CRPeriod.Pos)
                    {
                        CRPeriod.Pos += 3;
                        RAMStrPtr += TCPPutArray(MySocket, RAMStrPtr, CRPeriod.Pos-RAMStrPtr);
                        if(RAMStrPtr == CRPeriod.Pos)
                        {
                            if(!TCPPut(MySocket, '.'))
                            {
                                CRPeriod.Pos -= 3;
                                break;
                            }
                        }
                        else
                        {
                            CRPeriod.Pos -= 3;
                            break;
                        }
                        CRPeriod.Pos = (uint8_t*)strstr((char*)RAMStrPtr, (const char*)"\r\n.");
                    }

                    // If we get down here, either all replacements have been made or there is no remaining space in the TCP output buffer
                    RAMStrPtr = TCPPutString(MySocket, RAMStrPtr);
                    ROMStrPtr2 = TCPPutString(MySocket, (uint8_t*)ROMStrPtr2);
                    TCPFlush(MySocket);
                }
            }
            else
            {
                if(SMTPFlags.bits.ReadyToFinish)
                {
                    if(*ROMStrPtr2)
                    {
                        ROMStrPtr2 = TCPPutString(MySocket, (uint8_t*)ROMStrPtr2);
                        TCPFlush(MySocket);
                    }

                }
            }

            if(*ROMStrPtr2 == 0u)
            {
                SMTPState++;
            }
            break;

        case SMTP_QUIT_INIT:
            SMTPState++;
            ROMStrPtr = (const uint8_t*)"QUIT\r\n";
        // No break here

        case SMTP_QUIT:
            if(*ROMStrPtr)
            {
                ROMStrPtr = TCPPutString(MySocket, (uint8_t*)ROMStrPtr);
                TCPFlush(MySocket);
            }

            if(*ROMStrPtr == 0u)
            {
                TransportState = TRANSPORT_CLOSE;
            }
            break;

        // Default case needed to supress compiler diagnostics
        default:
            break;
        }
        break;

    case TRANSPORT_CLOSE:
        // Close the socket so it can be used by other modules
        TCPClose(MySocket);
        MySocket = INVALID_SOCKET;

        // Go back to doing nothing
        TransportState = TRANSPORT_HOME;
        break;
    }
}