I am trying to develop an SMTP relay server using the Delphi help, Indy examples and Google.
I am just beginning and have a dummy SMTP client which sends a test message to an SMTP server which will later process it and forward it using an SMTP relay.
At the server, I receive one OnRcpt for each recipient (I test with one To, one CC and one Bcc).
I also see the To and CC recipients in AMsg when I handle OnMsgReceive and convert AMsg to a TStringStream (suitably prefixed with To: and CC:), but I don't see the BCC there.
Question: how do I tell which addresses were To, CC and BCC?
Here's some exceedingly simple code. I am setting breakpoints & examining received parameters to try to see what is happing
procedure TEmailMonitorMainForm.IdSMTPServerMsgReceive(ASender: TIdSMTPServerContext;
AMsg: TStream; var VAction: TIdDataReply);
var
LMsg : TIdMessage;
begin
LMsg := TIdMessage.Create(Nil);
LMsg.LoadFromStream(AMsg);
LMsg.Free(); // breakpoint here examine Lmsg
end; // IdSMTPServerMsgReceive()
[Update] when I send, then <message>.BCCList.Count = 1, but when I Receive, it is 0, whereas the received CClist.Count is 1, as it should be.
At the server, I receive one OnRcpt for each recipient (I test with one To, one CC and one Bcc).
The SMTP protocol does not distinguish between To, CC, and BCC recipients. That is an artifact of the email format via its To: and Cc: headers (there is no Bcc: header, though TIdMessage.SaveToFile() creates one so TIdMessage.LoadFromFile() can re-load it). When an SMTP client sends an email to an SMTP server, the client determines the intended recipients and specifies each one in a separate RCPT TO command to the server (that is what allows BCC to work - there is no Bcc: header in the email itself, but the server is given a RCPT TO command for each Bcc recipient). The RCPT TO addresses are the values specified in the TIdSMTPServer.OnRcptTo event, and are collected in the TIdSMTPServerContext.RCPTList property. These addresses are explicitly requested by the SMTP client, and are the only addresses you should deliver the email to (not the recipients specified in the email headers).
I also see the To and CC recipients in AMsg when I handle OnMsgReceive and convert AMsg to a TStringStream (suitably prefixed with To: and CC:), but I don't see the BCC there.
The actual email itself is just arbitrary data as far as an SMTP server is concerned. It is meant to be delivered as-is to each requested recipient. That is why the email data is provided as a TStream in the TIdSMTPServer.OnMsgReceive event, and why TIdSMTPServer in Indy 10 does not provide TIdMessage-based events like it did in Indy 9 and earlier.
If the email is in an RFC 822/2822/5322 format (which it usually is), it will have To: and Cc: headers. When receiving/loading such an email into a TIdMessage, those headers are used to fill in the TIdMessage.Recipients and TIdMessage.CCList properties, respectively. But since there is usually no Bcc: header (unless the email is being loaded from a file created by TIdMessage.SaveToFile()), the TIdMessage.BCCList will not be filled in.
how do I tell which addresses were To, CC and BCC?
Since the SMTP protocol itself does not distinguish between the different recipient types, the only way for you to distinguish between them is to parse the email, looking at its To: and Cc: headers. Any address in the TIdSMTPServerContext.RCPTList property that is not in one of those two headers must be a Bcc recipient. However, there is no guarantee that the other addresses in the TIdSMTPServerContext.RCPTList property will always match the To: and Cc: headers (though they usually will), since technically an SMTP client can send whatever raw data it wants and the SMTP will deliver it as-is without caring what it actually contains.
Here's some exceedingly simple code.
DO NOT use TIdMessage.LoadFromStream() to parse the TStream object provided by the TIdSMTPServer.OnMsgReceive event! It will not always work correctly. The technical reason has been discussed in detail multiple times in the Embarcadero and AToZed forums, for example in this discussion. You need to use the following workaround for the time being. The issue will be addressed in Indy 11:
procedure TEmailMonitorMainForm.IdSMTPServerMsgReceive(ASender: TIdSMTPServerContext; AMsg: TStream; var VAction: TIdDataReply);
var
LMsg : TIdMessage;
LClient: TIdMessageClient;
LIO: TIdIOHandlerStreamMsg;
begin
LMsg := TIdMessage.Create(Nil);
try
//LMsg.LoadFromStream(AMsg);
LClient := TIdMessageClient.Create;
try
LIO := TIdIOHandlerStreamMsg.Create(LClient, AMsg);
LIO.FreeStreams := False;
LIO.EscapeLines := True;
LIO.Open;
LClient.IOHandler := LIO;
LClient.ProcessMessage(LMsg, False);
finally
LClient.Free;
end;
// now you can use LMsg as needed...
finally
LMsg.Free;
end;
end;
Related
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.
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.
I am not able to send emails using TIdSMTP I am getting the following message: Socket Error # 10060 / Connection timed out.
I am using version Delphi XE6
Here is my code:
procedure TForm1.Button1Click(Sender: TObject);
var
IdSMTP : TIdSMTP;
IdMessage : TIdMessage;
begin
IdSMTP := TIdSMTP.Create (Nil);
IdMessage := TIdMessage.Create (Nil);
IdSMTP.Host := 'mail.mysmtp.com';
IdSMTP.Port := 25;
IdSMTP.Connect ();
IdMessage.From.Address := 'test#mysmtp.com';
IdMessage.From.Name := 'Contato';
IdMessage.Recipients.EMailAddresses := 'test#hotmail.com';
IdMessage.Subject := 'Contato test';
IdMessage.Body.Text := 'test';
IdSMTP.Send (IdMessage);
IdSMTP.Disconnect ();
FreeAndNil (IdMessage);
FreeAndNil (IdSMTP);
end;
From Google: A socket error in the 10060 range is a Winsock error. It is generally caused by either outgoing connection problems or connection problems on the host end.
I don't know if you sanitized this code to post it or not, but I'd say the culprit is either the hostname or the username on the from address.
Winsock will attempt to create a connection to the hostname. If it fails to get the expected ACK, it'll generate a timeout error. I've also seen this happen when the domain name isn't resolved by DNS.
Also, what was mentioned earlier regarding authentication ... the lack of response from the SMTP host could be due to improper authentication. It all depends on how the host's SMTP service was configured, so it could just ignore unauthorized requests.
You need to see if you must pass in a username/pwd with the SMTP request, or read the mailbox first (read before write, so to speak). I cannot imagine anybody configuring an SMTP server without requiring some kind of authentication, because otherwise you've got what amounts to an "open relay" where any process can send out unlimited traffic through it.
Also, the from address might be required to be valid. That is, 'test#mysmtp.com' would require a user/mailbox for 'test' to exist, as opposed to '*#mysmtp.com' that would work with ANY user/mailbox name.
All of these could result in a timeout because the SMTP host could be configured to simply ignore improper and unauthenticated requests.
I am getting interesting rejections from my clients mail server when sending a mail with indy-10's tidMessage component saying:
550 Rejected: Message does not contain a Message-ID
I get this even when using indy's own demo app
http://www.indyproject.org/DemoDownloads/Indy_10_MailClient.zip
what do I do to fix this. thanks!
It works with Indy9, maybe things haven't cahnged too much in 10:
procedure AddMsgID(AMsg: TIdMessage);
var
id: AnsiString;
begin
id := GenerateUniqueMsgID;
AMsg.MsgId := id;
AMsg.AddHeader('Message-ID=' + id);
// AMsg.ExtraHeaders.Values['Message-ID'] := id;
end; // AddMsgID
TIdMessage in Indy 10 intentionally omits the 'Message-Id' header when encoding an email to a socket or TStream. You will have to use the TIdMessage.ExtraHeaders property, eg:
IdMessage1.MsgId := '...';
IdMessage1.ExtraHeaders.Values['Message-Id'] := IdMessage1.MsgId;
EDIT:
As a followup for this - TIdMessage has now been updated with logic changes in how it handles the "Message-ID" and "In-Reply-To" headers:
https://www.indyproject.org/2016/09/12/logic-changes-in-tidmessage-regarding-message-id-and-in-reply-to-headers/
The TIdMessage.MsgId property now generates a "Message-ID" header regardless of whether the email is being saved, streamed, or transmitted. So you do not need to use the ExtraHeaders property anymore.
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.