it has a LOT of fixes in place, works with multi recipients, multiple attachments,
html, text or multipart messages, and has been tested to work with a large range of email clients (yahoo, gmail, outlook, opera, hotmail and others) if you try the earlier versions on this page
you will have issues, getting emails to work under many situations. - Ken. (thanks to the author of the original code for getting this started)

Here's a simple-to-use way to send email without a dependency on any 3rd party OCX, DLL or Application (no Outlook Required!). All you need is to know a usable SMTP server's name or address. Anyone have a simple way to read the SMTP address from Outlook? (to provide the user with a default, and to minimize user error in entering the SMTP address...)

Contributors: wgcs
I suggest changing to GetData(@cMsgIn) and GetData(@Junk) .Anatoliy Mogylevets
Thanks, Anatoliy, I'm sure that will make it more reliable... I believe that my code required SET UDFPARMS TO Reference, and the explicit passing by reference removes that restriction. - wgcs
I used the Send Smtp Email function to send email. It works fine when I send a message without attachement. However, when I try to attach a very small text file to the message, it given an error message on line oSock.GetData.(@cMsgIn). The error message says -- "OLE IDispatch exception code 0 from Winsock. Wrong protocol or connection State for the requested transaction or request."
I am using VFP8 on Windows XP.
Any suggestion? Thanks.
- rktaxali

I suggest changing the commented lines to ASSERTs. That way the developer can run with them on for debugging, and the user can run with them off. A very good application of the infrequently used command. Ray Kirk
I have a couple of issues with these routines. First, when using the routines above, I get a 'Class definition MSWINSOCK.WINSOCK is not found.' error when running on certain machines. It runs fine on other machines. Second, for both I get an 'Unable to relay for [email protected]' error when trying to send e-mail to someone not in my company address book. Is there a way to log on to a SMTPServer in order to fix this issue? Many thanks. -- BarryNewton The file MSWINSCK.OCX must be registered (and licensed for development, I just recently learned) on the machine that this code is running on. Regarding relaying, the FROM Address must be in the domain of the Smtp Server you're talking to, otherwise the smtp server will think you're trying to 'relay'... send mail from this server while not a customer/account holder on this server. - ?wgcs To avoid the development Licensing issue of the MSWINSCK.OCX control, you can change the CREATEOBJECT("MSWINSOCK.WINSOCK") call to CREATEOBJECT("vfpWinsock") and include this class defintion (this code is not thoroughly tested):

. The following sounds like the preferred approach, since it avoids licensing issues etc. However, I'm getting a do nesting too deep where the protected function rd is referencing itself in the call THIS.cIn = THIS.cIn+THIS.Rd(). I could really use a fix for this one ASAP. -- Mike Yearwood.

It seems that line didn't belong there... its functionality happens on the following three lines. Fixed below -- ?wgcs

The 'WSAWait For Multiple Events' call never seems to get its FD_READ event and always waits the entire timeout period before returning. Any Ideas on why this is or how to correct it?
Doug Thomson

Alternatively, don't use the above code "as is". Instead, put it into a form (making a form method for each procedure), and creating one OCX Control Container (called "Sock") on the form for the MSWINSOCK.WINSOCK control, and remove the logic for creating the Sock variable. Change all the references to "Sock" to use "THISFORM.Sock" instead. This is how the WinSock is really designed to be used (embedded in a form at run time), though the only difference, functionally, is M$'s licensing.... - wgcsWS2_32.DLL: sending email messages (SMTP, port 25)
Provide valid SMTP server name, and email address for both sender and recipient to initialize the object.

Certainly you should know your SMTP server name, otherwise there is no reason for you in using this program code :)

Unfortunately you can enter any sender address. This approach somehow hides your identity (except the actual IP address). By any means I do not encourage you to cheat your recipients. Myself, I really hate spammers and unsolicited messages.

This class can be easily modified for sending messages to multiple recipients.

The message body is just a plain text. Some program code should be added to support multipart messages with attachments.

The first routine above includes the ability to send attachments -- wgcs
How can I mark an email as HTML so it show formatted insted of showing all ... tags
PAC pac@cortiel.com

Research MIME: Basically, include a header line that says:
MIME-Version: 1.0Content-Type: text/html

..then stick to the MIME standard for defining the message bodies. -- wgcs
The section:
case ':' $ lcServ
> lnAt = at(':',lcServ)
> lcServ = left( lcServ, lnAt-1 )
> lnServPort = val( Substr(lcServ, lnAt+1) )
will never get the entered port numer as lcServ is already chopped
> lcServ = left( lcServ, lnAt-1 )
needs to be after the get port bit or I guess noone has used this on anything thats not port 25 or you would have noticed it always defaults. I'll let you edit it to however you want, thats all

KenSands Good point... Fixed. - wgcs
I am using the UUEncode method and when I use a linux server for attachments I get the following error message 451 http://cr.yp.to/docs/smtplf.html The Linux server doesn't like chr(10) on it's own. If I use chr(13) with it then it doesn't work at all. Could anyone tell me how to get round this problem.

For unknown reason this line causes ALWAYS 1 second delay. Thus sending
every e-mail takes always 6 seconds, not related to connection speed at all.

2. Violation of SMTP timeouts can cause message loss.
Code waits only 1 seconds for SMTP server response. Server may not respond during 1 second. According to http://www.faqs.org/rfcs/rfc2821.html timeouts must be a lot bigger.
In this case this code terminates TCP connection and message may
be not sent.

and I also added the boundary header "--Ipgo_140520060124" and tailer "--Ipgo_140520060124--" to bound my message, but receiver cannot display the message in the mail box. What can I do ?

Edward ( edwkong@gmail.com )
Newest Version
Feel free to edit this into a topmost example or whatever to keep it all tidy, I've done lots of testing on the code and through telnet sessions with ms-exchange and hmail I've discovered a range of issues with the code, from timing out to early to all the return codes being off by one!. Below is my modified version which includes login, html format emails, and attachments encoded as base64 (this needs a dll I tried base64 encoding in fox but it was very slow, so I made a dll in c) it's used in a production environment and now works very well (at least for me!).

Thanks Barry, it was one of those mistakes you introduce when trying to remove all the non generic bits that relate to your company (it used to be "mycompanynameboundary"). I'll sort it in the main code section.

-Ken

I've made another ammend there, the boundary line should not end with a semicolon, surprisingly it's been running for over a year now and we've only just had an email cause issues due to this, and it was connected with it getting processed trough a email scanner. anyway, thats now correct.

CDO also doesn't work half the time, gets messed around with security issues and does not suit a slim automated system. it's based on that computers outlook settings, so how do you send email via different email servers, or using different accounts? and what happens when outlook gets an automatic update restricting the cdo interface yet again? for those reasons CDO is not always a good solution. when setting up a system to send emails in a production environment this works a lot better, at least in my experience. My version also includes options of html, plaintext, or multipart, and attachments, I use all these options - Ken

Has anyone managed to get a version of this program to work without WinSock?

I had the WinSock version working beautifully when following the instalation of a new AntiVirus Program (CA AntiVirus) and running SpyBot to get rid of some spyware, the program stopped working. Now I get the error "Class definition MSWINSOCK.WINSOCK is not found" mentioned above. One of these programs deregistered the WinSock and stuffed up the software. None of the program logs indicate what was done. I have removed all software but the error still happens.

I tried using vfpWinSock but Send SMTPEmail still returns a FALSE value.

OUTCOME of investigations...
1. inserting vfpWinSock class after the Send SMTPEmail.prg DID ACTUALLY WORK. The program just returned a false value.
2. working through the program with the debugger I found that the 'error' was due to the sock program not being able to quit while sending an encoded attachment. If you delay the program 20-30 seconds while the request is processed the quit command works.
3. for some reason this behaviour is only apparent when using the vfpWinSock class - MSWinSck.ocx did accepted the command without generating an error.
4. I have marked the relevant point to insert a delay in the above program.

---Problem on sending multi receipants and attachments

I tried to use Sendsmtpemail, it works properly for single receipant and single attachment, but I want to send to multiple receipants with 2 or more attachement, it failed.
What should I do ?

---Send SMTPEmail has a parameter, oFB_Attachments, which allows multiple attachments. File names are separated by commas. See restrictions in comments at top of program. You can send multiple emails by creating a program that works through a table and calls Send SMTPEmail with the relevant parameters. Simon Cropper.

---
I send emails to mutiple recipients with multiple attachments no problem, are you using my version of the code? the last one on this page? - ken.

---

Hi Guys,

I recently encountered a problem, which I could use some hints in solving. I send a newsletter to about 150 of my clients using Send SMTPEmail - mailout program scans table of clients, uses Send SMTPEmail to send an email with PDF attachement, waits 40 seconds for the file to be encoded and sent, then moves onto the next client. Occassionally people emailed me that the file was corrupted and asked for me to resent the attachement. The second attempt always worked. They don't change their program or anything they do, nor do I, it is just that the second attempt isn't corrupted or something.

The last mailout resulted in about 10% of the 150 not working. Most complained that the file was coming as an mime-encoded inclusion and was not able to be displayed in the body of the text. I resend the file, either from my foxpro program or outlook, and they have no problem with getting the email or opening the attachment (PDF). It appears the second attempt has all the bits and flags inside the email in the right place. One person had Mail Marshall state that there could be a virus in the attachment. The file and attachement were clean.

I have emailed 120 copies of the same email and attachement to myself with my virus scanner on/off, outlook on/off and IE7 on/off. Despite trying I can't reproduce the problem.

Anyone got any ideas what might be happening here? It appears that the Send SMTPEmail does something that can be misinterpreted by different email programs.

I intend to ask everyone who has had a problem a range of questions about their system to try and identify the problem but I am not sure what I should be asking. Any guidance here would be helpful as well.

All the people that responded had Lotus Notes. It appears this program can do funny things with MIME-encoded data depending on the configuration, etc. What is confusing though is that if I send them emails from outlook they work. So as a work around I have flagged clients that report an error using the Send SMTPEmail program and save an email for them in Outlook using Office Automation. I can then go to Outlook and send the files manually. Not a big problem for a few people, but could be a pain for lots.

Simon Cropper
Simon, I was encountering a similar issue with email filter software causing issues at the point of scanning, I put something in to do with email boundries which fixed it, I'm not sure exactly what it was now though. is the problem occuring using the last version on this page that I put up? - Ken.

17/01/2008 -- After a long delay I finally worked through this problem and seem to have resolved this issue. I tried to run the last program on this page without any success -- problems with Winsock Registration. I was therefore required to modify my existing hybrid program that uses vfpWinSock. I correctly presumed, as mentioned by you, the problem would be in either the encoding routine or in delimiting of the attachments. I therefore went through the SendEmail program and compared it line-by-line to Send SMTPEmail program and modified these sections appropriately. It worked.

Simon CropperThere is always "RCPT Failed" error when I send to single receipant
When I send a plain text email to single receiver, it always show "RCPT Failed", why ? please help !!
Edward
I was getting a "Error 501 5.0 ehlo requires domain address". Changing the line

IF NOT readwrite(sock,"EHLO", 250)

to

IF NOT readwrite(sock,"EHLO localhost", 250)

fixes the problem.
fixed an attachment semi bug the code should have been CHRTRAN not STRTRAN to sepatrate multiple attachments - Ken
Just as feedback for future users.

Take care when using these programs as they use !="" alot. As I found out through trial an error while trying to clarify why my email program suddenly stopped working -- !="" is not treated the same with SET EXACT ON and OFF.

For example, the IF b64file != "" command below is only positive if SET EXACT is ON; if OFF it is negative. I changed the command to IF NOT b64file == "" to get the correct result.

Simon Cropper
Here is a peculiar issue that occurs in both my modified Send SMTPEmail() that uses vfpWinSock and the SendEmail() program listed at the bottom of this page that uses WinSock.

If you send an email message (no attachements) that is over 990 characters long something in the program cuts it up and inserts a carriage return followed by a space. I have tried to track down the culprit but it seems to be occurring in vfpWinSock and WinSock. My debugging indicates the message text is intact when handed to ReadWrite() to send.

To see what I mean, try sending 1111111111222222222233333333334444444444...(reiterate so you have several thouusand characters) to yourself. Look at an email.

Essentally what happens is that the initial segment is 990 characters followed by CHR(13)+" ", then 989 characters followed by CHR(13)+" ". The last 989 chunks are repeated over and over. No data seems to have gone missing, it is just that various words in my emails get spaces inserted in them. To date this has gone unnoticed in large text files but have become a problem in large HTML files due to hyperlinks and commands being broken into two parts.

Attachments seem to get sent without any problems.

Has anyone encountered this problem before? Anyone know what is going on?

Cheers Simon Cropper (scropper_AT_botanicus-aust.com.au)

27/02/2008 -- I have continued trying to debug this problem and found that it is not confined to SendEmail. Using the CDOEmail() functions from http://fox.wikis.com/wc.dll?Wiki~CdoEmail also generates the same problem. Files over 990 characters long are split into 990, then 989 sized, blocks separated by chr(13)+" ". It does not seem to matter whether the data is sent as HTML or TEXT. The fact that I get the problem whether I am using SendEmail()/CDOEmail() which uses Microsoft's WinSock or Send SMTPEmail() using vfpWinSock, I am pretty sure it is not the WinSock Library. The fact that I can send any sized email from Outlook without any problem also supports this. I am using Office2000 + XPPro SP2 + VFP9. Simon.

28/02/2008 -- Ken was unable to reproduce the error on his system so it appears that the issue is centred around my computer or ISP. Ken suggested it might have something to do with email spam filtering or virus scanning. I have turned off everything I could to no avail. What I did discover was that the problem occurs in all the programs I could find - BLAT, SendEmail, Send SMTPEmail and CDOEmail. It does not occur in Outlook. All use WinSock. The other interesting fact was that the problem only appears if the length of a string of characters between carriage returns in the email goes over the 990, 989, etc limit. That is, the problem only appears if you create a continuous string over 990 charcaters long. My work around is to insert CHR(13) where ever I can in my emails. Bye and bye, spaces don't count.

05/03/2008 -- I could not find util.prg (used in the code), so I reproduce how to get timezone from windows below. The good thing about putting this into your code is that it is one less dependency. Tony Wallace

[2009.05.16 10:07:09 AM GMT] Could anyone highlight the final workable version for usage ?!

I've only just notice the date thing myself, it was a few errors in the time code right from the original, I never looked into it because it didn't break the code it just put slightly incorrect send times into the emails.
I've fixed that now, there is no reference to UTILS and the daylight saving time bias works. again I've fixed it in the last full code example shown above. - Ken

I've also added guids to the temp filenames used in base64 encoding, this allows this code to be called multiple times without the tempfiles overlapping and sending the wrong attachments! code for the guid bit was taken from the wiki at http://fox.wikis.com/wc.dll?Wiki~GUIDGenerationCode
- Ken

[2010-11-16]I've successfully used this code to send emails with attachments, but am unable to get it to CC: someone. I've added a new parameter (emailCC) and relevant code to pass a CC: email address in to the function and altered the lcOutStr to read as follows:

The email will still reach the TO: recipient, but the CC: recipient receives nothing. Any ideas?
regards, Ric. Hi Ric... I'm pretty sure that CC recipients are NOT handled by adding header entries like this. (Some mail programs, like SENDMAIL, might receive and parse the CC: header, than obey the request to send the mail to the specified recipient(s).) Instead, I believe CC recipients are specified in the initial SMTP conversation with the "RCPT TO:" commands. The .SendMail function could loop twice, issuing "RCPT TO:" each of the TO: addresses, then issue it once for each CC address that was specified separately. BCC recipients especially should be handled like this so that their addresses do not show up in any of the TO: or CC: recipients' email headers. -- ?wgcs

Hi William, many thanks for response! You are spot on with that - I had misread the SMTP entry in Wikipedia and missed the bit about adding "RCPT TO:" for each CC: recipient. Adding the CC: Header just displayed the name in the actual email without actually sending it to them. I've now added this extra code to issue "RCPT TO:" for the CC address (placed after the code for "RCPT TO:" for the TO: recipients) and it all works now. Many thanks for your guidance. Ric.

---
-Stelios---
Just to mention that you have to make the directory BASE64TEMP and in the line
lcoutfname = "C:\BASE64TEMP\base64temp"+Alltrim(lcguid)+".txt"
lead it to corect path
Also i have this error
If iCode == iExpectedCode &? iCode <= iemax
Error in line 495: Command contains unrecognized phrase/keyword.
but I don't mind I just comment out the whole if Hi Stelios: I'm not sure what the exact intent of that code snippet was... but it is clear that a bug creeped in when it was posted. It should either be:
If iCode == iExpectedCode && iCode <= iemax
or
If iCode == iExpectedCode AND iCode <= iemax
The first just has a comment at the end of the line ("&& iCode <= iemax") and the second adds an odd restriction to when .T. can be returned (the odd restriction is that the iExpectedCode provided to the function must be less than or equal to iemax (apparently a calculated maximum valid error code...). The logic does not look correct to me, so I think that "iCode <= iemax" was just intended to be commented out at the end of the line. I fixed the code snippet to avoid this error. -- ?wgcs

It was used when I was debugging the code for different smtp return codes, I think I had just the iCode <= iemax bit in place for a while to allow it to go through, as it seemed different servers were replying with different codes yet still working, however that was solved by tweaking what was sent through to be something compatible with at least all the servers I tested it with. - Ken

In attempting to adapt this code for my needs, I found that it would be helpful for ReadWrite() to output whatever the server sends back when the command is successful. This lets me collect the list of advertised authentication methods so I can fork the code based on the server's requirements. So I modified ReadWrite() as follows: