Sunday, January 06, 2013

Sending mail from programs

I've been working in a desultory fashion for the past two weeks on adding mailing lists to the management program which I write and maintain for the Occupational Psychologist. Whilst the concept is obvious (define a list which holds the email addresses of people), the actual implementation is slightly more complicated, as there are at least two types of people with email addresses defined in the program - therapists and customer contacts - and their details are stored in different tables. But once this problem is out of the way, all that remains is to create a form which receives a mailing list, a subject, an optional file and text (one's ordinary email message) and then send the subject, file and text to the mailing list.

There are a few caveats which need to be considered before one starts:
  1. Not everyone in the mailing list may have an email address
  2. The people in the mailing list should appear in the Bcc field, so all the recipients are oblivious to who also appears in the mailing list
  3. The number of recipients who appear in a given mail should not exceed 25, in order not to arouse the mail server's spam filter
As there are other parts of the program which send mails via Outlook, I thought that I would start with the same general idea (open Outlook, connect to the Inbox, create a mail, insert the data). I noticed that in all the other cases, the program displays the email on the screen but does not send it, leaving this to Outlook. This is because these cases insert a recipient's address but do not include any text.

This meant that I had to add the final necessary stages - add text, send, close Outlook - to the mailing list code. I wrote general code to do this a few years ago so it wasn't a problem to add the code. Having written that, I should point out that this old code was written before Outlook adopted a security patch which prevents unauthorised mails to be sent. The way to overcome this security patch is to use the Redemption software package. 

Whilst using this package allows my code to create and send emails, it also causes my code to crash with an Access Violation message. I noticed that the address in the AV message to always be the same, which is a give away that there is probably a bug in the clean-up code of the library. After I googled the issue, I came across a mail which I myself had posted to Stack Overflow, presenting this exact same issue (with the same address in the AV message) and asking for a solution. Whilst several people had posted answers, none of them helped.

I then realised that I must have given up on writing Outlook code at that point and had moved to sending emails via an SMTP server (what might be termed, "the classical way") and the Indy components which come with Delphi. Once I had this problem solved, sending the emails - along with all the caveats listed above - became easy.

For icing on the cake, I also discovered how I could send the emails as right to left displaying HTML text as opposed to left to right plain text (problematic when the text is in Hebrew). I would like to be able to take the output of a rich text component and use this directly but apparently rich text and HTML are different and there is no simple way to convert.

There is one problem which comes with using SMTP code: the component sending the mail has to know the address of the SMTP server, the account name and password. This is not the sort of information which I would like to embed in a program as a hacker could get the program, decipher the information and then hack the email account. In the past, I have stored this information (encoded or otherwise) in the computer's registry, but here I preferred to store the information in the database itself. 

And after all that typing, here's the code to a simple SMTP program

procedure TForm1.EmailBtnClick(Sender: TObject);
var
 i: integer;
 html: TStrings;
 htmpart, txtpart: TIdText;

begin
 html:= TStringList.Create;
 with html do
  begin
  Add ('!html!');
  Add ('!head!');
  Add ('!/head!');
  add ('!body!!div align="right"!');
  for i:= 1 to mem.lines.count do
   add (mem.lines[i-1] + '!br!');
  Add ('!/div!!/body!!/html!');
  end;

 with IdSMTP1 do
  begin
   host:= .......;
   username:= .......;
   password:= .......;
  end;

 with email do
  begin
   From.Address:= 'somewhere@somecompany.com';
   bcclist.add.address:= 'user@user.com';
    subject:= edSubject.Text;
   ContentType := 'multipart/mixed';
   Body.Assign (html);
  end;

 txtpart:= TIdText.Create(email.MessageParts);
 txtpart.ContentType:= 'text/plain';
 txtpart.Body.Text:= '';
 htmpart:= TIdText.Create (email.MessageParts, html)
 htmpart.ContentType := 'text/html';

// TIdAttachment.Create (email.MessageParts, fn);

 try
  try
   IdSMTP1.Connect (1000);
   IdSMTP1.Send (email);
  except
  end;
 finally
  if IdSMTP1.Connected
   then IdSMTP1.Disconnect;
 end;
 html.free;
 close;
end;

This would be the code, except that there's a bit missing - possibly the most important part. The formatter here doesn't seem to allow me to enter html code as actual text, so I've had to alter the code slightly. In the lines which follow 'with html do...', the exclamation mark (!) should be replaced by an angle bracket.

No comments: