Nedávno jsem programoval komunikační modul do našeho systému a jedna z jeho součástí byla funkce potvrzování doručení mailů a to na obou úrovních které znám.
Samozřejmě jsem použil jako vždy Synapsi a rád bych zde o tom napsal několik poznámek.
Oprava: v tom MailToDNS byla chybka - za tím rfc822 se má přidat emailová adresa. Opraveno. Sice to fungovalo i tak, ale přesto.
Potvrzování funguje na dvou stupních:
- potvrzení o přečtení (resp. zobrazení mailovým klientem)
- potvrzení o doručení cílovému serveru
První část je zajišťována vhodnou strukturou hlaviček zprávy, konkrétně přidáním Disposition-Notification-To.
var
oMail:TMimeMess;
….
// vytvoření zprávy
oMail.Header.CustomHeaders.Add('Disposition-Notification-To:'+sReply)
//poslat via SMTP, např. funkcí SendToRaw z jednotky smtpsend
To vcelku nebyl problém.
Horší bylo implementovat druhý bod - potvrzení o doručení. Tato informace je zasílána přímo SMTP serverem
a to v případě kdy:
- to SMTP server umí (musí umět rozšíření ESMTP, což není vždy)
- je o to při komunikaci požádán
Standardně Synapse podporuje podmnožinu příkazů z rozšíření ESMTP, přičemž tato část podporována není. Naštěstí kód synapse je přímý jak
žebřík, tudíž pokud člověk ví co chce tak úpravu provede i kutil Tim. Výhodou Synapse je to, že se jedná o knihovnu tříd, takže si je člověk
může začlenit do svého projektu a přiohnout jak potřebuje, přičemž pro jiný projekt může například použít zase "vanilla" verzi bez úprav.
Jak tedy na to?
Předpokládám, že máte sestrojen mail a používáte např. funkci SendToRaw. Začneme tím, že si tuto pomocnou funkci zkopírujeme a budeme ji rozšiřovat.
Tato funkce v podstatě vytvoří instanci třídy TSMTPSend, která implementuje komunikaci se SMTP serverem. Odeslání mailu spočívá v podstatě z přihlášení k serveru,
zaslání informací jako je adresát a odesílatel a poslání dat, následované logout.
Toto je samozřejmě jednoduchá varianta, která nám stačit nebude. Popsaný kód rozšíříme o test zda server podporuje ESMTP rozšíření a pokud ano, tak zda z plejády rozšíření podporuje DSN (Delivery Status Notifications).
Tzn. něco ve smyslu:
var
SMTP: TSMTPSend;
…
if SMTP.Login then
begin
bSupportDSN := SMTP.ESMTP and (SMTP.ESMTPcap.IndexOf('DSN') > -1);
// podporuje SMTP naše rozšíření?
Nyní SMTP server může dostat informaci o adresátovi, v Synapsi metoda MailTo
function TSMTPSend.MailTo(const Value: string): Boolean;
begin
FSock.SendString('RCPT TO:<' + Value + '>' + CRLF);
Result := ReadResult div 100 = 2;
end;
Jak jsem uvedl, je to velmi čitelný kód. Pokud chceme ale informaci o doručení je nutno toto změnit. Problémek je, že metoda ReadResult je private.
Pod minulým článkem oxo uvedl, že na private položky se dá dostat přes class helper, ale v mém případě bylo jednodušší přidat další metodu.
function TSMTPSend.MailToDNS(const Value: string): Boolean;
begin
FSock.SendString('RCPT TO:<' + Value + '> NOTIFY=FAILURE,SUCCESS ORCPT=rfc822;'+Value + CRLF);
Result := ReadResult div 100 = 2;
end;
Pokud porovnáte s předchozím kódem tak uvidíte, že prosím SMTP server aby mi dal vědět v případě jak selhání, tak úspěchu. V předchozím případě by pro většinu
SMTP serverů byl nastaven ekvivalent NOTIFY=FAILURE, tj. poslat jen při chybě. ORCPT značí, že SMTP server by měl ponechat původní informace o hlavičkách.
No a nyní stačí jen čekat na odpověď od serveru, která bude poslána na zadaný mail. Ten obsahuje část s identifikací Content-Type: multipart/report a pak buďto report-type=delivery-status nebo report-type=disposition-notification. Mail stáhnete přes POP3 nebo IMAP a není třeba stahovat vše, ale jen hlavičky (aspoň v první fázi).
Berte tento text spíše jako nakopnutí než kompletní řešení. Ještě jednou díky Lukáši za Synapsi.