|
I'm using it in a comercial app and its connecting to non SSL/TLS servers without problem.
Just check the parameters you are setting.
|
|
|
|
|
Thanks for your response. You're quite correct. The code works just fine sending emails to non-secure servers. I'm not sure exactly what I was doing wrong but no doubt it was some faulty parameter settings. I'm not having much luck with attachments though - in fact, no luck at all. Keep getting the error : 'Attachment does not exist' or some such. I've been using mail.AddAttachment(full_path) but even if I get no error, no attachment shows up on the recipient end. I read through all the comments and realize that there are some issues others have had with attachments - I'll read through them again. FWIW, it's not much of a problem to run in Unicode environment (VS2010, 64-bit), but that sort of begs the question about sending foreign language, e.g., Chinese, email messages. Seems to me that won't be possible until the OpenSSL libraries are updated to handle Unicode - or have I missed another important point here?
|
|
|
|
|
It sounds like you are figuring things out. Two things I thought I would chime in, though:
1. Gmail does use a secure SMTP connection, TLS to be specific
-and-
2. I just wanted to make certain that when you said you are using the
mail.AddAttachment(full_path) that you mean you updated your code per the comment below to
hFile = fopen(Attachments[FileId].c_str(), "rb");
Thanks,
David
|
|
|
|
|
Thanks David. That fixes the problem. The code works beautifully from what I can tell. Thank you not only for making it available but for responding to all the comments. An outstanding effort.
|
|
|
|
|
Fortunately we are getting a lot of good crowdsourcing on this project. Glad its working out for you. Make sure to communicate back any fixes or additions you make to it.
|
|
|
|
|
David with Gmail you can use both TLS and SSL just need to select the correct port for each.
|
|
|
|
|
Thank you for this excellent set of classes, which will really help me out in my current project.
In the CSmtp::Send() function, when the attachment file is opened for a second time so that its contents may be passed to SendData(), I was wondering if the line that reads...
hFile = fopen(FileName.c_str(), "rb");
should actually be...
hFile = fopen(Attachments[FileId].c_str(), "rb");
...so that files which are not in the current working path can still be attached ?
(If so, then I'm guessing that the line as is stands now was correctly used to strip the path from the file name as displayed by the email client and then accidentally copied for this other context) ?
Best Regards,
Graham
|
|
|
|
|
It looks like you are right. Thanks for the input!
|
|
|
|
|
Hi:
I had no crash problem with the code until I came across a client with what look an slow server and randomly crash my module based on CSMTP in the call to WSACleanup() after CleanupOpenSSL() in class CSmtp destructor.
The message I sometimes got has to do with cryto_free and looks like a data structure become corrupted or double freed.
I used the CSmtp code in dll to enable our application to send emails to a SMTP server,
What is happening is if our app strat more that 3 threads using the dll the crash start to random stop the app. This app is windows service reading a queue and sending emails from that queue.
If I reduce the thread count to 1 it works perfect. And so far 3 threads looks fine too, but as soon as I got pass 3 the crash starts random.
Any thought will be much appreciated.
|
|
|
|
|
Are you saying it had three simultaneous connections to the same SMTP server? If so, could it be that the server only allows 2 or 3 connections and refuses any beyond that?
|
|
|
|
|
I could be that but anyway it should not crash, it should give an error.
I found yesterday that it could be caused by SSL initialization more than once.
I will move the SSL initialization to the DLL init, and the SSL release to the dll release to see if that fix the isuue.
Thanks
|
|
|
|
|
I just find out the issue.
The initialization and release process for OpenSSL can only be done once.
With the initialization you can do it many times and will not affect but you could be leaking some memory.
With the release/free process you should do it just at the end of the use you are doing of OpenSSL.
hope it help some one, it took me some time to refactor so to avoid this.
|
|
|
|
|
i have noticed that Bcc recipients are added to header, i think this will cause all other recipients can see other mails.
This code is in "FormatHeader"
if(BCCRecipients.size())
{
for (i=0;i<BCCRecipients.size();i++)
{
if(i > 0)
bcc.append(",");
bcc += BCCRecipients[i].Name;
bcc.append("<");
bcc += BCCRecipients[i].Mail;
bcc.append(">");
}
}
|
|
|
|
|
BCC recipients should be added as RCPT TO: but not as part of the DATA in the headers.
this way it will receive the message but will not be included in the email info.
If you add it to the headers in the DATA, the recipients will be able to see then.
Maybe this should work as there is a header bcc: but I've tried it and it didn't work.
What I manage to get working as BCC should, is to included tehn as RCPT TO: smtp command and avoid
to included then as bcc: header in DATA.
hope it helps.
|
|
|
|
|
Great point! Kind of ruins the purpose of the Bcc. I've removed it for the next release.
|
|
|
|
|
In CSmtp::Send() function,
snprintf(FileName, 255, Attachments[FileId].c_str());
Why use snprintf to copy Attachments[FileId].c_str() to FileName?
If Attachments[FileId].c_str() contains characters like "%s" or other format specifier, the code would be erroneous.
|
|
|
|
|
uni_gauldoth,
Excellent point. I changed this in the last release of the code after finding that if you use strcpy in MSVC 2012 it gives warnings about wanting you to use strcpy_s instead. Unfortunately strcpy_s is not only not portable, but for whatever reason, MS switched the order of the arguments relative to strncpy so that you can't just use a #define to toggle between different platforms. You have to create a whole separate line - one that MSVC 2012 likes and another that everything else likes. I was trying to be clever about copying the string in a portable manner, but it seems I missed out on an important caveat. Any ideas other than making it like this:
#if _MSC_VER >= 1400
strcpy_s(FileName, 255, Attachments[FileId].c_str());
#else
strncpy(FileName, Attachments[FileId].c_str(), 255);
#endif
|
|
|
|
|
Thanks for reply, the lib is great and easy to use.
Why do we need to copy Attachments[FileId].c_str() into FileName buf?
Can we just use Attachments[FileId] directly?
Maybe we could use std::string instead of c string functions.
I changed the code a little like below,
hFile = fopen(Attachments[FileId].c_str(),"rb");
#ifndef LINUX
string::size_type pos = Attachments[FileId].find_last_of("\\");
#else
string::size_type pos = Attachments[FileId].find_last_of("/");
#endif
string fileName;
if(pos != string::npos)
{
fileName = Attachments[FileId].substr(pos+1);
encodedFileName += "=?UTF-8?B?";
encodedFileName += base64_encode((unsigned char *)fileName.c_str(),fileName.size());
encodedFileName += "?=";
}
strcat(SendBuf,"Content-Type: application/x-msdownload; name=\"");
strcat(SendBuf,encodedFileName.c_str());
strcat(SendBuf,"\"\r\n");
strcat(SendBuf,"Content-Transfer-Encoding: base64\r\n");
strcat(SendBuf,"Content-Disposition: attachment; filename=\"");
strcat(SendBuf,encodedFileName.c_str());
strcat(SendBuf,"\"\r\n");
strcat(SendBuf,"\r\n");
After all, we only need Attachment's name when,
1)opening the file,
2)filling info into "Content-Type: application/x-msdownload; name=" and "Content-Disposition: attachment; filename=".
In the above code,I have encoded the file name so unicode file names could be displayed correctly in email clients.
The same could be done with subject to make subject support unicode.
The above code works but may be Problematic due to my poor knowledge with email.
Could you consider making subject and attachment names support unicode in the next release?
|
|
|
|
|
uni_gauldoth,
Great idea! I changed your code just a little to streamline it. Do you see any problems with this implementation:
unsigned int i,rcpt_count,res,FileId;
char *FileBuf = NULL;
FILE* hFile = NULL;
unsigned long int FileSize,TotalSize,MsgPart;
string FileName,EncodedFileName;
string::size_type pos;
...
hFile = fopen(Attachments[FileId].c_str(), "rb");
...
for(FileId=0;FileId<Attachments.size();FileId++)
{
#ifndef LINUX
pos = Attachments[FileId].find_last_of("\\");
#else
pos = Attachments[FileId].find_last_of("/");
#endif
if(pos == string::npos) continue;
FileName = Attachments[FileId].substr(pos+1);
EncodedFileName = "=?UTF-8?B?";
EncodedFileName += base64_encode((unsigned char *) FileName.c_str(), FileName.size());
EncodedFileName += "?=";
snprintf(SendBuf, BUFFER_SIZE, "--%s\r\n", BOUNDARY_TEXT);
strcat(SendBuf, "Content-Type: application/x-msdownload; name=\"");
strcat(SendBuf, EncodedFileName.c_str());
strcat(SendBuf, "\"\r\n");
strcat(SendBuf, "Content-Transfer-Encoding: base64\r\n");
strcat(SendBuf, "Content-Disposition: attachment; filename=\"");
strcat(SendBuf, EncodedFileName.c_str());
strcat(SendBuf, "\"\r\n");
strcat(SendBuf, "\r\n");
SendData(pEntry);
...
To encode the subject do you just surround it with the same "=?UTF-8?B?" ..."?=" block and then base64_encode it?
Thanks,
David
|
|
|
|
|
Yes, I just put the utf-8 subject encoded in base64 in one "=?UTF-8?B?" ... "?=" block, and it does displays correctly in thunderbird, but other email clients may not display it correctly.
But acording to RFC 2047, if the subject is too long, multiple encoded-word should be used.
An 'encoded-word' may not be more than 75 characters long, including
'charset', 'encoding', 'encoded-text', and delimiters. If it is
desirable to encode more text than will fit in an 'encoded-word' of
75 characters, multiple 'encoded-word's (separated by CRLF SPACE) may
be used.
While there is no limit to the length of a multiple-line header
field, each line of a header field that contains one or more
'encoded-word's is limited to 76 characters.
The length restrictions are included both to ease interoperability
through internetwork mail gateways, and to impose a limit on the
amount of lookahead a header parser must employ (while looking for a
final ?= delimiter) before it can decide whether a token is an
"encoded-word" or something else.
Also RFC 2047 says,
Each 'encoded-word' MUST represent an integral number of characters.
A multi-octet character may not be split across adjacent 'encoded-
word's.
If we decide to split the subject into multiple encoded-words, we should not split it in the middle of an utf-8 character.
If an utf-8 character has three bytes, the three bytes should all be put into one encoded-word.
I don't known how to split utf-8 strings without breaking characters in the middle.
Below is a subject header composed by outlook 2013,
Subject: =?utf-8?B?UGxlYXNlIGpvaW4gdGhlIHJldmlldyBvZiDgpKTgpLLgpJXgpYsg4KS4?=
=?utf-8?B?4KS+4KSo4KWLIOCkrOCkvuCkleCkuCDgpK3gpL/gpKTgpY3gpLAg4KSw4KWL?=
=?utf-8?B?4KSu4KSo4KSu4KS+IOCkn+CkvuCkiOCkqiDgpJfgpLDgpY3gpKjgpYHgpLngpYs=?=
=?utf-8?B?4KS44KWN4LaG4Lav4La74LeK4LeBIOC3g+C3kuC2guC3hOC2vSDgtr0=?=
=?utf-8?B?4Lea4Lab4LarLiDgt4Pgt5Lgtrrgtrbgt4Pgt4og4LeA4LeZ4La24LeKIOC2hQ==?=
=?utf-8?B?4Lap4LeA4LeS4La6IOC3g+C3kuC2guC3hOC2vSDgtrrgt5TgtrHgt5Lgtprgt50=?=
=?utf-8?B?4Lap4LeKIOC3hOC2s+C3lOC2seC3jyDgtpzgt5DgtrHgt5Pgtrjgtqcg4La44LeZ?=
=?utf-8?B?4LeE4LeSIOC2tOC3kOC2uOC3kuC2q+C3meC2sSDgtpTgtrYg4LeD4LeP4Lav4La7?=
=?utf-8?B?4La64LeZ4Lax4LeKIOC2tOC3kuC3heC3kiDgtpzgtrHgt5MhIOC2uOC3meC2uA==?=
=?utf-8?B?IOC3gOC3meC2tuC3iiDtlqXssLBfcmV2aWV3LnBkZi4=?=
|
|
|
|
|
This sounds like something that would be good to incorporate. Do you want to come up with some code and post it and I'll pull it into the main distribution?
|
|
|
|
|
Yes, it's my pleasure.
I just got a little busy those days.
When I have time I will try to make it support utf-8 in subject, attachment name and attachment path.
The code is already done, but I need some time to make sure that they are compliant with those RFCs.
Does fopen support unicode in other system except windows?
I also think about making attachment path(used to locate the file, not the attachment path displayed in email) to use unicode, because attachment path contains characters not in the local code page is very common under windows.
|
|
|
|
|
That would be great and much appreciated. Just by way of note, however, version 2.3 of the library does support UFT-8 filenames for the attachments. It would be good for you to check over the implementation and make sure it is compliant, but it is already there.
Thanks,
David
|
|
|
|
|
In
int CSmtp::ConnectRemoteServer(...):
if(connect(hSocket,(LPSOCKADDR)&sockAddr,sizeof(sockAddr)) == SOCKET_ERROR)
{
...
}
else
{
return true; <======================= (??) ==============================
}
...
if(securityType!=DO_NOT_SET) SetSecurityType(securityType);
if(GetSecurityType() == USE_TLS || GetSecurityType() == USE_SSL)
{
InitOpenSSL();
if(GetSecurityType() == USE_SSL)
{
OpenSSLConnect();
}
}
modified 25-Jun-13 3:42am.
|
|
|
|
|
This part of the code probably doesn't work like you would expect based on typical syntax. The connect call is actually supposed to return a SOCKET_ERROR with the error code being WSAWOULDBLOCK so the else branch is avoided unless there is another error. See the explanation of WSAWOULDBLOCK here for more information:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx[^]
|
|
|
|
|