home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 November / Chip_2002-11_cd1.bin / zkuste / delphi / kompon / d3456 / SYNAPSE.ZIP / source / lib / SMTPsend.pas < prev    next >
Pascal/Delphi Source File  |  2002-07-17  |  19KB  |  568 lines

  1. {==============================================================================|
  2. | Project : Delphree - Synapse                                   | 003.002.001 |
  3. |==============================================================================|
  4. | Content: SMTP client                                                         |
  5. |==============================================================================|
  6. | Copyright (c)1999-2002, Lukas Gebauer                                        |
  7. | All rights reserved.                                                         |
  8. |                                                                              |
  9. | Redistribution and use in source and binary forms, with or without           |
  10. | modification, are permitted provided that the following conditions are met:  |
  11. |                                                                              |
  12. | Redistributions of source code must retain the above copyright notice, this  |
  13. | list of conditions and the following disclaimer.                             |
  14. |                                                                              |
  15. | Redistributions in binary form must reproduce the above copyright notice,    |
  16. | this list of conditions and the following disclaimer in the documentation    |
  17. | and/or other materials provided with the distribution.                       |
  18. |                                                                              |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may      |
  20. | be used to endorse or promote products derived from this software without    |
  21. | specific prior written permission.                                           |
  22. |                                                                              |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"  |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE    |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE   |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR  |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL       |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR   |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER   |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT           |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY    |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH  |
  33. | DAMAGE.                                                                      |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c) 1999-2002.          |
  37. | All Rights Reserved.                                                         |
  38. |==============================================================================|
  39. | Contributor(s):                                                              |
  40. |==============================================================================|
  41. | History: see HISTORY.HTM from distribution package                           |
  42. |          (Found at URL: http://www.ararat.cz/synapse/)                       |
  43. |==============================================================================}
  44.  
  45. {$WEAKPACKAGEUNIT ON}
  46.  
  47. unit SMTPsend;
  48.  
  49. interface
  50.  
  51. uses
  52.   SysUtils, Classes,
  53.   blcksock, SynaUtil, SynaCode;
  54.  
  55. const
  56.   cSmtpProtocol = 'smtp';
  57.  
  58. type
  59.   TSMTPSend = class(TSynaClient)
  60.   private
  61.     FSock: TTCPBlockSocket;
  62.     FResultCode: Integer;
  63.     FResultString: string;
  64.     FFullResult: TStringList;
  65.     FESMTPcap: TStringList;
  66.     FESMTP: Boolean;
  67.     FUsername: string;
  68.     FPassword: string;
  69.     FAuthDone: Boolean;
  70.     FESMTPSize: Boolean;
  71.     FMaxSize: Integer;
  72.     FEnhCode1: Integer;
  73.     FEnhCode2: Integer;
  74.     FEnhCode3: Integer;
  75.     FSystemName: string;
  76.     FAutoTLS: Boolean;
  77.     FFullSSL: Boolean;
  78.     procedure EnhancedCode(const Value: string);
  79.     function ReadResult: Integer;
  80.     function AuthLogin: Boolean;
  81.     function AuthCram: Boolean;
  82.     function Helo: Boolean;
  83.     function Ehlo: Boolean;
  84.     function Connect: Boolean;
  85.   public
  86.     constructor Create;
  87.     destructor Destroy; override;
  88.     function Login: Boolean;
  89.     procedure Logout;
  90.     function Reset: Boolean;
  91.     function NoOp: Boolean;
  92.     function MailFrom(const Value: string; Size: Integer): Boolean;
  93.     function MailTo(const Value: string): Boolean;
  94.     function MailData(const Value: Tstrings): Boolean;
  95.     function Etrn(const Value: string): Boolean;
  96.     function Verify(const Value: string): Boolean;
  97.     function StartTLS: Boolean;
  98.     function EnhCodeString: string;
  99.     function FindCap(const Value: string): string;
  100.   published
  101.     property ResultCode: Integer read FResultCode;
  102.     property ResultString: string read FResultString;
  103.     property FullResult: TStringList read FFullResult;
  104.     property ESMTPcap: TStringList read FESMTPcap;
  105.     property ESMTP: Boolean read FESMTP;
  106.     property Username: string read FUsername Write FUsername;
  107.     property Password: string read FPassword Write FPassword;
  108.     property AuthDone: Boolean read FAuthDone;
  109.     property ESMTPSize: Boolean read FESMTPSize;
  110.     property MaxSize: Integer read FMaxSize;
  111.     property EnhCode1: Integer read FEnhCode1;
  112.     property EnhCode2: Integer read FEnhCode2;
  113.     property EnhCode3: Integer read FEnhCode3;
  114.     property SystemName: string read FSystemName Write FSystemName;
  115.     property Sock: TTCPBlockSocket read FSock;
  116.     property AutoTLS: Boolean read FAutoTLS Write FAutoTLS;
  117.     property FullSSL: Boolean read FFullSSL Write FFullSSL;
  118.   end;
  119.  
  120. function SendToRaw(const MailFrom, MailTo, SMTPHost: string;
  121.   const MailData: TStrings; const Username, Password: string): Boolean;
  122. function SendTo(const MailFrom, MailTo, Subject, SMTPHost: string;
  123.   const MailData: TStrings): Boolean;
  124. function SendToEx(const MailFrom, MailTo, Subject, SMTPHost: string;
  125.   const MailData: TStrings; const Username, Password: string): Boolean;
  126.  
  127. implementation
  128.  
  129. const
  130.   CRLF = #13#10;
  131.  
  132. constructor TSMTPSend.Create;
  133. begin
  134.   inherited Create;
  135.   FFullResult := TStringList.Create;
  136.   FESMTPcap := TStringList.Create;
  137.   FSock := TTCPBlockSocket.Create;
  138.   FSock.CreateSocket;
  139.   FSock.ConvertLineEnd := True;
  140.   FTimeout := 300000;
  141.   FTargetPort := cSmtpProtocol;
  142.   FUsername := '';
  143.   FPassword := '';
  144.   FSystemName := FSock.LocalName;
  145.   FAutoTLS := False;
  146.   FFullSSL := False;
  147. end;
  148.  
  149. destructor TSMTPSend.Destroy;
  150. begin
  151.   FSock.Free;
  152.   FESMTPcap.Free;
  153.   FFullResult.Free;
  154.   inherited Destroy;
  155. end;
  156.  
  157. procedure TSMTPSend.EnhancedCode(const Value: string);
  158. var
  159.   s, t: string;
  160.   e1, e2, e3: Integer;
  161. begin
  162.   FEnhCode1 := 0;
  163.   FEnhCode2 := 0;
  164.   FEnhCode3 := 0;
  165.   s := Copy(Value, 5, Length(Value) - 4);
  166.   t := SeparateLeft(s, '.');
  167.   s := SeparateRight(s, '.');
  168.   if t = '' then
  169.     Exit;
  170.   if Length(t) > 1 then
  171.     Exit;
  172.   e1 := StrToIntDef(t, 0);
  173.   if e1 = 0 then
  174.     Exit;
  175.   t := SeparateLeft(s, '.');
  176.   s := SeparateRight(s, '.');
  177.   if t = '' then
  178.     Exit;
  179.   if Length(t) > 3 then
  180.     Exit;
  181.   e2 := StrToIntDef(t, 0);
  182.   t := SeparateLeft(s, ' ');
  183.   if t = '' then
  184.     Exit;
  185.   if Length(t) > 3 then
  186.     Exit;
  187.   e3 := StrToIntDef(t, 0);
  188.   FEnhCode1 := e1;
  189.   FEnhCode2 := e2;
  190.   FEnhCode3 := e3;
  191. end;
  192.  
  193. function TSMTPSend.ReadResult: Integer;
  194. var
  195.   s: string;
  196. begin
  197.   Result := 0;
  198.   FFullResult.Clear;
  199.   repeat
  200.     s := FSock.RecvString(FTimeout);
  201.     FResultString := s;
  202.     FFullResult.Add(s);
  203.     if FSock.LastError <> 0 then
  204.       Break;
  205.   until Pos('-', s) <> 4;
  206.   s := FFullResult[0];
  207.   if Length(s) >= 3 then
  208.     Result := StrToIntDef(Copy(s, 1, 3), 0);
  209.   FResultCode := Result;
  210.   EnhancedCode(s);
  211. end;
  212.  
  213. function TSMTPSend.AuthLogin: Boolean;
  214. begin
  215.   Result := False;
  216.   FSock.SendString('AUTH LOGIN' + CRLF);
  217.   if ReadResult <> 334 then
  218.     Exit;
  219.   FSock.SendString(EncodeBase64(FUsername) + CRLF);
  220.   if ReadResult <> 334 then
  221.     Exit;
  222.   FSock.SendString(EncodeBase64(FPassword) + CRLF);
  223.   Result := ReadResult = 235;
  224. end;
  225.  
  226. function TSMTPSend.AuthCram: Boolean;
  227. var
  228.   s: string;
  229. begin
  230.   Result := False;
  231.   FSock.SendString('AUTH CRAM-MD5' + CRLF);
  232.   if ReadResult <> 334 then
  233.     Exit;
  234.   s := Copy(FResultString, 5, Length(FResultString) - 4);
  235.   s := DecodeBase64(s);
  236.   s := HMAC_MD5(s, FPassword);
  237.   s := FUsername + ' ' + StrToHex(s);
  238.   FSock.SendString(EncodeBase64(s) + CRLF);
  239.   Result := ReadResult = 235;
  240. end;
  241.  
  242. function TSMTPSend.Connect: Boolean;
  243. begin
  244.   FSock.CloseSocket;
  245.   FSock.CreateSocket;
  246.   if FFullSSL then
  247.     FSock.SSLEnabled := True;
  248.   FSock.Bind(FIPInterface, cAnyPort);
  249.   FSock.Connect(FTargetHost, FTargetPort);
  250.   Result := FSock.LastError = 0;
  251. end;
  252.  
  253. function TSMTPSend.Helo: Boolean;
  254. var
  255.   x: Integer;
  256. begin
  257.   FSock.SendString('HELO ' + FSystemName + CRLF);
  258.   x := ReadResult;
  259.   Result := (x >= 250) and (x <= 259);
  260. end;
  261.  
  262. function TSMTPSend.Ehlo: Boolean;
  263. var
  264.   x: Integer;
  265. begin
  266.   FSock.SendString('EHLO ' + FSystemName + CRLF);
  267.   x := ReadResult;
  268.   Result := (x >= 250) and (x <= 259);
  269. end;
  270.  
  271. function TSMTPSend.Login: Boolean;
  272. var
  273.   n: Integer;
  274.   auths: string;
  275.   s: string;
  276. begin
  277.   Result := False;
  278.   FESMTP := True;
  279.   FAuthDone := False;
  280.   FESMTPcap.clear;
  281.   FESMTPSize := False;
  282.   FMaxSize := 0;
  283.   if not Connect then
  284.     Exit;
  285.   if ReadResult <> 220 then
  286.     Exit;
  287.   if not Ehlo then
  288.   begin
  289.     FESMTP := False;
  290.     if not Helo then
  291.       Exit;
  292.   end;
  293.   Result := True;
  294.   if FESMTP then
  295.   begin
  296.     for n := 1 to FFullResult.Count - 1 do
  297.       FESMTPcap.Add(Copy(FFullResult[n], 5, Length(FFullResult[n]) - 4));
  298.     if (not FullSSL) and FAutoTLS and (FindCap('STARTTLS') <> '') then
  299.       if StartTLS then
  300.       begin
  301.         Ehlo;
  302.         FESMTPcap.Clear;
  303.         for n := 1 to FFullResult.Count - 1 do
  304.           FESMTPcap.Add(Copy(FFullResult[n], 5, Length(FFullResult[n]) - 4));
  305.       end;
  306.     if not ((FUsername = '') and (FPassword = '')) then
  307.     begin
  308.       s := FindCap('AUTH ');
  309.       if s = '' then
  310.         s := FindCap('AUTH=');
  311.       auths := UpperCase(s);
  312.       if s <> '' then
  313.       begin
  314.         if Pos('CRAM-MD5', auths) > 0 then
  315.           FAuthDone := AuthCram;
  316.         if (Pos('LOGIN', auths) > 0) and (not FauthDone) then
  317.           FAuthDone := AuthLogin;
  318.       end;
  319.     end;
  320.     s := FindCap('SIZE');
  321.     if s <> '' then
  322.     begin
  323.       FESMTPsize := True;
  324.       FMaxSize := StrToIntDef(Copy(s, 6, Length(s) - 5), 0);
  325.     end;
  326.   end;
  327. end;
  328.  
  329. procedure TSMTPSend.Logout;
  330. begin
  331.   FSock.SendString('QUIT' + CRLF);
  332.   ReadResult;
  333.   FSock.CloseSocket;
  334. end;
  335.  
  336. function TSMTPSend.Reset: Boolean;
  337. begin
  338.   FSock.SendString('RSET' + CRLF);
  339.   Result := ReadResult = 250;
  340. end;
  341.  
  342. function TSMTPSend.NoOp: Boolean;
  343. begin
  344.   FSock.SendString('NOOP' + CRLF);
  345.   Result := ReadResult = 250;
  346. end;
  347.  
  348. function TSMTPSend.MailFrom(const Value: string; Size: Integer): Boolean;
  349. var
  350.   s: string;
  351. begin
  352.   s := 'MAIL FROM:<' + Value + '>';
  353.   if FESMTPsize and (Size > 0) then
  354.     s := s + ' SIZE=' + IntToStr(Size);
  355.   FSock.SendString(s + CRLF);
  356.   Result := ReadResult = 250;
  357. end;
  358.  
  359. function TSMTPSend.MailTo(const Value: string): Boolean;
  360. begin
  361.   FSock.SendString('RCPT TO:<' + Value + '>' + CRLF);
  362.   Result := ReadResult = 250;
  363. end;
  364.  
  365. function TSMTPSend.MailData(const Value: TStrings): Boolean;
  366. var
  367.   n: Integer;
  368.   s: string;
  369. begin
  370.   Result := False;
  371.   FSock.SendString('DATA' + CRLF);
  372.   if ReadResult <> 354 then
  373.     Exit;
  374.   for n := 0 to Value.Count - 1 do
  375.   begin
  376.     s := Value[n];
  377.     if Length(s) >= 1 then
  378.       if s[1] = '.' then
  379.         s := '.' + s;
  380.     FSock.SendString(s + CRLF);
  381.   end;
  382.   FSock.SendString('.' + CRLF);
  383.   Result := ReadResult = 250;
  384. end;
  385.  
  386. function TSMTPSend.Etrn(const Value: string): Boolean;
  387. var
  388.   x: Integer;
  389. begin
  390.   FSock.SendString('ETRN ' + Value + CRLF);
  391.   x := ReadResult;
  392.   Result := (x >= 250) and (x <= 259);
  393. end;
  394.  
  395. function TSMTPSend.Verify(const Value: string): Boolean;
  396. var
  397.   x: Integer;
  398. begin
  399.   FSock.SendString('VRFY ' + Value + CRLF);
  400.   x := ReadResult;
  401.   Result := (x >= 250) and (x <= 259);
  402. end;
  403.  
  404. function TSMTPSend.StartTLS: Boolean;
  405. begin
  406.   Result := False;
  407.   if FindCap('STARTTLS') <> '' then
  408.   begin
  409.     FSock.SendString('STARTTLS' + CRLF);
  410.     if (ReadResult = 220) and (FSock.LastError = 0) then
  411.     begin
  412.       Fsock.SSLDoConnect;
  413.       Result := FSock.LastError = 0;
  414.     end;
  415.   end;
  416. end;
  417.  
  418. function TSMTPSend.EnhCodeString: string;
  419. var
  420.   s, t: string;
  421. begin
  422.   s := IntToStr(FEnhCode2) + '.' + IntToStr(FEnhCode3);
  423.   t := '';
  424.   if s = '0.0' then t := 'Other undefined Status';
  425.   if s = '1.0' then t := 'Other address status';
  426.   if s = '1.1' then t := 'Bad destination mailbox address';
  427.   if s = '1.2' then t := 'Bad destination system address';
  428.   if s = '1.3' then t := 'Bad destination mailbox address syntax';
  429.   if s = '1.4' then t := 'Destination mailbox address ambiguous';
  430.   if s = '1.5' then t := 'Destination mailbox address valid';
  431.   if s = '1.6' then t := 'Mailbox has moved';
  432.   if s = '1.7' then t := 'Bad sender''s mailbox address syntax';
  433.   if s = '1.8' then t := 'Bad sender''s system address';
  434.   if s = '2.0' then t := 'Other or undefined mailbox status';
  435.   if s = '2.1' then t := 'Mailbox disabled, not accepting messages';
  436.   if s = '2.2' then t := 'Mailbox full';
  437.   if s = '2.3' then t := 'Message Length exceeds administrative limit';
  438.   if s = '2.4' then t := 'Mailing list expansion problem';
  439.   if s = '3.0' then t := 'Other or undefined mail system status';
  440.   if s = '3.1' then t := 'Mail system full';
  441.   if s = '3.2' then t := 'System not accepting network messages';
  442.   if s = '3.3' then t := 'System not capable of selected features';
  443.   if s = '3.4' then t := 'Message too big for system';
  444.   if s = '3.5' then t := 'System incorrectly configured';
  445.   if s = '4.0' then t := 'Other or undefined network or routing status';
  446.   if s = '4.1' then t := 'No answer from host';
  447.   if s = '4.2' then t := 'Bad connection';
  448.   if s = '4.3' then t := 'Routing server failure';
  449.   if s = '4.4' then t := 'Unable to route';
  450.   if s = '4.5' then t := 'Network congestion';
  451.   if s = '4.6' then t := 'Routing loop detected';
  452.   if s = '4.7' then t := 'Delivery time expired';
  453.   if s = '5.0' then t := 'Other or undefined protocol status';
  454.   if s = '5.1' then t := 'Invalid command';
  455.   if s = '5.2' then t := 'Syntax error';
  456.   if s = '5.3' then t := 'Too many recipients';
  457.   if s = '5.4' then t := 'Invalid command arguments';
  458.   if s = '5.5' then t := 'Wrong protocol version';
  459.   if s = '6.0' then t := 'Other or undefined media error';
  460.   if s = '6.1' then t := 'Media not supported';
  461.   if s = '6.2' then t := 'Conversion required and prohibited';
  462.   if s = '6.3' then t := 'Conversion required but not supported';
  463.   if s = '6.4' then t := 'Conversion with loss performed';
  464.   if s = '6.5' then t := 'Conversion failed';
  465.   if s = '7.0' then t := 'Other or undefined security status';
  466.   if s = '7.1' then t := 'Delivery not authorized, message refused';
  467.   if s = '7.2' then t := 'Mailing list expansion prohibited';
  468.   if s = '7.3' then t := 'Security conversion required but not possible';
  469.   if s = '7.4' then t := 'Security features not supported';
  470.   if s = '7.5' then t := 'Cryptographic failure';
  471.   if s = '7.6' then t := 'Cryptographic algorithm not supported';
  472.   if s = '7.7' then t := 'Message integrity failure';
  473.   s := '???-';
  474.   if FEnhCode1 = 2 then s := 'Success-';
  475.   if FEnhCode1 = 4 then s := 'Persistent Transient Failure-';
  476.   if FEnhCode1 = 5 then s := 'Permanent Failure-';
  477.   Result := s + t;
  478. end;
  479.  
  480. function TSMTPSend.FindCap(const Value: string): string;
  481. var
  482.   n: Integer;
  483.   s: string;
  484. begin
  485.   s := UpperCase(Value);
  486.   Result := '';
  487.   for n := 0 to FESMTPcap.Count - 1 do
  488.     if Pos(s, UpperCase(FESMTPcap[n])) = 1 then
  489.     begin
  490.       Result := FESMTPcap[n];
  491.       Break;
  492.     end;
  493. end;
  494.  
  495. {==============================================================================}
  496.  
  497. function SendToRaw(const MailFrom, MailTo, SMTPHost: string;
  498.   const MailData: TStrings; const Username, Password: string): Boolean;
  499. var
  500.   SMTP: TSMTPSend;
  501.   s, t: string;
  502. begin
  503.   Result := False;
  504.   SMTP := TSMTPSend.Create;
  505.   try
  506. // if you need SOCKS5 support, uncomment next lines:
  507.     // SMTP.Sock.SocksIP := '127.0.0.1';
  508.     // SMTP.Sock.SocksPort := '1080';
  509. // if you need support for upgrade session to TSL/SSL, uncomment next lines:
  510.     // SMTP.AutoTLS := True;
  511. // if you need support for TSL/SSL tunnel, uncomment next lines:
  512.     // SMTP.FullSSL := True;
  513.     SMTP.TargetHost := SeparateLeft(SMTPHost, ':');
  514.     s := SeparateRight(SMTPHost, ':');
  515.     if (s <> '') and (s <> SMTPHost) then
  516.       SMTP.TargetPort := s;
  517.     SMTP.Username := Username;
  518.     SMTP.Password := Password;
  519.     if SMTP.Login then
  520.     begin
  521.       if SMTP.MailFrom(GetEmailAddr(MailFrom), Length(MailData.Text)) then
  522.       begin
  523.         s := MailTo;
  524.         repeat
  525.           t := GetEmailAddr(fetch(s, ','));
  526.           if t <> '' then
  527.             Result := SMTP.MailTo(t);
  528.           if not Result then
  529.             Break;
  530.         until s = '';
  531.         if Result then
  532.           Result := SMTP.MailData(MailData);
  533.       end;
  534.       SMTP.Logout;
  535.     end;
  536.   finally
  537.     SMTP.Free;
  538.   end;
  539. end;
  540.  
  541. function SendToEx(const MailFrom, MailTo, Subject, SMTPHost: string;
  542.   const MailData: TStrings; const Username, Password: string): Boolean;
  543. var
  544.   t: TStrings;
  545. begin
  546.   t := TStringList.Create;
  547.   try
  548.     t.Assign(MailData);
  549.     t.Insert(0, '');
  550.     t.Insert(0, 'x-mailer: Synapse - Delphi & Kylix TCP/IP library by Lukas Gebauer');
  551.     t.Insert(0, 'subject: ' + Subject);
  552.     t.Insert(0, 'date: ' + Rfc822DateTime(now));
  553.     t.Insert(0, 'to: ' + MailTo);
  554.     t.Insert(0, 'from: ' + MailFrom);
  555.     Result := SendToRaw(MailFrom, MailTo, SMTPHost, t, Username, Password);
  556.   finally
  557.     t.Free;
  558.   end;
  559. end;
  560.  
  561. function SendTo(const MailFrom, MailTo, Subject, SMTPHost: string;
  562.   const MailData: TStrings): Boolean;
  563. begin
  564.   Result := SendToEx(MailFrom, MailTo, Subject, SMTPHost, MailData, '', '');
  565. end;
  566.  
  567. end.
  568.