|
|
Comments and Discussions
|
|
 |

|
Hi David,
Can I used this code in commercial applications,
Thanks.
|
|
|
|

|
Hi,
i know we got this Topic already here but this is 3 years ago and the solution doesn't work at me.
So my question:
I want to compile the Project files in Dev-C++.
What i did:
-Download your package.
-Put the necessary files (*.h and *.cpp) into a new Project file.
-Add "openssl-0.9.8l\inc32" to Additional Include Directories with clicking on: Project -> Project Options -> Directories -> Include Directories
-Deleted the following lines in CSmtp.cpp:
#ifndef LINUX
#endif
Downloaded the openssl package for Dev-C++ from here: http://sourceforge.net/projects/devpaks/files/openssl/[^] and installed it with the Package Manager.
Then I clicked: Project -> Project Options -> Parameters -> then in the Linker Box:
-Ws2_32.lib
openssl-0.9.8l/out32/libeay32.lib
openssl-0.9.8l/out32/ssleay32.lib
But now i get 500 Linker Errors:
https://dl.dropboxusercontent.com/u/22986507/Linker%20Errors.txt[^]
The openssl Folder is in the Project Folder. So the structure is like that:
https://dl.dropboxusercontent.com/u/22986507/Data%20Structure.pdf[^]
Thanks for your help and i hope you understand me
|
|
|
|

|
I'm not familiar with Dev-C++ so I can't be of too much help. What I can say, however, is that when I first got started with this project I was using a different version of MSVC than what the project was using so I had to recompile OpenSSL with my compiler to get it to link properly. Have you compiled OpenSSL on your own Dev-C++ compiler or are you using a precompiled version that could have been compiled with a different version of Dev-C++?
David
|
|
|
|

|
Hi, thanks for your help.
I am using your precompiled OpenSSL. I only copied it into my project folder.
How i can compile it properly? Only compile every *.h for it own?
Wolkenpaul
|
|
|
|

|
I'm guessing you can find a guide on how to compile it using Dev-C++ on the OpenSSL website. I think that's where I found a guide for compiling it with MSVC.
|
|
|
|
|

|
If you attempt to send an attachment that is greater than the default limit of 5MB (say 6MB, for example), the program waits indefinitely until the server times out and closes the connection and then proceeds to crash.
Here's the sequence:
1. Start sending attachment.
2. Notice that the file is too large, throw exception MSG_TOO_BIG
3. Catch exception and call DisconnectRemoteServer.
4. Send QUIT command.
5. Waits for response.
6. Server times out waiting for message data.
7. My server responded with: 421 SMTP incoming data timeout - closing connection.
8. Server closes connection abruptly.
9. Select returns with response received in step 7 (421...)
10. Checks response.
11. 421 is not a valid response for QUIT.
12. Throws exception
13. Program aborts because it is a new exception is thrown that is unhandled (at least in the example program).
The underlying problem here (at least in this case) is that the QUIT command is not valid because it is currently inside the message data portion. The message data must be ended first before the server can start accepting commands again. I suggest sending the end of message line before throwing for the failed attachment. Also perhaps it's better to check the file's validity before even putting out the MIME header data for it.
The secondary problem is the unhandled exception. I suggest wrapping the call to DisconnectRemoteServer (from inside the Send catch handler) with its own try/catch block. These exceptions are then trapped and ignored. Unfortunately I'm not sure what to do about the original exception. I've tried different methods, but in each one I've tried the program aborts when rethrowing the original exception.
Hopefully this makes sense. There may be other failure points inside the message data section as well that have this issue, but this is what I found so far.
|
|
|
|

|
GKarRacer,
Which version of the project are you using? This is one of the primary issues resolved in version 2.2 of the code - at least I think. I just want to make sure if you are seeing an issue with it that you are on the latest version of the project.
Thanks,
David
|
|
|
|

|
I have version 2.1. I had downloaded it fairly recently and didn't realize there was an update out already. I will check it out. Thanks.
|
|
|
|

|
I took a quick look at 2.2. I haven't had a chance to run it yet, but the attachment handling does look better. Thanks.
There is still an issue, however, with exceptions occurring within DisconnectRemoteServer. If an exception occurs while calling this, the program most certainly will crash due to nested exceptions or worse by throwing from within the destructor. I think for safety all calls to DisconnectRemoteServer need to have its own try/catch block wrapper and these exceptions should not be forwarded onto the caller. The destructor for CSmtp especially needs this.
To get around the double exception problem in the Send function I mentioned earlier, here's a suggestion: Don't call DisconnectRemoteServer from within Send's catch handler. Let the destructor handle it during the normal stack unwind from the original exception. Make sure, of course, to wrap DisconnectRemoteServer in the destructor within its own try/catch block. If you do this Send doesn't even need its own try/catch block.
Come to think of it instead of always wrapping DisconnectRemoteServer, you could simply have the DisconnectRemoteServer function have its own internal try/catch block that eats all exceptions.
I think ConnectRemoteServer also may have the same issue as Send.
What do you think?
|
|
|
|

|
When sending attachments you read the entire file to calculate the file size. This seems highly inefficient. There are, of course, various operating system specific ways to get this information, but even with straight C++ you can do the following:
fseek(hFile, 0, SEEK_END);
TotalSize = ftell(hFile);
fseek(hFile, 0, SEEK_SET);
The only problem with this is if the total size of the file is greater than 4GB (clearly too big for an attachment, but you know those crazy users...). With VS, at least, you can use _ftelli64, but I don't know if there's a cross platform equivalent. You're current code also will fail with excessively sized files. Admittedly though, this is unexpected input.
|
|
|
|

|
Sorry, the second line should be:
FileSize = ftell(hFile);
TotalSize += FileSize;
or simply:
TotalSize += ftell(hFile);
|
|
|
|

|
GKarRacer,
Great point. Also, in the process of putting this in, I realized a couple of things that aren't good about rev 2.2. Now it reads the attachments one time just to check for total size and throws an error there if it is too large. However, in doing so:
1. Like you mentioned, it reads the whole file in the process just to find out how large it is.
2. It doesn't close the files so they will be left locked.
3. It still throws an error in the middle of sending so that if the file size changes between when it checks the first time and when it sends them, it could still end up in limbo.
To fix these problems I've made the following changes.
Starting on line 533:
TotalSize = 0;
for(FileId=0;FileId<Attachments.size();FileId++)
{
snprintf(FileName, 255, Attachments[FileId].c_str());
hFile = fopen(FileName,"rb");
if(hFile == NULL)
throw ECSmtp(ECSmtp::FILE_NOT_EXIST);
fseek(hFile, 0, SEEK_END);
FileSize = ftell(hFile);
TotalSize += FileSize;
if(TotalSize/1024 > MSG_SIZE_IN_MB*1024)
throw ECSmtp(ECSmtp::MSG_TOO_BIG);
fclose(hFile);
}
and then after making that change, starting on line 647:
fseek(hFile, 0, SEEK_END);
FileSize = ftell(hFile);
fseek (hFile,0,SEEK_SET);
MsgPart = 0;
for(i=0;i<FileSize/54+1;i++)
{
res = fread(FileBuf,sizeof(char),54,hFile);
MsgPart ? strcat(SendBuf,base64_encode(reinterpret_cast<const unsigned char*>(FileBuf),res).c_str())
: strcpy(SendBuf,base64_encode(reinterpret_cast<const unsigned char*>(FileBuf),res).c_str());
strcat(SendBuf,"\r\n");
MsgPart += res + 2;
if(MsgPart >= BUFFER_SIZE/2)
{ MsgPart = 0;
SendData(pEntry); }
}
if(MsgPart)
{
SendData(pEntry); }
fclose(hFile);
|
|
|
|

|
how to send email through a proxy?
does it have the setproxy function?
|
|
|
|

|
ASERERTA@#@s,
Sorry, this capability is not currently supported. If you want to develop it and post back we could look into adding it.
Thanks,
David
|
|
|
|
|

|
In DelMsgLine, ModMsgLine, GetMsgLine you have an "if" check to validate the range of the requested line. However, the check is incorrect.
You have: if(Line > MsgBody.size())
This should be: if(Line >= MsgBody.size()).
Otherwise you'll allow one potentially bad index through.
|
|
|
|

|
Good catch. I'll get that in the next release.
|
|
|
|

|
CSmtp.cpp needed #include for gethostname().
On Linux, I was able to use the already-installed openssl using pkg-config rather than the openssl subdirectory in the download.
Thanks!
-JimFred
|
|
|
|

|
Jim,
Thanks for the feedback. Can you please try adding it to CSmtp.h instead just below
#include <iostream>
I just want to verify it will work as expected before adding it to the next release.
Thanks,
David
|
|
|
|

|
Hi:
Any one here has been abel to test it with an email from live.com, outlook.com or hotmail.com?
or any email address using the smtp.live.com smtp service?
I need help!!!
I've been able to send with almost all service around but no way I can send with live.com SMTP.
|
|
|
|

|
Have you tried watching the communication log to see what messages are being passed back and forth between the server and the client?
|
|
|
|

|
Yes I had.
The TLS link is successful.
The SSL error comes after sending the AUTH command to the SMTP
|
|
|
|

|
Which AUTH type is it using? Also, can you please confirm the version of the code that you are using by looking at the header of CSmtp.cpp and telling us the highest version listed?
|
|
|
|

|
Hi:
Thnaks for the support, I feel like you are my Obi-Wan Kenobi
Microsoft forum after some none technical responses blamed OpenSSL.
I've done the test directly with your code and something similar happens, hope you can help me.
The CSmtp.cpp is version 2.1 (Version 2.1: Updated with fixes reported as of 26 Mar 2012)
This is what I've tested:
STARTTLS
AUTH LOGIN
tried with smtp.live.com (port 25 and 587):
- get SSL error in response after sending AUTH LOGIN
tried with smtp.outlook.com (port 587)
- send AUTH LOGIN
- got request login
- send login
- got request password
- send password
- get SSL error in response after sending the password
thanks again.
|
|
|
|

|
Ok - you have me stumped now. I guess I'm not going to be able to help you from here, young Jedi.
There were some modifications made to the AUTH PLAIN implementation, which I'll eventually release in version 2.2 of the code as described here:
http://www.codeproject.com/Articles/98355/SMTP-Client-with-SSL-TLS?msg=4442931#xx4442931xx
However, I'm not aware of an issue with AUTH LOGIN. For that matter, as I think you postulated, this seems to be an issue with the SSL library rather than with the email library itself. Please post back if you find and fix the problem.
Thanks,
David
|
|
|
|

|
This code works the same if the recipient email address is valid (e.g. someone@zabkat.com) or invalid (e.g. nosuchemail@zabkat.com). The SMTP conversation is the same, and sending to invalid addresses (mailboxes) is allowed by the server:
250 Message 0Mfeu7-1UCqaK1wMd-00P9eB accepted by mreu3.kundenserver.de
is there any way that the "discussion" with the SMTP server can continue after sending to check if the mailbox exists or not?
I read somewhere that the server should validate the recipient email in response to:
RCPT TO:
but it doesn't!
|
|
|
|

|
I'm guessing what you read said that the server was responsible for validating the address, meaning as opposed to the client being responsible for that. That process does not take place until after the mail submission process is completed. Therefore, I don't think smtp allows you to receive this feedback during the mail submission process and cancel the mail submission if the address is not valid.
|
|
|
|

|
Thanks for the wonderful piece of code. God bless you. People like you make the internet such a big success. Thanks again.
|
|
|
|

|
Hi everyone,sorry about my poor english,wish you guys can understand what i say.
I compiled the codes in a static library and call it in my C project,and then my program terminates with this message:terminate called after throwing an instance of 'ECSmtp'.
Fortunately I found the reason:DisconnectRemoteServer() would throw exception if mistake occurs,and it is called by the function CSmtp::~CSmtp().I think we should not throw an exception out of a destructor,isn't it?
|
|
|
|

|
Hi,
Nice project! It really help me to send emails with TLS.
But I've tried to build a DLL with all your classes and I've got many problems when I generate my project.
Have you tried to generate dll instead of exe?
How can I do easily?
When I generate your project adapted to an output dll, I've got these errors:
libeay32.lib(cryptlib.obj) : error LNK2019: unresolved external
symbol __imp__GetUserObjectInformationW@20 referenced in function _OPENSSL_isservice
And 17 more like this...
I'm using VS C++ Express 2008.
Thank you
|
|
|
|

|
Hi,
Could you please post the full error list? I have successfully compiled it as a DLL library without any error.
|
|
|
|

|
I don't remember exactly the full error list but it was all about libeay32.lib.
So I've decided to compile OpenSSL source code, version 0.9.8l. From there, I've used the new .lib files that I've replaced by yours.
And when I've recompiled your project, it was a success!!
Maybe on your machine, some components are installed and not on mine. I don't know...
|
|
|
|
|

|
Hi,
how can I obtain a copy of the sending mail?
10x
Sandro
|
|
|
|

|
Maybe I don't understand what you mean. If you mean the library this article documents, you can download it by clicking the "Download Source" link at the top of the page.
Thanks,
David
|
|
|
|

|
Hi,
I apologize for my bad English,
I mean if is it possible, when sending an email (by object CSmtp), save a copy of it to disk (eml files).
10x
Sandro
|
|
|
|

|
Sandro,
No problem! I just didn't understand your first post. The library doesn't currently offer that feature. If you develop it, please send me the code so we can share it with everyone else.
Thanks,
David
|
|
|
|

|
I'm running this on OSX 10.7.4, which has OpenSSL 0.9.8 installed, and it is working great. When using TSL or SSL to connect to GMail, Instruments is reporting several memory leaks in LibCrpyto: CRYPTO_malloc. When using no authentication and connecting to a local SMTP server, I see no memory leaks.
Is there some free command I didn't use? the CSMTP object is created as a local variable, and is deleted when it goes out of scope. The CleanupOpenSSL function is being called.
Thanks
|
|
|
|

|
As long as you are either not manually setting "m_bConnected=false" then DisconnectRemoteServer() will be called on the destructor. That's the only pitfall I could see.
|
|
|
|

|
No, I am not manually setting disconnected to false, and the DisconnectRemoteServer() is being called on the destructor.
I commented out the send function, and just do connect/disconnect, and it is clearly leaking memory. I wonder if there is a difference because it is on OSX.
|
|
|
|

|
It could be. Please post back a solution if you find one.
Thanks,
David
|
|
|
|

|
The SMTP sender I have can send to all the SMTP relays I tested but Microsoft smtp.live.com
I'm able to connect but after sending the "AUTH XXXX" (be LOGIN or PLAIN) the next receive just give an unknown error and go to the catch, but I get no useful error info.
Any help?
|
|
|
|

|
use mail.SetSMTPServer("smtp.live.com",587);
|
|
|
|

|
I did test it way, I just did it again to be sure
The STARTTLS connect fine, the problem is when sending the LOGIN XXXX command
the receive response just through an exception and to the catch.
|
|
|
|

|
this code is for CRAM-MD5
tested and working
I need to remove the try/catch but is easy to ad again.
else if(IsKeywordSupported(RecvBuf, "CRAM-MD5") == true)
{
pEntry = FindCommandEntry(command_AUTHCRAMMD5);
strcpy(SendBuf, "AUTH CRAM-MD5\r\n");
SendData(pEntry);
if (m_nError)
return false;
ReceiveResponse(pEntry);
if (m_nError)
return false;
std::string encoded_challenge = RecvBuf;
encoded_challenge = encoded_challenge.substr(4);
std::string decoded_challenge = base64_decode(encoded_challenge);
unsigned char *ustrChallenge = CharToUnsignedChar(decoded_challenge.c_str());
unsigned char *ustrPassword = CharToUnsignedChar(m_sPassword.c_str());
if(!ustrChallenge || !ustrPassword)
{
m_nError = BAD_LOGIN_PASSWORD;
return false;
}
// if ustrPassword is longer than 64 bytes reset it to ustrPassword=MD5(ustrPassword)
int passwordLength=m_sPassword.size();
if(passwordLength > 64){
MD5 md5password;
md5password.update(ustrPassword, passwordLength);
md5password.finalize();
ustrPassword = md5password.raw_digest();
passwordLength = 16;
}
//Storing ustrPassword in pads
unsigned char ipad[65], opad[65];
memset(ipad, 0, 64);
memset(opad, 0, 64);
memcpy(ipad, ustrPassword, passwordLength);
memcpy(opad, ustrPassword, passwordLength);
// XOR ustrPassword with ipad and opad values
for(int i=0; i<64; i++){
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
//perform inner MD5
MD5 md5pass1;
md5pass1.update(ipad, 64);
md5pass1.update(ustrChallenge, decoded_challenge.size());
md5pass1.finalize();
unsigned char *ustrResult = md5pass1.raw_digest();
//perform outer MD5
MD5 md5pass2;
md5pass2.update(opad, 64);
md5pass2.update(ustrResult, 16);
md5pass2.finalize();
decoded_challenge = md5pass2.hex_digest();
delete[] ustrChallenge;
delete[] ustrPassword;
delete[] ustrResult;
decoded_challenge = m_sLogin + " " + decoded_challenge;
encoded_challenge = base64_encode(reinterpret_cast(decoded_challenge.c_str()),decoded_challenge.size());
sprintf(SendBuf, "%s\r\n", encoded_challenge.c_str());
pEntry = FindCommandEntry(command_PASSWORD);
SendData(pEntry);
if (m_nError)
return false;
ReceiveResponse(pEntry);
if (m_nError)
return false;
}
|
|
|
|

|
Are the only fixes:
1. Changing the throw ECSmtp(ECSmtp::BAD_LOGIN_PASSWORD); to { m_nError = BAD_LOGIN_PASSWORD; return false; } - Not sure I see the need in that one.
2. Adding error checking after the SendData(pEntry); and ReceiveResponse(pEntry); at the end? - This must relate to something you added because there is no m_nError member variable. Can you please explain?
|
|
|
|

|
there are changes that are specific to what I'm using it for. But the original code did not work for me.
the same for the other post I made.
|
|
|
|

|
this will fix the AUTH PLAIN process
also the MD5-DIGEST is broken but you should not used it anymore
else if(IsKeywordSupported(RecvBuf, "PLAIN") == true)
{
pEntry = FindCommandEntry(command_AUTHPLAIN);
unsigned char ustrLogin[200];
ZeroMemory( ustrLogin, 200);
memcpy(ustrLogin, m_sLogin.c_str(), m_sLogin.length());
memcpy(ustrLogin + m_sLogin.length() + 1, m_sLogin.c_str(), m_sLogin.length());
memcpy(ustrLogin + 2 * m_sLogin.length() + 2, m_sPassword.c_str(), m_sPassword.length());
std::string encoded_login = base64_encode(ustrLogin, m_sLogin.length() * 2 + m_sPassword.length() + 2);
pEntry = FindCommandEntry(command_PASSWORD);
sprintf(SendBuf, "AUTH PLAIN %s\r\n", encoded_login.c_str());
SendData(pEntry);
ReceiveResponse(pEntry);
}
|
|
|
|

|
Doesn't this get the exact same result as the current code? Is there some portability issue with how it is currently written?
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
C++ SMTP client, support SSL and TLS encrypted connection to SMTP server
| Type | Article |
| Licence | CPOL |
| First Posted | 3 Aug 2010 |
| Views | 223,692 |
| Downloads | 11,720 |
| Bookmarked | 131 times |
|
|