Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Articles
Search: 

Sending email through Visual FoxPro without additional components
Roberto C. Ianni, March 1, 2004
Many of us send email using third-party tools such as Outlook Express or Microsoft Outlook, among others, and most likely have encountered registration problems in the client, licencing problems, OLE errors that only occurr at the client's site but not in our developer environment. Also problems if ...
Summary
Many of us send email using third-party tools such as Outlook Express or Microsoft Outlook, among others, and most likely have encountered registration problems in the client, licencing problems, OLE errors that only occurr at the client's site but not in our developer environment. Also problems if both Outlook Express and Microsoft Outlook are being used at the same time, just to mention a few.
Description
Email has become an everyday thing and, for many people, an absolutely necessary work tool.

For that reason, we as developers, try to maximize the benefits that can be obtained from this utility, building tools that use this standard, ranging from mass-mailing catalog applications sending to many addresses, to simple price list message sent to a customer via email.

Some history

Between September and October 1971, Ray Tomlinson, the American engineer who is considered the "father of email", sent an email for the first time in history. Tomlinson can't remember the content of that email nor who was the receiver, but "I only remember that the whole thing was in capital letters".

Objectives

Many of us send email using third-party tools such as Outlook Express or Microsoft Outlook, among others, and most likely have encountered registration problems at the client, licensing problems, OLE errors that only occur at the client's site but not in the developer environment. Also there could be problems if both Outlook Express and Microsoft Outlook are being used at the same time, just to mention a few.

To use these tools we utilize MAPI.Session or Outlook.Application, as you know (maybe smiling while you are reading this), creating the objects and calling their properties and methods, from data in the interfaces.

But have you ever wonder "how do these components work? What is it they really do in order to send an email?"? This is what we are going to find out in this article, by only using FOX and the operating system, with all the advantages that could be derived from it.

It is my intention that at the end of this article you will become capable of sending an email from Visual Fox Pro without the need of any OCX or an external component. Just using the tools provided by FoxPro and the operating system would be enough to do the job.

In order to do this I am going to explain two basic points:

  1. How to connect to the eMail Server without any additional control.

  2. What is and how does the standard email protocol work.

Connect to the eMail Server

I think that this is going to be the most difficult point for those who are only FoxPro programmers, because we are used to have high level functions that perform low level jobs, so we can completely ignore its inner workings.

I understand that this is going to be more complicated because we will be using the Windows API, using structures for those API, information conversions and other things to carry out the task of connecting to the eMail server.

It is only fair to mention that this point could be quickly solved by using the Microsoft WinSock Control OCX, but in this way we would not be doing what we want, which is to only use FoxPro and Windows.

Those familiar with the MS WinSock know that in order to connect to a server it is necessary to have three fundamental data to establish a conversation: the first one is the name or the IP Address of the server, to be able to localize it in the Net; the second one is the port number to which we want to communicate; and the third one is the communications protocol to be used (UPD, TCP, etc).

For eMail servers, the last two usually default to port 25 for SMTP (110 for POP3) and the TCP communications protocol.

API functions

Then, to establish the connection to the Mail server, we have to make a Socket connection, doing the same as the Microsoft WinSock Control does. So we are going to use a number of API functions provided by the operating system.

API function Description
socket Creates a Socket of a certain type and protocol.
WSAStartup Enables the current process for the use of the WS2_32.DLL library.
connect Establishes a connection through a predetermined Socket.
inet_addr Converts a string...
recv Receives data from the socket it is connected to.
ioctlsocket Controls the I/O mode of a socket.
closesocket Closes a socket.
send Sends data from the socket it is connected to.
htons Converts an unsigned short in a 16-bit number used in TCP/IP protocols.
DECLARE LONG socket IN "wsock32.dll" LONG af, LONG prototype, LONG protocol
DECLARE LONG WSAStartup IN "wsock32.dll" INTEGER wVersionRequested, STRING @lpWSAData
DECLARE LONG connect IN "wsock32.dll" LONG s, STRING @Name, LONG namelen
DECLARE LONG inet_addr IN "wsock32.dll" STRING cp
DECLARE INTEGER htons IN "wsock32.dll" INTEGER hostshort
DECLARE LONG recv IN "wsock32.dll" LONG s, STRING @buf, LONG length, LONG flags
DECLARE LONG ioctlsocket IN "wsock32.dll" LONG s, LONG cmd, LONG @argp
DECLARE LONG closesocket IN "wsock32.dll" LONG s
DECLARE LONG send IN "wsock32.dll" LONG s, STRING buf, LONG length, LONG flags
With these functions we are now able to replace the Microsoft WinSock control to connect to a server as well as to send or receive information and also to disconnect from it.

The idea is not to explain all the various properties of these API functions, but how to use them for our application, because it would make no sense to explain something already well covered in the MSDN from Microsoft.

Pseudocode

We are going to describe here the logic for connection to a server using the operating system API that we have been seen in FoxPro.

If createsock()
   If connectserver()
      If send( "Hello " )
         Answervar = Receive()
         Disconnect()
      Else
         Error( "Could not send message" )
   else
      Error( "Could not connect to server" )
else
   Error( "O.S. could not create socket" )

Development of a socket class

At this point we are able to develop a class entirely written in FOX, capable of connecting to a server, with no need of third party tools. For this we are going to assume that the API functions are already declared as described above.

DEFINE CLASS WSocket AS CUSTOM

   m_Error = .NULL.

   PROCEDURE SendInformation

      IF ( THIS.CreateSock() )
         IF ( THIS.ConnectServer() )
            IF ( THIS.Send( "Test information" ) )
               AnswerVar = this.receive()
               THIS.Desconnect()
            ELSE
               THIS.m_Error = "Could not send message"
               RETURN .F.
            ENDIF
         ELSE
            THIS.m_Error = "Could not connect to server"
            RETURN .F.
         ENDIF
      ELSE
         THIS.m_Error = "O.S. could not create socket"
         RETURN .F.
      ENDIF
   ENDPROC
So far all we did was code in Foxpro the pseudocode we had already seen, so there is not that much to explain. What we are now going to do is code each one of the methods called from the SendInformation method, and explain what each one does.
   PROTECTED PROCEDURE MakeWord( Val1, Val2 )
      *|------------------------------------------------------
      *| ((WORD) (((BYTE) (a)) | ((WORD) ((BYTE) (b))) << 8))
      *|------------------------------------------------------
      RETURN BITOR( Val1, BITLSHIFT( Val2, 8 ) )
   ENDPROC

   PROTECTED m_HandleSocket
   m_HandleSocket = .NULL.

   PROCEDURE CreateSock

      #DEFINE PF_INET           2
      #DEFINE SOCK_STREAM       1
      #DEFINE INVALID_SOCKET    0xFFFF  && 65535
      LOCAL wSpaceData
      wSpaceData = SPACE( 400 ) && Space for the WSAData structure

      && Initializes the WS2_32.DLL to use the Socket.
      IF ( WSAStartup( THIS.MAKEWORD(1, 1), @wSpaceData) != 0 )
         RETURN .F. && O.S. could not initialize socket
      ENDIF 

      && Get the socket handle from the O.S.
      THIS.m_HandleSocket = socket( PF_INET, SOCK_STREAM, 0 )
      IF THIS.m_HandleSocket == INVALID_SOCKET
         RETURN .F. && O.S. could not get socket handle
      ENDIF
ENDPROC

You will surely be asking yourselves, what is all this?. But do not despair. It looks complicated but it really is not so. If we look closely we see that what it does in the first place is initialize the WS2_32.dll library for the process being executed, and then ask Windows to create a new socket. When it is created, a handle is returned.

You will notice that the first thing done is a call to the WSAStartup function, because if the latter is not executed first, any of the functions needing the WS2_32.dll library will return an error, because the library is not loaded. But what is the parameter being sent?

The first parameter is the result of executing the MAKEWORD method, which was transcribed from VC++, basically to combine two values into one and then be able to restore both.

But if you look closely, this function has a second parameter, passed by reference, which contains the information returned by the API. We are not analyzing the result from that parameter, because the only thing of interest to us is that the function does not fail when it is loading the library.

   m_HostName = .NULL.
   m_HostPort = .NULL.

   PROTECTED PROCEDURE Long2ToString( m.Value )

      LOCAL I1, m.StringToReturn

      m.StringToReturn = ""

      FOR I1 = 24 TO 0 STEP -8
         m.StringToReturn = CHR(INT(m.Value/(2^I1))) + m.StringToReturn
         m.Value = MOD(m.Value, (2^I1))
      NEXT

      RETURN m.StringToReturn
   ENDPROC

   PROTECTED PROCEDURE Short2ToString( m.Value )

      RETURN CHR( BITAND( m.Value, 0xFF ) ) + CHR( BITRSHIFT( m.Value, 8 ) )
   ENDPROC

   PROTECTED PROCEDURE StringToLong( m.StrToConvert )

      LOCAL I1, m.NbrToReturn

      m.NroARetornar = 0
      FOR I1 = 0 TO 24 STEP 8
         m.NroARetornar = m.NroARetornar + (ASC(m.StrToConvert) * (2^I1))
         m.StrToConvert = RIGHT(m.StrToConvert, LEN(m.StrToConvert) - 1)
      NEXT

      RETURN m.NbrToAReturn
   ENDPROC

   PROCEDURE ConnectServer
      
      #DEFINE INADDR_NONE   0xFFFFFFFF
      #DEFINE AF_INET       2

      LOCAL lsipAddress, lsStructConn, lnReturn

      lsipAddress = inet_addr( THIS.m_HostName )

      IF ( lsipAddress == INADDR_NONE OR lsipAddress <= 0 )
         *|-- IP address not found, it is probably the name
         *|-- of the Server instead of its address.
         lsipAddress = This.ResolveName()
         IF lsipAddress == -1
            RETURN .F.
         ENDIF
      ENDIF

      *|-----
      *|--   typedef struct SOCKADDR{
      *|--      short            sin_family      // 2 Bytes
      *|--      unsigned short   sin_port        // 2 Bytes
      *|--      long             sin_addr        // 4 Bytes
      *|--      char             sin_zero[8]     // 8 Bytes
      *|--   };
      *|-----
      lsStructConn = ""
      lsStructConn = lsStructConn + THIS.Short2ToString( AF_INET )
      lsStructConn = lsStructConn + THIS.Short2ToString( ;
         htons( THIS.m_HostPort ) )
      lsStructConn = lsStructConn + THIS.Long2ToString( lsipAddress )
      lsStructConn = lsStructConn + SPACE( 8 )

      lnReturn = connect( THIS.m_HandleSocket, @lsStructConn,;
         LEN( lsStructConn ) ) && sizeof(SOCKADDR) -> 16 Bytes

      RETURN IIF( lnReturn == -1, .F., .T. )
   ENDPROC
We see here three methods: Long2ToString, Short2ToString and StringToLong; first two to handle numerical conversions to a string, and the third one in charge of converting a string to a FoxPro number. But what kind of string is returned by the two first ones? The string returned is the representation that this number actually has in memory for a long or short data type; and the third one converts that Long type string in a FoxPro numeric.

The ConnectServer method is the one realizing the connection to the server, using the two above named API: inet_addr and connect; but in order to use the connect function it is necessary to pass one structure by parameter to it, which we built in the lsStructConn variable, formed by the connection type (two 'Short' bits), server address in the net (four 'Long' bits) and finally some eight chars which are reserved, and placed empty for that reason. Once the variable that will work as a "Struc type variable" is formed, we pass it as a parameter to the function and with this we connect to the server.

You will notice that if the inet_addr function returns a number less or equal to zero, or else a value equal to 0xFFFFFFFF, the ResolveName method will be called. This method resolves the address occupied by the server in the network through its name. We are not going to explain this in detail because it has nothing to do with the connection itself, but it is the resolution of a name through the DNS.

   PROCEDURE Send( msg )

      #DEFINE FIONBIO         0x8004667E
      #DEFINE SIOCATMARK      0x40047307
      #DEFINE SOCKET_ERROR          -1
      LOCAL slen, nlenMsg, nindex, smsgAux

      nlenMsg = LEN( msg )
      nindex  = 0
      slen    = 0
      nRead = 0x1

      slen = ioctlsocket( THIS.m_HandleSocket, FIONBIO, @nRead )
      IF ( slen == SOCKET_ERROR )
         RETURN .F.
      ENDIF

      smsgAux = msg
      DO WHILE( nlenMsg > 0 )
         slen = send( THIS.m_HandleSocket, smsgAux, nlenMsg, 0 )
         IF ( slen < 1 )
            RETURN .F. && Sending to server failed
         ENDIF

         nlenMsg = nlenMsg - slen
         nindex  = nindex + slen
         smsgAux = SUBSTR( msg, nindex, LEN( msg ) - nindex )
      ENDDO
      RETURN .T.
   ENDPROC
We have reached the point where we see how to send information to the server, but if we look closely, we'll notice that it is not very complex; there are only calls to two API functions and then the FoxPro functions to handle strings, something which is very clear.

We are then going to analyze the two API functions in depth. The first function, ioctlsocket, receives three parameters. The first one is on what socket connection it is going to be working; the second one is the configuration type it is going to modify and the third one is the value we are going to specify.

Then we have the Send function. It receives four parameters. The first one is the Connection socket on which the function will work; the second one is the text going to be sent; the third one is the length of the text and the fourth one is a flag that can be sent to the function but that we are not going to use.

You will notice that this API Send function is within a conditioned iteration cycle. This is due to the fact that this API function returns the number of characters from the original string that it could send to the server, because the function does not send the whole string at once, but whatever it can. This is why we verify how much it could send, and we send the remainder in the next iteration of the cycle.

   PROCEDURE Receive()
   
   DO WHILE( retval = 0 )
      buffer = SPACE( 4096 )
      retval = recv( THIS.m_HandleSocket, @buffer, LEN( buffer ), 0 )
      IF retval <> 0 AND retval <> SOCKET_ERROR
         reply = reply + LEFT( buffer, retval )
      ENDIF
   ENDDO
   ENDPROC
The receive method is very similar to the Send method. The difference is that it calls the recv API function to get the text that the server is sending. For this reason we shall only see what changes in comparison with the Send method.

The modifications we can see are self evident, and it is simple to understand what the code tries to do. When a call is made to the recv function, it needs four parameters.The first one is the socket connection from which it will receive the information; the second one is a variable where the information sent from the server will be stored. This parameter is sent by reference, because this is the way in which FoxPro handles variable pointers to API functions. The third one is the string size. The API function will copy in the variable as a maximum the value which is sent in this parameter. The last one is a flag that we shall not use.

You will notice that the recv API function is within a conditioned iteration cycle, just as the Send API function which was used in the Send method.

Both functions work in a similar way, with a fundamental difference, which is: the first one receives information from the server, while the second one sends information to it, but the way of using them is very similar.

The recv function returns the information that was sent and copies it to the variable passed by reference in the second parameter. If the number of characters in the variable was not enough to copy the information sent by the server, the cycle is repeated until it finalizes, because it finished copying all the information sent by the server, or because an error occurred while receiving the information.

   PROCEDURE Disconnect()

      #DEFINE INVALID_SOCKET    0xFFFF  && 65535
      #DEFINE SOCKET_ERROR       -1
      LOCAL nClose

      && Validates the connection
      IF THIS.m_HandleSocket == INVALID_SOCKET
         RETURN .F.
      ENDIF

      nClose = closesocket( THIS.m_HandleSocket )
      RETURN IIF ( nClose != SOCKET_ERROR, .T., .F. )
   ENDPROC

ENDDEFINE
Here we can see how to use the closesocket API function to close a connection socket previously opened. There is not that much complexity in this, because the function receives the socket handle as the parameter, which was returned when the connection was created, and then it closes it, liberating it from the operating system as well as from the server where it was connected.

With the disconnect method we are finishing the creation of a class completely developed in FoxPro, intelligent enough to connect to any server through sockets, thus replacing the Microsoft WinSock ActiveX Control.

Email standard protocol

The protocol to send email is a standard known to us as SMTP (Simple Mail Transport Protocol), based in the sending of plain text with a predetermined format to the email server. The latter recognizes the format and generates the email going over different servers, as many as necessary, until it reaches the destination server of the email address, where it will be stored until the destination email owner searches it and recovers it by means of another standard email protocol, POP3 (Post Office Protocol v.3).

We are going to analyze the SMPT protocol to see some of its variations to generate an email. For those of you wishing to read the standard, it is available in the Internet at ftp://ftp.aha.ru/support/rfc/rfc822.txt or else at http://www.faqs.org/rfcs/rfc822.html. In there you can see all the specifications of the email sending protocol.

As mentioned before, the email protocol is based on the sending of text between the client and the email server, with a predetermined text format. With this we mean that, in order to send an email, it is not necessary to have a very complex email program, such as Microsoft Outlook. Rather it can be done with a simple Telnet.

Yes it is just as you read it. It is possible to do it with Telnet. In order to do this it is necessary to connect to the email server, to the STMP port and send the mail with the corresponding format. That's all there is to it !!

For those of you who do not know Telnet, it is an application distributed with the operating system since a long time ago, and useful to establish a connection with a server through a determined port.

Many of us have used Telnet to connect to Linux or to the very Unix, making a remote connection in text mode. But its usefulness does not end there. A good example is what we are going to do, is a connection to an email server.

The idea is to show you first how to send emails with Telnet and then that you understand how to do it. I shall explain in more detail some fundamental points of the email standard, and then we shall build our own VFP class to send emails with what we have learned. Lastly we shall use the WSocket class we developed above, as a means to connect with the server.

Using Telnet to send mails

As mentioned, we can send email with Telnet and do everything a sophisticated email program can do. We need to know how to connect to the server and then what to send to it so it will understand us.

To connect we only have to pass Telnet the IP address or the server name, and the port we are going to connect to, as a parameter. Down below we specify how to connect to an assumed mail server published in the Internet. It is worth mentioning that the SMTP servers are public in the Internet and can be used by any user. Contrarywise, the POP3 protocol is not public and asks for a username and password to enable working with it. There is a reason for this: if it were public everybody would be able to see the emails belonging to everybody else.

C:\>telnet smtp.ciudad.com.ar 25
Once connected to the server, we have to start sending commands for it to do some tasks. Let us see an example of some commands, and I will explain briefly each one of them:
   HELO 10.10.10.1

   MAIL FROM:
   RCPT TO:
   DATA
   X-Sender: MyAddress@MyDominion
   X-Mailer: M$ SendMail Build 1.0.0019
   Date: Sat, 24 Jan 2004 13:45:00 -0300
   From: "My Name and My Last name" 
   Subject: Telnet mail test
   To: "IANNI, ROBERTO" 
    "Blank line for the beginnig of the mail body"
   Text test in the body of the mail
    "Blank line indicating end of mail body"
   .
    "Blank line indicating end of message"
   QUIT
If we copy this text (line by line), and copy it into Telnet, once connected, we are already sending a mail, just like that. At first sight it is not very difficult and in reality it is not. The protocol has more variations and commands to send to it, so it behaves in such and such a way. But the basic idea is this one. With these simple commands we are doing it with nothing else.

If we copy line by line the above example we would be sending the email, but you will notice that the server will answer with an ok to each command you send to it, obtaining a result similar to the one below:

   220 postino3.prima.com.ar ESMTP
   HELO 10.10.10.1
   250 postino3.prima.com.ar
   MAIL FROM:
   250 ok
   RCPT TO:
   250 ok
   DATA
   354 go ahead

   X-Sender: MyAddress@MyDominion
   X-Mailer: M$ SendMail Build 1.0.0019
   Date: Sat, 24 Jan 2004 13:45:00 0300
   From: "My Name and My Lastname" 
   Subject: Telent mail test
   To: "IANNI, ROBERTO" 
   
   Text test in the mail body    
   
   .
   250 ok 1074969345 qp 85748
   
   502 unimplemented (#5.5.1)
   QUIT
   221 postino3.prima.com.ar
   
   connection with   host has been lost
   
   C:\>
With this we have just sent our first email, but let's analyze a little just what it is we are doing with the commands shown.

First, we are greeting the server with the HELO command and telling it who we are "HELO 10.10.10.1". Here we are saying, we are this IP; then we define the mail sender with the MAIL FROM command and after we define the mail destination with the RCPT TO command . We have thus part of the mail header.

Then, the DATA command begins to define our email properties. For instance, the day and time in which the mail is sent, in "Date:"; sender data in "From:"; mail subject in "Subject:", destination data in "To:". All of these are the properties that our mail applications read to enrich the information they will show.

We then take the body of the email. Once entered the body of the emal, all we have to do is finish it. To do this we just write..."(point) and we press Enter. This is to finish our email and tell the server that you are ready for it to start sending.

We then disconnect from the email server with this command:QUIT And that is all. Our message has been sent.

The properties X-Mailer and X-Sender are optional, and do not create the mail itself, they just offer information to the mail client so that it can have more data to give the user. For example the first one,X-Mailer, only identifies with what application the email has been sent. In our case with the "M$ SendMail Build 1.0.0019" application. But it can be anything. Even the name of your application.

Fundamental points of the standard

We shall now review some fundamental points of the email standard. We refer here to the commands and properties to enable us to format our message.

You might wonder about things such as the following:

How to define the importance of the email?

We do this specifying two properties in the command DATA: X-Priority and Importance. Let us see an example of how to do this.

We define a normal importance email thus:

DATA
X-Priority: 3
Importance: Normal

A high importance email is defined like so:

DATA
X-Priority: 1
Importance: High

A low priority email is defined thus:

DATA
X-Priority: 5 (Lowest)
Importance: Low

How do we send the same email to more than one person?

You will remember that in order to define the destination address, the RCPT TO command is used. To add more people we only have to repeat the same command for every person we want to send the message to. Then it should be defined in the TO property of the DATA, specifying the different persons separated by a comma (","). With this we are sending the same email to more than one person, but let us see some examples.

We send an email to just one person in this manner:

RCPT TO:
DATA
To: "IANNI, ROBERTO" 

In this way we send an email to more than one person:

RCPT TO:
RCPT TO:
DATA
To: "IANNI, ROBERTO" , "SECOND, MAIL" 

How do we send email with one or more hidden copies?

In order to send an email with one or more hidden copies all we have to do is add all the people involved in the email as destination. For copies or hidden copies we do this, up until now, with the RCPT TO command, and then we define their type in the DATA command.

Here we are going to tell, from all those involved, to whom the TO or CC properties correspond. You might wonder where the hidden copies are. Well, we declare the destination by means of the RCPT command and then we define, for every one of them, whether or not they are TO or CC. If we do not provide anything for the hidden copies, when they open the mail there will be no information at the client, to show the hidden copies. Doing this, we achieve our purpose. He received the email but no one else did.

RCPT TO:
RCPT TO:
RCPT TO:
DATA
To: "IANNI, ROBERTO" 
Cc: "SECOND, MAIL" 

How do we send emails with HTML format ?

This is something that we, as email consumers, are used to utilize often when we want to give a different touch to our messages. Many companies also use this in mass mailings of their products published in the Internet, taking advantage in this manner of the possibility to send HTML in an email.

It is not complicated to include HTML in an email. All we have to do is define the property Content-Type in the command DATA. We call this one HTML and this is enough to be able to include HTML when we start writing the body of the email, and for the client to show it as such. It is also possible to use Java or VB scripts. Being HTML, all properties and facilities provided by it can be used. If the Content-Type property is by passed at the DATA command, what was sent is assumed to be text by default and it will be shown in text format. If we were to send HTML with text format, what the client would see would not be the result of the HTML, but the latter code. Let's see the examples.

Message with Text format:

RCPT TO:
DATA
Content-Type: text/plain;charset=iso-8859-1
To: "IANNI, ROBERTO" 

Mail body with text format
Message with HTML format:
RCPT TO:
DATA
Content-Type: text/html;charset=iso-8859-1
To: "IANNI, ROBERTO" 


   
      HTML window title
   
   
      

HTMl format text

How to have the email answered to another address?

This point is also very used in mailings, which send messages from a garbage address but want to be answered to an existent address. This is not very difficult to do either. There is a property plus the DATA command that we can use to do this: Reply-To, where we indicate where we wish the mail client to respond by default. Let us see an example.

MAIL FROM:
RCPT TO:
DATA
Content-Type: text/plain;charset=iso-8859-1
Reply-To: company@dominion.com
To: "IANNI, ROBERTO" 
As seen in the example, the email is sent by the address , but the mail client is asked to respond to the address just in case the user clicks on the "Answer" button. This solves the problem.

How to specify the email character?

The email character is used a lot when we want to specify beforehand to the receiver how it should treat the email contents.The possible variations are: Normal, Personal, Private, Confidential. As in the previous examples this is also a property specified in the DATA command, called Sensitivity, with a different value for each case, except for the Normal case, which is the default. Therefore the property must not be specified. Here are some examples:

Email of personal character:

MAIL FROM:
RCPT TO:
DATA
To: "IANNI, ROBERTO" 
Sensitivity: Personal

Email with private character:

MAIL FROM:
RCPT TO:
DATA
To: "IANNI, ROBERTO" 
Sensitivity: Private

Email with confidential character:

MAIL FROM:
RCPT TO:
DATA
To: "IANNI, ROBERTO" 
Sensitivity: Company-Confidential

How to ask for a reading confirmation?

We use this when we want to know at what time a person reads the mail, and is no more than just another property of the DATA command: Disposition-Notification-To. Once specified and found by the mail client, it usually asks whether you wish to send the confirmation, but in some organizations mail client applications are used with this property set by default, without user intervention. Let us see an example:

MAIL FROM:
RCPT TO:
DATA
Content-Type: text/plain;charset=iso-8859-1
To: "IANNI, ROBERTO" 
Disposition-Notification-To: "IANNI, ROBERTO" 

Adding files to the email

This maybe the most complicated thing concerning sending an email, because a few more things should be considered. I am going to explain in more detail how to attach a file.

As a first step we have to realize that what we see as an attached file is really no more than a copy of the whole file within the message, in the body portion, as an identifying attribute that shows to it that it begins from a certain part and ends in another. In the middle is found all the text file as it is also in the disk where it is being copied from.

Another thing to clarify is that I am going to concentrate on how to attach a file based on the text format, because binary files have certain characters that have to be replaced before sending them and it is not worth the time, because it does not affect the explanation. For those of you who are as curious as myself, you can find all these explanations in the RFC822 standard, at the addresses already shown.

What we commented should be enough to understand the idea, but, what is it that we specify? Once all the email heading is defined, we have to set a new property of the DATA command, the Content-Type property and tell it that it will be divided in several parts.

One part will be the email text and another one will be the attached file. To do this it is necessary to set the Content-Type property with the multipart/mixed value, with the corresponding ID to identify it within the body of the emial. Let us see an example.

MAIL FROM:
RCPT TO:
DATA
Content-Type: text/plain;charset=iso-8859-1
Reply-To: 
To: "IANNI, ROBERTO" 
   Content-Type: multipart/mixed; boundary="----=_NextPart_000_333d_1506_5025"
This defines that the email has several parts with their corresponding IDs, but we still have not done much more. The first thing to do after this property is added to the email header, is to leave a blank line, as seen, to begin with the body of the email, where the attached file and the message will be lodged.
 "Blank line for the beginning of the body of the mail"
------=_NextPart_000_333d_1506_5025
 "Blank line"
Text test in the body of the mail
 "Blank line"
------=_NextPart_000_333d_1506_5025
Content-Disposition: attachment; filename="Roberto Ianni.txt"
 "Blank line"
Here begins the text file
and here it ends in the second line.-
------=_NextPart_000_333d_1506_5025--
 "Blank line indicating the end of the body of the email"
.
 "Blank line indicating the end of the body of the email"
You will notice that in the body of the email another property that we did not mention is defined; this is the Content-Disposition property and is telling the email client to receive the attachment. Separated by a ";" (semi colon) the name of the file is indicated (filename="Roberto Ianni.txt").

With this we declare all necessary characteristics for the file, but once it ends it is necessary to close the ID with "--" (double hyphen) so that the client understands it. In this manner, when the mail leaves the server it will no longer be text, but when it arrives to the email client, the latter will give it shape and the attached file specified will be seen, and you will not note the difference, as we all are used to do it daily.

This is the end of the explanation on how to send email with Telnet. You will realize that it is not very difficult to do and that no third party tools are necessary, or complicated components.

Developing a FoxPro class to send emails

By developing this class we shall materialize what we said at the beginning of the article: "Develop a class to send email only using FoxPro". You will notice how simple it is now that all the elements are clear in our minds, from how to connect to the email server up to how the protocol really works, and so we can put our hands on to finish this last section.

To be able to develop this class I am going to base it on the WSocket class developed above, so we can connect to the email server without having to use any socket component, as for instance MSWinSocket.

I shall beging the class by inheriting from WSocket and naming it WSendMail. It already has all that is necessary to connect to the server but we still have to add all the logic so it generates the emails. With the functionality provided by WSocket, it will almost be the same as if we were inside Telnet.

Figure 1: SendMail - Logic diagram

The WSendMail class will have public properties and methods so that the final developer will be able to send an email with the utmost ease and very little coding, because all of its logic will be encapsulated.

The only public methods in the WSendMail class will be SendMail and AdjuntarArchivo.

The first one will be in charge of sending the mail with all the characteristics defined into the class before calling the method.

As a first step, it has to create the Socket and subsequently use it in the connection with the email server. Once the connection is established, it has to greet the server and send it the whole email header with the specified properties, as for instance the receiver and the kind of body that will be sent to it. Once all this is finished, it sends the body of the email in the corresponding format and finalizes it with its own command, and then send whatever attached files it has. After this, it only remains to disconnect from the server and exit from the method.

The logic diagram we just described with words, can be seen in figure 1.

The remaining method is AttachFile, in charge of adding each file that is passed as a parameter to an internal array of the class, which is then scanned to send the attached files.

Now we are going to explain the properties that our WSendMail class will have to expose in order to do all the tasks we have been explaining so far.

Table of all properties exposed by WSendMail

PropertyDescription
m_Originator Address of the one sending the mail.
Used by the "MAIL FROM" command.
m_Destination Mail destination address, all receivers should be separated by ";".
Used by the "RCPT TO" command.
m_To Mail address resulting in a complete name for the mail client.
Use by the "To" property.
m_Cc Mail address resulting in a complete name for the mail client (copy).
Used by the "Cc" property.
m_From Mail address resulting in a complete name for the mail client of the mail originator.
Used by the "From" property.
m_RespondTo Address where it is desired that the mail be replied to.
Used by the "Reply-To" property.
m_NotifyTo Address where it is desired that the mail be notified of having been read.
Used by the "Disposition-Notification-To" property.
m_xMailer Name of the application that sends the mail.
Used by the "X-Mailer" property.
m_xSender Name of the mail sender.
Used by the "X-Sender" property.
m_Body Body of the mail, either plain text or HTML.
used by the "Data" command.
m_Subject Description of the mail subject.
Used by the "Subject" property.
m_MailType Mail format. "T" = Text or "H" = HTML.
Used by the "Content-Type" property.
m_Priority Mail priority. "H" = High; "L" = Low o "N" = Normal.
Used by "X-Priority" and "Importance" properties.
m_CharacterType Mail character. "N" = Normal; "P" = Personal; "V" = Private; "C" = Confidential.
Used by the "Sensitivity" property.
m_TimeZone Time zone where mail is sent from, by default it is "-0300" (Argentina).
Used by the "Date" property.

Examples on the use of the WSendMail class

We shall now focus on how to use the WSendMail class already developed, and not on how to develop it, because it is something very simple for us FoxPro programmers and there is nothing strange about it. The only thing it does is implementing the RFC822 standard of SMTP, already explained.

Therefore, our previous questions on how to work with the SMTP and Telnet standards, are to be asked again, but this time with the WSendMail class. We can see that our class is very simple to use, and it has nothing to envy to those third party components currently around the developer market. On the contrary, it has a lot of things to its favour. For instance, it is OpenSource and is completely developed in FoxPro, our language of choice.

All examples to be explained below will assume that the programs defined so far are available to you: WSendMail.prg and WSocket.prg.

Sending a simple email

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_HostName      = 'smtp.ciudad.com.ar'
oMail.m_Originator    = 'ianni_roberto@hotmail.com'
oMail.m_Destination   = 'ianni_roberto@hotmail.com'
oMail.m_From          = '"IANNI, ROBERTO" '
oMail.m_Subject       = 'Our first Mail"
oMail.m_To            = '"IANNI, ROBERTO" '
oMail.m_Body          = 'Body of mail'
oMail.SendMail()
RELEASE oMail
We are thus sending our firs email without using any third party tools. You may notice that I am using the SMTP from Ciudad Internet, but we can use any SMTP, because as you may recall, they are public.

How to define the importance of the email?

To do this just define the priority given in the property m_Priority of our class. Let us see the examples.

We give a high priority to the email:

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_From     = '"IANNI, ROBERTO" '
oMail.m_Priority = 'H'
RELEASE oMail
We give a normal priority to the email:
oMail = CREATEOBJECT( "WSendMail" )
oMail.m_From     = '"IANNI, ROBERTO" '
oMail.m_Priority = 'N'
RELEASE oMail
We give a low priority to the email:
oMail = CREATEOBJECT( "WSendMail" )
oMail.m_From     = '"IANNI, ROBERTO" '
oMail.m_Priority = 'B'
RELEASE oMail

How to send the same email to more than one person?

To do this the standard demands that we repeat the RCPT TO command as many times as people we want to send the email to. Our class will only ask that we put all destinations in the m_Destination property separated by ";" (semi colon). Here is an example.

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_From        = '"IANNI, ROBERTO" '
oMail.m_Destination = 'ianni_roberto@hotmail.com'
RELEASE oMail

How to send an email with copies or hidden copies ?

This is also very easy. You may recall that the standard tells us that we have to send the mail to all destinations and only set in the properties of the DATA command the ones that we want the email client to show. Let us see an example.

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination = 'ianni_roberto@hotmail.com; second_mail@hotmail.com; 
tercer_mail@hotmail.com'
oMail.m_From      = '"IANNI, ROBERTO" '
oMail.m_To = '"IANNI, ROBERTO" '
oMail.m_Cc = '"SECOND, MAIL" '
RELEASE oMail
In this example we are sending the email to three destinations, of which one gets the direct message, another one gets one copy and the third one, which we do not show, gets a hidden copy.

How to send email with HTML format?

We had to use the Content-Type property, but in our class we only have to use the m_TipoMail property and then write the body of the mail with the format previously defined. Let us see an example.

Email with text format:

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination = 'ianni_roberto@hotmail.com'
oMail.m_From        = '"IANNI, ROBERTO" '
oMail.m_MailType    = 'T'
oMail.m_Body        = 'Test text'
RELEASE oMail
Email with HTML format:
oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination = 'ianni_roberto@hotmail.com'
oMail.m_From        = '"IANNI, ROBERTO" '
oMail.m_MailType    = 'H'
oMail.m_Body        = '  HTML window title' ;
            + '

HTMl text format

' RELEASE oMail

How to make the email be responded to into another address?

In the standard, this was done with the Reply-To property and in our class it will be the m_ReplyTo property. Just defining the address is enough for the email client to interpret it. Let us see the example.

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination = 'ianni_roberto@hotmail.com'
oMail.m_From        = '"IANNI, ROBERTO" '
oMail.m_ReplyTo     = 'company@dominion.com'
RELEASE oMail

How to specify the email character?

To do this we informed the Sensitivity property of the standard. In our class we shall inform the m_TipoCaracter property, where we shall specify whether it is Personal; Private, Confidential, Normal. By default the class considers the last character type for the mail. Let us see the examples.

Email with private character:

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination   = 'ianni_roberto@hotmail.com'
oMail.m_From          = '"IANNI, ROBERTO" '
oMail.m_CharacterType = 'V'
RELEASE oMail
Email with Normal character (this is by default so it is not necessary to put it)
oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination    = 'ianni_roberto@hotmail.com'
oMail.m_From           = '"IANNI, ROBERTO" '
oMail.m_CharacterType  = 'N'
RELEASE oMail

How do we ask for reading confirmation?

In the standard we defined the Disposition-Notification-Toproperty. In our class we shall do it in the m_NotifyTo property, specifying the address where reading notification of the email sent should be made. Let us see an example.

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination = 'ianni_roberto@hotmail.com'
oMail.m_From     = '"IANNI, ROBERTO" '
oMail.m_NotifyTo = '"IANNI, ROBERTO" '
RELEASE oMail

How do we attach a file to the email?

To attach files the standard required the definition of the Content-Type: multipart/mixed property and then an ID and with this ID define the body of the email. Separately, the body of the file. In our class it is simpler, because it only requires that we call the AttachFile method with the name of the file as many times as files should be attached. The class will take care of the rest. Let us see an example.

oMail = CREATEOBJECT( "WSendMail" )
oMail.m_Destination = 'ianni_roberto@hotmail.com'
oMail.m_From            = '"IANNI, ROBERTO" '
oMail.AttachFile( 'C:\ParaRoberto.txt' )
oMail.AttachFile( 'C:\OtroParaRoberto.txt' )
oMail.SendMail()
RELEASE oMail

Conclusions

As seen, the SMTP protocol is not very complex. It only requires a little attention to the details so as not to overlook something important. Considering this, we could concentrate in developing a totally FoxPro class,capable of handling all these specifications.

In order to develop the class totally in FoxPro we had to previously implement a WSocket class to connect to the email server, but we have seen that with the help of the Operating System API functions this was very easy to do.

We finally developed an OpenSource class in FoxPro that has nothing to envy the third party products, and which brings us, FoxPro programmers, a lot of advantages from different view points, ranging from economy to scalability.

I hope this article is useful to you. Bear in mind that it can be useful for many other things, as for instance a mailing program.

I thank you for having read this article, it has been a pleasure for me to write it.

Source code

Roberto C. Ianni, Banco Hipotecario
Roberto Ianni (Buenos Aires, Argentina) is a professional software developer for the last 4 years. He has programmed in C++, VFP, VC++, C#, and has lots of experience in VFP and VC++. He currently works in the Banco Hipotecario as an Analyst-Developer.
More articles from this author
Roberto C. Ianni, December 1, 2004
Today we are going to talk about data compression. This is something we do frequently, something which is useful for many different tasks.
Roberto C. Ianni, June 1, 2004
The "EventLog" or "Event viewer" is something we use on a daily basis, to obtain information about what happened to our computer at a certain moment; for example, when an application generates an error, most of us will intuitively look at the EventLog to see what information it left for us.
Roberto C. Ianni, May 1, 2004
The "EventLog" or "Event viewer" is something we use every day, to obtain information about what happened to our computer at a certain moment. For example, when an application generates an error, most of us instinctively go to the EventLog to see what information it left for us.
Roberto C. Ianni, July 1, 2004
One of the great limitations I find in VFP is the lack of low-level manipulation I can obtain from it. For many, this might not be an inconvenience, but several times I have encountered a problem which can't be solved with Fox, and it is then that I use a DLL or an FLL.
Roberto C. Ianni, August 1, 2004
As an objective for this issue, I want to propose the following points, to complete the first part of the development of an FLL. Advanced development with an FLL; Accessing VFP data; Executing VFP commands from a FLL; MultiThreading; FLL with VFP 9.0; Executing Assembler from VFP.
Roberto C. Ianni, October 1, 2004
This article is the continuation of "Sending email through Visual FoxPro without additional components", published last March. Back when I wrote this article, I didn't think it would get such a huge response, but it did, hence, due to the mails coming from everywhere I decided to write again on that...
Roberto C. Ianni, November 1, 2004
In this second part we will learn how to: Implement MIME, Attach binary files, the final implementation of the WSendMail class, and the use of the WSendMail class.
Roberto C. Ianni, May 1, 2005
Windows Control Panel holds the configuration from several of the operating system's applications, and from some other applications that provide an applet to appear there. Learn how to build this kind of applets to give your application a professional configuration mechanism.
Roberto C. Ianni, April 1, 2005
The intention of this article is to finish with the use of POP3 (Post Office Protocol 3), implementing everything we have seen so far.
Roberto C. Ianni, January 1, 2005
This article is the beginning of receiving email from VFP; this is complemented with the articles you have already seen about sending email from VFP. To be able to do this, we will have to use the POP3 protocol (Post Office Protocol 3).
Roberto C. Ianni, February 1, 2005
We will continue some of the points mentioned in the previous article, in order to be able to implement message reception. The points to be developed are: POP3 Authentication (both Flat and MD5 authentication methods) and EMail parsing.
Roberto C. Ianni, August 1, 2006
The present article is one of those that Martín Salías calls "low level fox", the general idea is to develop a service for the operative system whose only goal will be to run a VFP application in charge of handling the business logic.