Send Email in HTML Format - delphi

At the moment we are using MAPI to send a plain text email from our application. We specify the dialog flag when the user invokes this function, so that the email will appear in their email client and they can then modify it and send it.
We would like to embelish the email and send it in an HTML format.
According to this link MSDN link MAPI is not sutiable for this http://support.microsoft.com/kb/268440
I have seen an article on ExpertsExchange that say you can use MAPI to do it but I can't get the example to work with Outlook (not tried anyother client yet)
procedure ShowMailDlg(ToName,Address,HTMLMessage: string);
var
li: integer;
lMessage: TMapiMessage;
lRecipArray: array of TMapiRecipDesc;
lREs: DWord;
begin
SetLength(lRecipArray,1);
lRecipArray[0].ulRecipClass:=MAPI_TO;
lRecipArray[0].lpszName:=pChar(ToName);
lRecipArray[0].lpszAddress:=pChar(Address);
lMessage.ulReserved:=0;
lMessage.lpszSubject:=nil;
lMessage.lpszNoteText:=pChar(HTMLMessage);
lMessage.lpszMessageType:= nil;//pChar('HTML');
lMessage.lpszDateReceived:=nil;
lMessage.lpszConversationID:=nil;
lMessage.flFlags:=0;
lMessage.lpOriginator:=nil;
lMessage.nRecipCount:=length(lRecipArray);
lMessage.lpRecips:=PMapiRecipDesc(lRecipArray);
lMessage.nFileCount:=0;
lMessage.lpFiles:=PMapiFileDesc(nil);
lRes:=MapiSendMail(0, 0 , lMessage,MAPI_DIALOG, 0);
end;
Anyone have any ideas how I can do this. I could probably automate Outlook but I would like to keep it fairly independant of email client (hence MAPI)
Thanks
Update: thanks to everyone for the suggestions. The feature is question is not that heavily used, so asking the user to configure SMTP details is not really an option. I think we will just stick to the plain text email.
Thanks

MAPI doesn't support HTML formatted messages. From Microsoft : "Extended Messaging Application Programming Interface (MAPI) should not be used to generate HTML-formatted messages. As an alternative, consider using the Microsoft Outlook Object Model, CDONTS, CDOSYS, CDOEX, or a third-party SMTP control."
I would echo the comments about sending via Indy. I published a unit that works to send HTML messsages with Indy very simply here or feel free to write your own. If you really want to make the messages editable, try a combination of WPTools and Indy. WPTools has good support for HTML markup and then you can send the resulting message via Indy.
I don't have any experience with Synapse so I can't say how easy/hard it is with that project.

If you only have to serve Outlook clients you could try accessing Outlook by OLE:
procedure SendMail(const aRecipient, aSubject, aNote, aFile: string; Silent, HTML: boolean);
const
olMailItem = 0;
var
ii: integer;
MyOutlook, MyMail: variant;
begin
//*** Send something via OLE/Outlook...
//*** Outlook- und Mail-Objekt erstellen...
MyOutlook := CreateOLEObject('Outlook.Application');
MyMail := MyOutlook.CreateItem(olMailItem);
//*** create a mail message...
MyMail.To := aRecipient;
MyMail.Subject := aSubject;
if aNote <> '' then begin
if HTML then
MyMail.HTMLBody := aNote
else begin
MyMail.Body := aNote;
end;
end;
//*** Add Attachment...
if aFile <> '' then begin
MyMail.Attachments.Add(aFile);
end;
if Silent then
MyMail.Send
else
MyMail.Display;
MyOutlook := UnAssigned;
end;

This is also possible using the Synapse library. A specific example is available from the howto page titled "About MIME and its MIME Parts". I personally have used this technique in several programs to send HTML email.
Unfortunately, this doesn't work over MAPI, you will need to get the users SMTP or IMAP information and handle that communication yourself (the Synapse library has routines to do just that).
If you decide to download Synapse, I strongly suggest getting the latest version from the subversion repository. The update available there includes support for Delphi 2009.

For Delphi emailing I would recommend SakEmail
http://groups.yahoo.com/group/sakemail/
If you're using a version of delphi higher then 7,
you should add the version definition to the .inc file.
that's supplied with SakEmail, else it will fall back to
Delphi4 compatibility mode. After patching the inc file it seems
fine with Delphi 2005.
also, it seems HTML over MAPI works in Thunderbird, but no other client.

There is an undocumented feature of MAPISendMail for including an HTML body:
set lpszNoteText to nil (or a pointer to an empty string)
add an HTML attachment
MAPI will use the html attachment as the body of the e-mail (and not include the attachment).

You can use SMTP with Indy:
HTML Messages
New HTML Message Builder class (Indy 10)

You should consider using an SMTP component, Indy for example, and adding the user doing the sending to the CC or BCC field of the message. This largely satisfies the need to have such sent messages appear in the user's own mail client, which is the primary advantage of MAPI. The user may even set up a separate account specifically for receiving such copies.
Doing it this way allows you to completely customize every detail related to sending mail (MHTML being one such example), including caching all mail and doing the sending in a separate thread, or at a different time, and so on. Also, this method is more client-agnostic than even MAPI; for example, this still works even if the user is using web-based email such as Gmail.

Related

Using Delphi to read emails subfolders in InBox on Thunderbird

I need to read emails from Thunderbird email client for all accounts Inbox and all his subfolders.
How I change the code to read all email from every subfolders?
Thanks
With this code (get from examples by Jedy libraries) I read emails only from default InBox Folder:
DownloadsListView.Items.Clear;
JvMail1.LogonOptions := [JvMail.loNewSession, JvMail.loDownloadMail];
JvMail1.LogOn;
try
b := JvMail1.FindFirstMail;
while b do
begin
JvMail1.ReadOptions := [roFifo, roHeaderOnly, roPeek];
JvMail1.ReadMail;
with DownloadsListView.Items.Add do
begin
Caption := JvMail1.Subject;
SubItems.Add(JvMail1.ReadedMail.RecipientName);
SubItems.Add(DateTimeToStr(JvMail1.ReadedMail.DateReceived));
end;
b := JvMail1.FindNextMail;
end;
finally
JvMail1.LogOff;
JvMail1.Clear;
DownloadsListView.BringToFront;
end;
TJvMail is a wrapper for SimpleMAPI, which has no concept of subfolders, and can only access a profile's inbox. You would need to use CDO or Extended MAPI to have full access to a profile's subfolders, but TJvMail does not support that.
Differences between CDO, Simple MAPI, and Extended MAPI
Alternatively, use the IMAP protocol (such as via Indy's TIdIMAP4 component) to access the email server directly, instead of accessing the user's local email client.

Sending email using TIdSMTP and TIdMessage

I'm using an action from my ActionManager to send an email to a client in my DBGrid.
I select the row with the client details in my DBGrid, right click, a Popupactionbar comes up, I click the send mail action and the mail should be sending.
This is the code for the mail send action:
procedure TForm2.actSendEmailExecute(Sender: TObject);
begin
IdSMTP1.Host := 'smtp.mail.yahoo.com';
IdSMTP1.Port := 465;
//setup mail message
IdMessage1.From.Address := 'my email address is here';
IdMessage1.Recipients.EMailAddresses := DBGrid1.DataSource.DataSet['email'];
IdMessage1.Subject := 'test subject';
IdMessage1.Body.Text := 'test email body' + DBGrid1.DataSource.DataSet['details'];
//send mail try
IdSMTP1.Connect ;
IdSMTP1.Send(IdMessage1) ;
IdSMTP1.Disconnect;
end;
I'm using TIdSMTP and TIdMessage (Indy components).
I've also set the hostname, port, username and password in the TIdSMTP Properties using Object Inspector, all details are as yahoo provided them on their website (hostname, port, my email for login and my email password)
In the row I select in DBGrid there is a field named email which contains the mail address of the client, hence that is where the email should go.
Also, I'm adding some information contained in a field named details, which should go in the body section of the email (see above code)
Once I start my software up, select the row in DBGrid and hit send on the action button, the software freezes (not responding) for somewhere around 20-40 seconds than it returns an error saying: Connection closed gracefully.
I'm using yahoo smtp just to test things out, I would eventually be using the smtp from my hosting provider that hosts my website (and where the db is located also)
Indy version 10.6.2.5311
Using Delphi 10 Seattle
Any thoughts as to what I'm doing wrong so far?
Could this relate to missing SSL libraries? I just note that you're using port 465; isn't that for SSL connections?
I've never worked with the TIdSMTP component, but I've seen a similar situation occur with the TIdHTTP component (the connection times out without any obvious indication as to why) when attempting to use use an SSL connection unless the appropriate DLLs have been made available. They aren't an integral part of Indy (I imagine it's a legal issue regarding the use of encryption in some countries) but they are freely available elsewhere.
Edit: The component also has a "UseTLS" property which may not have been set.

Sending email through Delphi

I have never worked with Delphi (my girlfriend have some experience), but there is a small tool that I want to make and I think it should be made on Delphi. After hours of searching and testing we found only this guide (http://delphi.about.com/od/indy/a/email-send-indy.htm) which doesn't work, it gives a huge error when pressing the Send email button.
I am trying to make a tool that will be distributed with my game and will allow the user to send me an email (to my Gmail) in case of a problem or for feedback. He will enter his email, attach a screenshot and fill in a comment box, doesn't need to have something else. Any help will be highly appreciated as I am stuck, thank you.
Here's a simple routine to send an email. I guess you can modify it to fit your needs:
procedure SendImage(const Comment, AImage: String);
var
SMTP: TIdSMTP;
Msg: TIdMessage;
begin
if not FileExists(AImage) then
Exit;
Msg := TIdMessage.Create(nil);
try
Msg.From.Address := 'xxxx#gmail.com';
Msg.Recipients.EMailAddresses := 'xxxx#gmail.com';
Msg.Body.Text := Comment;
TIdAttachmentFile.Create(Msg.MessageParts, AImage);
Msg.Subject := AImage;
SMTP := TIdSMTP.Create(nil);
try
SMTP.Host := 'smtp.gmail.com';
SMTP.Port := 25;
SMTP.AuthType := satDefault;
SMTP.Username := 'xxxx#gmail.com';
SMTP.Password := '##$%';
SMTP.Connect;
SMTP.Send(Msg);
finally
SMTP.Free;
end;
finally
Msg.Free;
end;
end;
PS: Note that you have to replace xxxx#gmail.com with your own email address, and not the user's. You could include their email address in the body of the crash report.
IMO, email is not going to work 100% of the time. You'll be lucky to get 80%. If you use your SMTP, firewalls and ISPs must permit it. If you use their existing email client, you rely on proper configuration. A typical scenario is that they use Gmail or Yahoo, and then you try to send something via "imap email already on system" and the user gets confronted with an old copy of Outlook Express or Windows Mail that they didn't even know they had, but it's "registered" with windows as the default email handler. This gets ugly.
I recommend sending via HTTP to an app on your web page. A PHP script that records the info and image into a MySQL database. Actually, I recommend using a bug tracking database like Mantis or FogBugz (from our patron!). I'd check them out, and then if you like what they offer, it's fairly straightforward to submit reports via HTTP post (or email, etc.).

Delphi Indy Sharepoint Windows Login

I am including these in uses clause
IdAuthentication
,
IdAuthenticationDigest
,
IdAuthenticationNTLM
,
IdAuthenticationSSPI
Currently I have code that does this:
W.Request.BasicAuthentication := True;
W.Request.Username := AOptionsPtr^.AuthUsername;
W.Request.Password := AOptionsPtr^.AuthPassword;
And if I have access to OpenSSL:
TmpOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create;
TmpOpenSSL.SSLOptions.Method := sslvSSLv23;
TmpOpenSSL.SSLOptions.Mode := sslmClient;
TmpOpenSSL.SSLOptions.VerifyMode := [];
TmpOpenSSL.SSLOptions.VerifyDepth := 0;
//--
W.IOHandler := TmpOpenSSL;
From skimming the documentation for WinINet (yes, I know it is not Indy) it seems persistent connections is also required for authentication. I suppose this also goes for Indy? URL:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384220(v=vs.85).aspx
I need to get this to work with SharePoint. The problem, however, is hat I have no intranet SharePoint server to test against. Thus I can not step through the code and see what works and what does not. However, I have a potential customer that can test it for me.
What more do I need to do to get above code working with SharePoint using Windows Authentication (NTML? SPPI?)
Will Indy automatically test and use proper auhentication?
do I need to set W.Request.BasicAuthentication := False; for auto authentication/detection to work?
If multiple requests are necessary (with first response being 401) I assume I need to add support for this in my own code when making a GET request? (To set authentication mode and make a new request?)
You can request a persistent connection by setting the Request.Connection property to 'keep-alive'.
TIdHTTP will check the server's WWW-Authorization header and compare it to the TIdAthentication classes you have included in your uses clause. The TIdHTTP.OnSelectAuthorization event will tell you which class was picked, and allow you to override it if needed. The TIdHTTP.OnAuthorization event will be triggered if authentication fails and different credentials are needed.
The BasicAuthrnication property simply allows TIdHTTP to fall back to TIdBasicAuthentication if no other TIdAuthentication class is assigned.
No, you do not need to handle multi-request authentications manually, like NTLM. TIdHTTP and TIdAuthentication handle those details for you.

Displaying Outlook Address book in Delphi XE app:

I have a Delphi XE app and I'd like to pop up the address book dialog that Outlook uses from within my Delphi app - I assume there are COM classes to support this? What's the best way to get this done? Platform is Win7-64 with Outlook 2010.
TIA
Disclaimer: It is definitely possible to do so through COM, but Outlook will display warnings that a 3rd party application is accessing the address book (and rightfully so). If you want to avoid these warnings, you can run the code from within an Outlook add-in, resort to MAPI, or use Outlook Redemption, which is basically an advanced wrapper around MAPI that feels like the Outlook Object Model.
The Outlook Object Model offers the SelectNamesDialog dialog to display the address book. It is highly configurable, and you can initialize it with custom sets of addresses as well.
As a little example, here is some code that pops up the address book in multiselect mode. For brevity, it uses late binding (OleVariants). You'll probably want to use early binding in production code.
procedure TForm1.Button1Click(Sender: TObject);
var
application: OleVariant;
dialog: OleVariant;
i: Integer;
recipients: String;
recipient: OleVariant;
begin
application := createOleObject( 'Outlook.Application' );
// Obtain the dialog
dialog := application.session.getSelectNamesDialog;
// Only show the a single 'add' field, multiselect
dialog.setDefaultDisplayMode( 6 ); // 6 = olDefaultDelegates
// Display the dialog
dialog.display;
// Display selection
recipients := '';
for i := 1 to dialog.recipients.count do
begin
recipient := dialog.recipients.item( i );
recipients := recipients + recipient.name + #13#10;
end;
showMessage( recipients );
end;
To do something like this you need to support the Extended MAPI interface.
Here is a link to a component which supports this on Win7-64 Outlook-2010.
Easy MAPI
Supports execution of address book dialogs.

Resources