Discussion:
Threaded SMTP with Indy
(too old to reply)
Brian
2008-07-01 21:59:33 UTC
Permalink
Hi all,

I am using Indy components in a web site and have had problems with some
of the email never being received by the receipients which I attribute to
their ISP's SPAM blockers. In an attempt to alleviate the problem, I've rewritten
the code to send the message to each receipient individually instead of putting
the entire list (~130 addresses) in the TO: field. The code I have written
is thus:

class TMailer : public TThread
{
private:
TIdSMTP *mailer;
TIdMessage *mailMsg;
TStringList *addresses;
FILE *fp;
protected:
void __fastcall Execute();
public:
__fastcall TMailer(bool CreateSuspended);
bool __fastcall loadData(String filePath,TIniFile *ini,String subject,String
msg,String replyTo,TStringList *to);
};

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "mailThread.h"
#pragma package(smart_init)
#pragma link "indy60.lib"
//---------------------------------------------------------------------------
__fastcall TMailer::TMailer(bool CreateSuspended) : TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall TMailer::Execute()
{
for(int i=0;i<addresses->Count;i++)
{ mailMsg->Recipients->Clear();
TIdEMailAddressItem *item=mailMsg->Recipients->Add();
item->Address=addresses->Strings[i];
try
{ if(!mailer->Connected())
mailer->Connect(5000);
if(mailer->Connected())
{ mailer->Send(mailMsg);
fprintf(fp,"(%s) message sent successfully to %s\n",Now().FormatString("mm/dd/yy
hh:nn:ss"),addresses->Strings[i]);
}
}
catch(Exception const &e)
{
fprintf(fp,"(%s) %s [%s]\nSubject: %s\n",Now().FormatString("mm/dd/yy
hh:nn:ss"),e.Message,addresses->Strings[i],mailMsg->Subject);
}
}
if(mailer->Connected())
mailer->Disconnect();
fclose(fp);
}
//---------------------------------------------------------------------------
bool __fastcall __fastcall TMailer::loadData(String filePath,TIniFile *ini,String
subject,String msg,String replyTo,TStringList *to)
{ String host,from,fromName,username,password,organization;

mailer=new TIdSMTP(Application);
mailMsg=new TIdMessage(Application);
addresses=new TStringList;

addresses->AddStrings(to);

host=ini->ReadString("Email","Host","");
from=ini->ReadString("Email","From Address","");
fromName=ini->ReadString("Email","From Name","");
username=ini->ReadString("EMail","Username","");
password=ini->ReadString("EMail","Password","");
organization=ini->ReadString("Email","Organization","None Specified");

fp=fopen(String(filePath+"mail.log").c_str(),"a");

if(password=="" || username=="")
mailer->AuthenticationType=atNone;
else
mailer->AuthenticationType=atLogin;

mailer->UseEhlo=true;
mailer->Host=host;
mailer->Username=username;
mailer->Password=password;

mailMsg->AttachmentEncoding="MIME";
mailMsg->ContentType="text/html";
mailMsg->Encoding=meMIME;
mailMsg->Organization=organization;
mailMsg->From->Address=from;
mailMsg->Subject=subject;
mailMsg->Date=Now();
if(replyTo!="")
{ TIdEMailAddressItem *item=mailMsg->ReplyTo->Add();
item->Address=replyTo;
}
mailMsg->Body->Text=msg;
return(true);
}
//---------------------------------------------------------------------------

The code from my 'main' is as follows:

TMailer *mail=new TMailer(true);
bool retval=mail->loadData(filePath,ini,subject,msg,replyTo,addresses);
if(retval)
mail->Resume();

The problem that I am having, according to the log is that I am receiving
a Socket Error #10093 mixed among the successful messages. The socket error
is referring to a successful WSAStartup not yet being performed. So, I'm
guessing that each time I send the message, TIdSMTP is performing a WSAStartup
and WSACleanup. If that is indeed the case, is there any way around the problem?

Also, this is my first foray into threading - so if I am doing it wrong,
please help me to understand why it's wrong and how I should go about changing
it to be proper.

Thanks for your help.
Remy Lebeau (TeamB)
2008-07-01 22:45:21 UTC
Permalink
Post by Brian
I am using Indy components
You did not say which version of Indy you are using.
Post by Brian
have had problems with some of the email never being received
by the receipients which I attribute to their ISP's SPAM blockers.
Or your own ISP server rejecting the recipients and not sending to them at
all.
Post by Brian
In an attempt to alleviate the problem, I've rewritten the code to
send the message to each receipient individually instead of putting
the entire list (~130 addresses) in the TO: field.
You should set up a server-side mailing list. Then you only have to send to
1 address, and it will forward the message to the other 130 addresses for
you.
Post by Brian
The problem that I am having, according to the log is that I am
receiving a Socket Error #10093 mixed among the successful
messages.
That is the following error:

WSANOTINITIALISED (10093)

Successful WSAStartup not yet performed.

Either the application hasn't called WSAStartup or WSAStartup failed.
The application may be accessing a socket which the current active task does
not own (i.e. trying to share a socket between tasks), or WSACleanup has
been called too many times.

To get that error, you must be doing something fundamentally wrong in your
program somewhere because WinSock is being unloaded from memory prematurely.
When using sockets proberly, that error should never occur.
Post by Brian
So, I'm guessing that each time I send the message, TIdSMTP is
performing a WSAStartup and WSACleanup.
No, it does not. Indy calls WSAStartup() only when any Indy component is
instantiated in the process for the first time (subsequent components do not
call WSAStartup() again), and then does not call WSACleanup() until the
process is terminated.

Since some of your messages are sent successfully, then WSAStartup() has
clearly been called and was successful, otherwise all sockets in the process
would fail miserably. So the only way you can be getting that error is if
something else in your project is calling WSACleanup() on its own outside of
Indy. If you are getting mixed success/failure while looping, then that
same code is likely calling WSAStartup() outside of Indy as well.


Gambit
Brian
2008-07-02 17:02:28 UTC
Permalink
Post by Remy Lebeau (TeamB)
You did not say which version of Indy you are using.
Indy 9
Post by Remy Lebeau (TeamB)
You should set up a server-side mailing list. Then you only have to
send to 1 address, and it will forward the message to the other 130
addresses for you.
The problem with this approach is that the list changes - on a message by
message basis.
Post by Remy Lebeau (TeamB)
To get that error, you must be doing something fundamentally wrong in
your program somewhere because WinSock is being unloaded from memory
prematurely. When using sockets proberly, that error should never
occur.
I am not directly calling WSAStartup/Cleanup. I am using the TWebModule routines
(which I'll assume based on your comments must be calling the WSA routines).
Could it be possible that the mail thread is still running when after the
webmodule performs its final Response back to the web browser and then attempts
to clean up?

When email is being sent the structure is as follows:

Request from web page comes in to CGI process.
Request is seen as a 'send email' request.
Request->ContentFields contain subject, replyto, message, and receipients
list.
MailThread is spun up to process the mail
Response->Content is set and CGI process terminates.
Remy Lebeau (TeamB)
2008-07-06 07:30:23 UTC
Permalink
Post by Brian
Indy 9
Which build?

Indy is commonly use by many spammers for its ease-of-use. So many
anti-spam systems automatically flag messages as spam if they were sent
using Indy. Indy 10 has additional logic to work with anti-spam systems.
But for older builds of Indy 9, one thing you have to do manually is change
the TIdSMTP.MailAgent property so anti-spam systems have to work harder to
detect Indy.
Post by Brian
I am not directly calling WSAStartup/Cleanup.
You might not be, but something else in your project may be.
Post by Brian
I am using the TWebModule routines (which I'll assume based on
your comments must be calling the WSA routines).
Could it be possible that the mail thread is still running when after
the webmodule performs its final Response back to the web browser
and then attempts to clean up?
Maybe, but unlikely. Even if it were using WSAStartup/Cleanup(), WinSock is
reference-counted. The type of situation I am referring to occurs when
calls to WSAStartup/Cleanup() are not balanced correctly, such as when
WSACleanup() is called too many times.


Gambit
Brian
2008-07-07 15:15:25 UTC
Permalink
Post by Remy Lebeau (TeamB)
Post by Brian
Indy 9
Which build?
9.0.18
Post by Remy Lebeau (TeamB)
systems. But for older builds of Indy 9, one thing you have to do
manually is change the TIdSMTP.MailAgent property so anti-spam systems
have to work harder to detect Indy.
Do I just put my organization's name as the mailagent or do I need something
more specific?
Post by Remy Lebeau (TeamB)
Maybe, but unlikely. Even if it were using WSAStartup/Cleanup(),
WinSock is reference-counted. The type of situation I am referring to
occurs when calls to WSAStartup/Cleanup() are not balanced correctly,
such as when WSACleanup() is called too many times.
Is it possible that the web module is 'closing' before the thread finishes
its work and that is causing the errors? If so, is there any way to see if
the thread is still processing and keep the application from exiting until
the thread is done?
Remy Lebeau (TeamB)
2008-07-07 17:34:55 UTC
Permalink
Post by Brian
9.0.18
Try upgrading to 9.0.50 to make sure you have all of the latest fixes and
functionality.
Post by Brian
Do I just put my organization's name as the mailagent or do I
need something more specific?
Anything that does not specify "Indy" in it is fine.


Gambit

Loading...