using delphi 10 seattle,
my email is setup like so:
Code:
dmEmail.emMessage.ContentType := 'text/calendar;method=REQUEST';
with tIDText.Create(dmEMail.emMessage.Messageparts) do
begin
ContentType := 'text/calendar;method=REQUEST';
// parentPart := 0;
Body.Text := eventText.Text;
end;
with TIdAttachmentFile.Create(dmEmail.emMessage.MessageParts, fName) do
begin
ContentDisposition := 'attachment';
ContentType := 'text/calendar;method=REQUEST';
ExtraHeaders.Values['Content-ID'] := ExtractFileName(fName);
ContentID := ExtractFileName(fName);
Filename := ExtractFilename(fName);
// parentPart := 1;
end
where fName is a .ics file that I am attempting to send.
currently, the contents of the ics file is:
Code:
BEGIN:VCALENDAR
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
ORGANIZER;CN=Connie M.:MAILTO:[real code has a valid email address here]
DTSTART:20190827T220000Z
DTEND:20190827T230000Z
TRANSP:OPAQUE
SEQUENCE:0
UID:185
DTSTAMP:20190827T071800Z
DESCRIPTION:3 pm my time
SUMMARY:Event on 27th
PRIORITY:2
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
my mail.txt (saving the message to file) is this:
Code:
Subject: Event on 27th
To: [real code has a valid email address here, which is different that the organizer address above]
Content-Type: text/calendar; method=REQUEST; charset=us-ascii; boundary="AZF7QdS9INFRk=_YwrBAXh6TL7aANRhj0D"
MIME-Version: 1.0
Date: Tue, 27 Aug 2019 07:18:42 -0700
BEGIN:VCALENDAR
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
ORGANIZER;CN=3DConnie M.:MAILTO:[weird that organizer name changed to start with 3D, but otherwise, same valie email address]
DTSTART:20190827T220000Z
DTEND:20190827T230000Z
TRANSP:OPAQUE
SEQUENCE:0
UID:185
DTSTAMP:20190827T071800Z
DESCRIPTION:3 pm my time
SUMMARY:Event on 27th
PRIORITY:2
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
raised exception class EIdSMTPReplyError with message 'STOREDRV.submission.excetption : invalid recipentsException;
failed to process message due to a permanent exception with mesage. Amessage can't be sent because it contains no recipients. InvalidRecipientsException a message can't be sent because it contains no recipients.
since I DO have a recipient, I'm not sure why I am getting this message.
what I am trying to do :
send an email with an event invite, with the 'accept/decline' stuff in the email.
so far, the most I have accomplished is only getting the ics file as an attachment.
it is VERY possible the ics file is incorrect (this is my first time attempting this)
any ideas what I am missing?
That is the wrong ContentType value to use at the top level of the email when multiple parts are used in the TIdMessage.MessageParts collection. It MUST be set to a 'multipart/...' media type instead, in this case 'multipart/mixed' since you have an standalone attachment that is not referred to by the text parts.
(08-27-2019, 02:33 PM)ccMcBride Wrote: my mail.txt (saving the message to file) is this:
Which is completely messed up, because you are not setting up the TIdMessage correctly, not even close.
(08-27-2019, 02:33 PM)ccMcBride Wrote: ORGANIZER;CN=3DConnie M.:MAILTO:[weird that organizer name changed to start with 3D, but otherwise, same valie email address]
That is just part of how the Quoted-Printable encoding works. '=' is a reserved character in QP, so it has to be escaped in hexadecimal format (the ASCII code for the '=' character is decimal 61, hex 0x3D). 'quoted-printable' is the default encoding for TIdText parts, whereas 'base64' is the default for TIdattachment....
(08-27-2019, 02:33 PM)ccMcBride Wrote: what I am getting in return :
raised exception class EIdSMTPReplyError with message 'STOREDRV.submission.excetption : invalid recipentsException;
failed to process message due to a permanent exception with mesage. Amessage can't be sent because it contains no recipients. InvalidRecipientsException a message can't be sent because it contains no recipients.
since I DO have a recipient, I'm not sure why I am getting this message.
You did not show the code that populates the recipient(s). But the email you did show has no 'From' header in it. Maybe that is what the server is actually complaining about? Hard to say since you did not show the actual SMTP commands being sent.
(08-27-2019, 02:33 PM)ccMcBride Wrote: any ideas what I am missing?
dmEmail.emMessage.From.Address := ...;
dmEmail.emMessage.Recipients.EMailAddresses := ...;
...
dmEmail.emMessage.ContentType := 'multipart/mixed';
with TIdText.Create(dmEMail.emMessage.MessageParts) do
begin
ContentType := 'multipart/alternative';
//ParentPart := -1;
end;
with TIdText.Create(dmEMail.emMessage.MessageParts) do
begin
ContentType := 'text/plain';
Body.Text := 'some plain text here to describe the event for email readers that don't support iCalendar';
ParentPart := 0;
end;
with TIdText.Create(dmEMail.emMessage.MessageParts) do
begin
ContentType := 'text/calendar;method=REQUEST';
Body.LoadFromFile(fName);
ParentPart := 0;
end;
with TIdAttachmentFile.Create(dmEmail.emMessage.MessageParts, fName) do
begin
ContentDisposition := 'attachment';
ContentType := 'text/calendar;method=REQUEST';
ContentID := ExtractFileName(fName);
Filename := ExtractFilename(fName);
//ParentPart := -1;
end;
So, 'something' is missing specifically when I'm sending it with a request method. just not sure what.
after more banging my head against my desk, I tracked this down to using outlook 365 as my email server.
if I use my gmail account for outgoing email, it works just fine (the email has the accept/tentative/decline buttons when sent to my outlook account).
(08-28-2019, 02:30 PM)ccMcBride Wrote: my smtp settings (this works with all other emails)
I see a few issues with that setup.
- Why are you setting a password only if SSL is used? Even if you don't use SSL to encrypt the connection, you can still login securely by using satSASL instead of atDefault, and then populating the TIdSMTP.SASLMechanism with secure SASLs like CRAM-SHA1, CRAM-MD5, etc.
- I don't suggest using the TIdSSLIOHandlerSocketOpenSSL.SSLOptions.Method property at all. You should be using the TIdSSLIOHandlerSocketOpenSSL.SSLOptions.SSLVersions property instead so that you can support multiple TLS versions at a time (ie TLS 1.0, 1.1, and 1.2). Not all servers support the latest TLS 1.2 yet, or are limited to a single version. And why is your assignment of SMTP.UseTLS dependent on whether you are using TLS 1.2 or not? Those are two independent settings, so you should be treating them separately in your config.
- you are populating emMessage.From from your config, and then you are overwriting it with emMessage.ReplyTo from a different config.
(08-28-2019, 02:30 PM)ccMcBride Wrote: I changed the main message part to multipart/mixed, still error.
as test, I also did the following:
...
it was sent with no errors, the event was attached as an ICS file, no 'accept/decline' on email
...
Your emails are still malformed. There are clearly multiple MIME parts present, despite your claim that the code logic for that is commented out. So clearly you have some left-over data in the TIdMessage, or you are not showing all of the code you are actually using to populate the TIdMessage. Either way, when multiple MIME parts are present, the top-level 'Content-Type' header MUST MUST MUST be a 'multipart/...' media type, like 'multipart/mixed'. So, either:
- populate ONLY the TIdMessage.Body and leave the TIdMessage.MessageParts empty, and set the TIdMessage.ContentType to 'text/calendar' accordingly.
- populate ONLY the TIdMessage.MessagePart and leave the TIdMessage.Body empty, and set the TIdMessage.ContentType to 'multipart/...' accordingly.
You should read my article on Indy's website about how multi-part emails actually work (it is geared towards HTML email, but much of it applies to this situation too. Just replace HTML with Calendar).
08-28-2019, 07:16 PM (This post was last modified: 08-28-2019, 08:05 PM by rlebeau.)
(08-28-2019, 04:45 PM)rlebeau Wrote:
(08-28-2019, 02:30 PM)ccMcBride Wrote: my smtp settings (this works with all other emails)
>I see a few issues with that setup.
>- Why are you setting a password only if SSL is used? Even if you don't use SSL to encrypt the connection, you can still login securely by using satSASL instead of ?>atDefault, and then populating the TIdSMTP.SASLMechanism with secure SASLs like CRAM-SHA1, CRAM-MD5, etc.
misnamed field name. NOT my structure, but is the structure being used.
enable_ssl is actually 'use encryption' and
flags is actually the method of encryption (0 being none)
--updated settings:
Code:
SMTP.Host := cdsDefaultSettings.FieldByName('SERVERNAME').AsString;
SMTP.Username := cdsDefaultSettings.FieldByName('USERNAME').AsString;
if cdsDefaultSettings.FieldByName('Enable_ssl').AsBoolean then
begin
SMTP.AuthType := satDefault;
SMTP.Password := DecryptStr(cdsDefaultSettingsACCOUNT_ID.AsInteger);
end
else
SMTP.AuthType := satNone;
if (not IsNullStr(cdsDefaultSettings.FieldByName('PORT').AsString)) and
(cdsDefaultSettings.FieldByName('PORT').AsInteger <> 0) then
SMTP.Port := cdsDefaultSettings.FieldByName('PORT').AsInteger
else
SMTP.Port := 25;
if cdsDefaultSettings.FieldByName('flags').AsInteger <> 0 then
begin
IOHandler.Host := SMTP.host;
IOHandler.Port := SMTP.Port;
if cdsDefaultSettings.FieldByName('flags').AsInteger = 4 then //tls
// begin
ioHandler.sslOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]
// ioHandler.sslOptions.method := sslvTLSv1_2
else
ioHandler.sslOptions.SSLVersions := [sslvSSLv2, sslvSSLv23,sslvSSLv3];
// ioHandler.SSLOptions.Method := TIDSSLVersion(cdsDefaultSettings.FieldByName('flags').AsInteger - 1);
SMTP.IOHandler := ioHandler;
if cdsDefaultSettings.FieldbyName('flags').AsInteger = 4 then
SMTP.UseTLS := utUseExplicitTLS;
end
else
begin
SMTP.ioHandler := nil;
SMTP.UseTLS := utNoTLSSupport;
end;
// end;
emMessage.From.Address := cdsDefaultSettings.fieldByName('username').AsString;
if not isNullStr(cdsDefaultSettings.fieldByname('display_name').AsString) then
emMessage.From.Name := cdsDefaultSettings.FieldbyName('display_name').AsString
else
emMessage.From.Name := globalConnection.LoginRec.LoginName;
if cdsuserEmail.IsEmpty then
emMessage.ReplyTo.EMailAddresses := cdsDefaultSettings.fieldByName('username').AsString
else
begin
emMessage.ReplyTo.EMailAddresses := cdsUserEmail.fieldByName('email_prime').AsString;
emMessage.From.EMailAddress := cdsUserEmail.fieldByName('email_prime').AsString;
end;
code for adding the ics:
Code:
procedure tdlgEMail.CreateAttachment(fName: String; IsRelated: Boolean = False; pPart: Integer = -1);
begin
if isEvent then
begin
if isCancel then
begin
dmEmail.emMessage.ContentType := 'text/calendar; charset="utf-8";method=CANCEL';
dmEmail.emmessage.Body.Text := eventText.Text;
end
else
begin
with tIDText.Create(dmEMail.emMessage.Messageparts) do
begin
ContentType := 'text/calendar;method=REQUEST';
Body.Text := eventText.Text;
end;
with TIdAttachmentFile.Create(dmEmail.emMessage.MessageParts, fName) do
begin
ContentDisposition := 'inline';
ContentType := 'text/calendar;method=REQUEST';
ExtraHeaders.Values['Content-ID'] := ExtractFileName(fName);
ContentID := ExtractFileName(fName);
Filename := ExtractFilename(fName);
// parentPart := 1;
end
end;
end
else
begin
with TIdAttachmentFile.Create(dmEmail.emMessage.MessageParts, fName) do
begin
if IsRelated then
begin
ContentDisposition := 'inline';
ContentType := 'image/jpeg';
ExtraHeaders.Values['Content-ID'] := ExtractFileName(fName);
ContentID := ExtractFileName(fName);
end
else
ContentType := GetMIMETypeFromFile(fName);
Filename := ExtractFilename(fName);
ParentPart := pPart;
end;
end;
end;
mail.txt: (this one blows up when I use outlook365 as my SMTP email server)
Code:
Subject: test Event for Christophe
To: "Connie M." <conniem@provantagesoftware.com>
Content-Type: multipart/mixed; boundary="yzkm5fRQQXAs5=_A3wk7LhIPDANsDvtqFL"
MIME-Version: 1.0
Date: Wed, 28 Aug 2019 12:06:55 -0700
BEGIN:VCALENDAR
PRODID:-www.provantagesoftware.com/v1.0/id=3D195
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
ORGANIZER;CN=3DConnie M.:mailto:conniem@provantagesoftware.com
DTSTART:20190830T230000Z
DTEND:20190831T000000Z
TRANSP:OPAQUE
SEQUENCE:0
UID:195
DTSTAMP:20190828T120600Z
DESCRIPTION:content type is actually set at the email level
SUMMARY:test Event for Christophe
PRIORITY:2
ATTENDEE;RSVP=3DTRUE:mailto:conniem@provantagesoftware.com
END:VEVENT
END:VCALENDAR
BEGIN:VCALENDAR
PRODID:-www.provantagesoftware.com/v1.0/id=3D195
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
ORGANIZER;MAILTO:conniem@provantagesoftware.com
DTSTART:20190830T230000Z
DTEND:20190831T000000Z
TRANSP:OPAQUE
SEQUENCE:0
UID:195
DTSTAMP:20190828T121200Z
DESCRIPTION:content type is actually set at the email level
SUMMARY:test Event for Christophe
PRIORITY:2
ATTENDEE;RSVP=3DTRUE:mailto:conniem@provantagesoftware.com
END:VEVENT
END:VCALENDAR
and the only difference between the two is this line:
not working: (throws recipient error when sent through outlook smtp, but works when I use gmail smtp server) ORGANIZER;CN=3DConnie M.:mailto:conniem@provantagesoftware.com
08-28-2019, 08:19 PM (This post was last modified: 08-28-2019, 08:21 PM by rlebeau.)
(08-28-2019, 07:16 PM)ccMcBride Wrote: misnamed field name. NOT my structure, but is the structure being used.
enable_ssl is actually 'use encryption' and
flags is actually the method of encryption (0 being none)
Still looks odd to me, but OK.
(08-28-2019, 07:16 PM)ccMcBride Wrote: code for adding the ics:
Where are you setting the TIdMessage.ContentType when using the TIdMessage.MessageParts?
Why are you setting the TIdAttachment.ContentDisposition to 'inline' instead of 'attachment'? And you are STILL setting the ExtraHeaders['Content-ID'] header when you shouldn't be.
(08-28-2019, 07:16 PM)ccMcBride Wrote: mail.txt: (this one blows up when I use outlook365 as my SMTP email server)
Define "blows up" exactly.
(08-28-2019, 07:16 PM)ccMcBride Wrote: and the only difference between the two is this line:
not working: (throws recipient error when sent through outlook smtp, but works when I use gmail smtp server) ORGANIZER;CN=3DConnie M.:mailto:conniem@provantagesoftware.com
I have tried putting the CN part in quotes, and tried putting it in double quotes, but I still get the same error.
That difference is merely in the content of the "ORGANIZER" field, regardless of how that content is being transported via email. One email is using "CN=<name>:mailto:<email>" (ignore the changing of '=' characters to '=3D', that is just the Quoted-Printable encoding at play during transport - any MIME-compliant receiver will decode '=3D' back to '=' before processing the ICS data), whereas the other email is using "MAILTO:<email>" instead. That has absolutely nothing to do with Indy, and if the receiver can't handle the added "CN" value, that is not Indy's fault.
That being said, if you find that the receiver is simply not handling the Quoted-Printable encoding properly, you CAN disable it (though I would not recommend that) by setting the TIdText.ContentTransfer property to either '7bit', 'base64', '8bit', or 'binary' instead ('quoted-printable' is the default if left blank).
(08-28-2019, 07:16 PM)ccMcBride Wrote: so, is the message still malformed?
There is an extra 'text/plain' MIME part that is present in your emails that doesn't belong there (it should not be causing problems for iCalendar-aware receivers, though), but I don't see your code adding a TIdText for that text, and Indy should not be generating a blank text MIME part like that (if it is, that will have to be addressed).
attached is a test project.
open dmEmail, go to setupsmtp, set your login creditials. start the program, add an email address, click the 'send email' button.
using delphi seattle, indy v. 10.6.2.5311
(08-29-2019, 03:12 PM)ccMcBride Wrote: attached is a test project.
There is no attachment present.
But even if there were, I would not be able to compile it anyway as I don't have a working compiler installed at the moment due to a system crash a few months ago. So while I can look at code and tell you if anything is out of place, you are going to have to do any actual debugging yourself.
(08-29-2019, 03:12 PM)ccMcBride Wrote: attached is a test project.
There is no attachment present.
But even if there were, I would not be able to compile it anyway as I don't have a working compiler installed at the moment due to a system crash a few months ago. So while I can look at code and tell you if anything is out of place, you are going to have to do any actual debugging yourself.
procedure TdlgEmail.btnOKClick(Sender: TObject);
begin
if (mmAddresses.Lines.Count = 0) and
(mmCarbon.Lines.Count = 0) and
(mmBlindCarbon.Lines.Count = 0) then
begin
ShowMessage('Please enter a valid email address');
end
else if dmEmail.SetupSMTP then
begin
if EmailMessage then
ModalResult := mrOK;
end;
end;
procedure TdlgEmail.FormDestroy(Sender: TObject);
begin
eventText.Free;
end;
procedure TdlgEmail.FormShow(Sender: TObject);
begin
mmAddresses.SetFocus;
mmCarbon.SetFocus;
mmCarbon.SelStart := 0;//scroll back to top of memo
mmCarbon.SelLength := 0;
mmBlindCarbon.SetFocus;
mmBlindCarbon.SelStart := 0;//scroll back to top of memo
mmBlindCarbon.SelLength := 0;
mmAddresses.SetFocus;
mmAddresses.SelStart := 0;//scroll back to top of memo
mmAddresses.SelLength := 0;
end;
function tdlgEmail.EmailMessage: boolean;
var
x : integer;
begin
Result := false;
if dmEmail.SetupSMTP then
begin
CreatePDFEmail;
Result := dmEmail.DoMail;
end;
end;
procedure TdlgEmail.CreatePDFEmail;
var
AlternativePartIdx: Integer;
//Stream: TStream;
begin
// let's not get too fancy here with CreateAttachment(), let's get a single Calendar
// email working properly first, then add on to the code logic later as needed ...
if reBody.Lines.Count > 0 then
begin
with TIdText.Create(dmEmail.emMessage.MessageParts, nil) do
begin
ContentType := 'multipart/alternative';
AlternativePartIdx := Index;
end;
with TIdText.Create(dmEmail.emMessage.MessageParts, reBody.Lines) do
begin
ContentType := 'text/plain';
ParentPart := AlternativePartIdx;
end;
end else
AlternativePartIdx := -1;
with TIdText.Create(dmEMail.emMessage.Messageparts, eventText) do
begin
ContentType := 'text/calendar;method=REQUEST';
ParentPart := AlternativePartIdx;
end;
with TIdAttachmentFile.Create(dmEmail.emMessage.MessageParts, 'testEvent.ics') do
begin
ContentDisposition := 'attachment';
ContentType := 'text/calendar;method=REQUEST';
ParentPart := -1;
end;
{ alternatively, since you already have the .ICS file loaded in memory, you could use this instead:
Stream := TStringStream.Create(eventText.Text, TEncoding.UTF8);
try
with TIdAttachmentMemory.Create(dmEmail.emMessage.MessageParts, Stream) do
begin
ContentDisposition := 'attachment';
ContentType := 'text/calendar;method=REQUEST';
ParentPart := -1;
end;
finally
Stream.Free;
end;
or this:
with TIdAttachmentMemory.Create(dmEmail.emMessage.MessageParts) do
begin
ContentDisposition := 'attachment';
ContentType := 'text/calendar;method=REQUEST';
ParentPart := -1;
Stream := PrepareTempStream;
try
WriteStringToStream(Stream, eventText.Text, IndyTextEncoding_UTF8);
finally
FinishTempStream;
end;
end;
}