MailRouting.pas
上传用户:dh8233980
上传日期:2014-10-16
资源大小:1015k
文件大小:63k
源码类别:

Email服务器

开发平台:

Delphi

  1. unit MailRouting;
  2. (******************************************************************************)
  3. (*                                                                            *)
  4. (* SMTP Mail Routing Utilities                                                *)
  5. (* Part of Hermes SMTP/POP3 Server.                                           *)
  6. (* Copyright(C) 2000 by Alexander J. Fanti, All Rights Reserver Worldwide.    *)
  7. (*                                                                            *)
  8. (* Contains: TSmtpMessageInformation, TListServer, TDeliverMail               *)
  9. (*                                                                            *)
  10. (* Created January 18, 2000 by Alexander J. Fanti.  See License.txt           *)
  11. (*                                                                            *)
  12. (* Depends on: DataU1 (TMailListInformation, routing)                         *)
  13. (*             Main   (Posts a Windows Message to the form to tell it we need *)
  14. (*                     some queue processing)                                 *)
  15. (*             UtilU1 (Address (e-mail) formatting)                           *)
  16. (*                                                                            *)
  17. (* Used by: SmtpServer                                                        *)
  18. (*                                                                            *)
  19. (* Description:                                                               *)
  20. (* TSmtpMessageInformation - An object used to manipulate a message coming in *)
  21. (*                           from an SMTP connection to the Smtp Server.  We  *)
  22. (*                           can manage the forward and reverse paths and     *)
  23. (*                           mail data with it.                               *)
  24. (* TListServer - An object used to examine mail to a list to determine if it  *)
  25. (*               should be distributed to the list, or interpreted as a       *)
  26. (*               command.  All commands are in the mail subject and preceeded *)
  27. (*               by a bang(!).  The command must then follow with any         *)
  28. (*               arguments.  The mail body is ignored.                        *)
  29. (* TDeliverMail - An object used to examine the To Routes of a mail message   *)
  30. (*                and deliver the mail in the following way: 1) convert       *)
  31. (*                alias to user, 2) Check user has forward (if so, rename to) *)
  32. (*                3) If user local, deliver and drop to, 4) if user is list,  *)
  33. (*                send to TListServer for further processing, 5) if user is   *)
  34. (*                non-local, queue for agent processing.                      *)
  35. (*                                                                            *)
  36. (* Revisions: 1/19/2000  AJF  Added TDeliverMail, debugged and commented      *)
  37. (*            2/13/2000  AJF  Added AccessControl data to                     *)
  38. (*                            TSmtpMessageInformation to facilitate Smtp      *)
  39. (*                            Server Access control                           *)
  40. (*                                                                            *)
  41. (******************************************************************************)
  42. interface
  43. uses Windows, Classes, SysUtils, INIFiles,
  44.      DataU1;
  45. type
  46.   TSmtpMessageInformation = class(TObject)
  47.   private
  48.     FReverseRoute : TMessageRouteInformation;
  49.     FForwardRoute : TList;
  50.     FData : TStringList;
  51.     function GetForwardRouteCount : Longint;
  52.     function GetForwardRoute(Index : Longint) : TMessageRouteInformation;
  53.     function GetHeaderElement(Element : String) : String;
  54.     procedure SetHeaderElement(Element, Value : String);
  55.     function GetAddress(MailRoute : String) : String;
  56.     function GetRoute(MailAddress : String) : String;
  57.   public
  58.     // AccCtrl variables are used store information about the sender by the
  59.     // Smtp Server Connection about the transaction as the message comes in
  60.     AccCtrl_ToLocalUser        : Boolean;  // True if RCPT TO is to local domain
  61.                                            // and mailbox (user, alias, list)
  62.     AccCtrl_ToLocalCount       : Longint;  // Count of RCPT TOs that are to a
  63.                                            // local user
  64.     AccCtrl_FromLocalUser      : Boolean;  // MAIL FROM is a local user at a
  65.                                            // local domain
  66.     AccCtrl_FromLocalDomain    : Boolean;  // MAIL FROM is just from local
  67.                                            // domain
  68.     AccCtrl_FromAcceptedDomain : Boolean;  // MAIL FROM is from an Accepted
  69.                                            // domain
  70.     AccCtrl_FromBannedDomain   : Boolean;  // MAIL FROM is from a Banned domain
  71.     AccCtrl_FromBannedMailbox  : Boolean;  // MAIL FROM is from a Banned Mailbox
  72.     AccCtrl_MessgaeSizeInBytes : Longint;  // Size of message
  73.     constructor Create;
  74.     destructor Destroy; Override;
  75.     procedure Initialize;
  76.     procedure InsertReceived;
  77.     procedure AddSelfToReverseRoute;
  78.     procedure Data_AppendLine(Line : String);
  79.     property ReverseRoute : TMessageRouteInformation
  80.              read FReverseRoute write FReverseRoute;
  81.     property ForwardRoute[Index : Longint] : TMessageRouteInformation
  82.              read GetForwardRoute;
  83.     property ForwardRouteCount : Longint read GetForwardRouteCount;
  84.     function AddForwardRoute(Route : String) : Boolean;
  85.     procedure DeleteForwardRoute(Index : Longint);
  86.     procedure ClearForwardRoutes;
  87.     property Data : TStringList read FData;
  88.     function SaveToFile : Boolean; // Header in ###.ini, data in ###.txt
  89.     function LoadFromFile(MailID : String) : Boolean;
  90.     function GetHeader_Subject : String;
  91.     procedure SetHeader_Subject(Subject : String);
  92.     procedure GetHeader_From(var Address : String; var Route : String);
  93.     procedure SetHeader_From(EMailAddress : String);
  94.     procedure GetHeader_To(var Address : String; var Route : String);
  95.     procedure SetHeader_To(EMailAddress : String);
  96.     procedure GetHeader_ReplyTo(var Address : String; var Route : String);
  97.     procedure SetHeader_ReplyTo(EMailAddress : String);
  98.   end;
  99.   // Terminology Definition for this module.
  100.   // Address (ListAddress) refers to a user style email address
  101.   //                       such as username@domain.com
  102.   // Route (ListRoute) refers to a SMTP style email address
  103.   //                   such as <"username"@[IPAddress]|domain.com>
  104.   TListServer_StatusUpdate = procedure(Sender : TObject; Status : String;
  105.                                        Level : Integer) of Object;
  106.   TListServer = class(TObject)
  107.   private
  108.     FListName, FListAddress, FListRoute : String;
  109.     FMailList : TMailListInformation;
  110.     FMessageInfo : TSmtpMessageInformation;
  111.     FSenderAddress, FFromAddress : String;  // email of sender (no <>)
  112.     FFromRoute : String;
  113.     FOnStatusUpdate : TListServer_StatusUpdate; // Event Ptr for StatusUpdate
  114.     procedure StatusUpdate(Status : String; Level : Integer); // Report Status
  115.     procedure ParseSubject(Subject : String; var Command : String;
  116.                            var Parameter : String);
  117.     procedure BuildMessageToUser(MessageType, SenderAddress,
  118.                                  FailureMessage : String;
  119.                                  MagicNumber : Longint;
  120.                                  ExpirationDate : TDateTime);
  121.     procedure AddMessageToArchive;
  122.   public
  123.     constructor Create(ListName, ReversePath : String; Data : TStringList);
  124.     destructor Destroy; Override;
  125.     procedure Process;
  126.     property OnStatusUpdate : TListServer_StatusUpdate    // Fired on Status
  127.              read FOnStatusUpdate write FOnStatusUpdate;  // Update
  128.   end;
  129.   TDeliverMail_StatusUpdate = procedure(Sender : TObject; Status : String;
  130.                                         Level : Integer) of Object;
  131.   TDeliverMail = class(TObject)
  132.   private
  133.     FMessageInfo : TSmtpMessageInformation;
  134.     FOnStatusUpdate : TDeliverMail_StatusUpdate; // Event Ptr for StatusUpdate
  135.     procedure StatusUpdate(Status : String; Level : Integer); // Report Status
  136.     procedure ProcessAlias(var UserName : String; Domain : String);
  137.     procedure DeliverLocally(UserInfo : TPop3UserInformation);
  138.     procedure SendUndeliverableReply(DestinationPath : String);
  139.   public
  140.     constructor Create(MessageInfo : TSmtpMessageInformation);
  141.     destructor Destroy; Override;
  142.     procedure Deliver;
  143.     property OnStatusUpdate : TDeliverMail_StatusUpdate   // Fired on Status
  144.              read FOnStatusUpdate write FOnStatusUpdate;  // Update
  145.   end;
  146. implementation
  147. uses Main, {This is so we can call a "Trigger" of the
  148.             Smtp Agent Queue to get service.}
  149.      UtilU1;
  150. (******************************************************************************)
  151. (*                                                                            *)
  152. (* START Message Information Object                                           *)
  153. (*                                                                            *)
  154. (* This object is used for receiving and manipulating a message.              *)
  155. (* We accept data to it, set the Reverse-Path and add Forward-Paths.          *)
  156. (* We can also iterate throught the Forward-Paths and manipulate them or      *)
  157. (* remove them as we achieve delivery to each one.  Then we save the          *)
  158. (* message, qeueing it for processing with the Smtp Agent.                    *)
  159. (*                                                                            *)
  160. (******************************************************************************)
  161. constructor TSmtpMessageInformation.Create;
  162. begin
  163.   inherited Create;
  164.   FReverseRoute := TMessageRouteInformation.Create(mrte_From);
  165.   FForwardRoute := TList.Create;
  166.   FData := TStringList.Create;
  167.   Initialize;
  168. end;
  169. procedure TSmtpMessageInformation.ClearForwardRoutes;
  170. var
  171.   x : Longint;
  172.   RouteInfo : TMessageRouteInformation;
  173. begin
  174.   for x := FForwardRoute.Count -1 downto 0 do begin
  175.     RouteInfo := FForwardRoute[x];
  176.     FForwardRoute.Delete(x);  // Remove from Forward-Path List
  177.     RouteInfo.Free;           // Drop Forward-Path Object
  178.   end;
  179. end;
  180. destructor TSmtpMessageInformation.Destroy;
  181. begin
  182.   FReverseRoute.Free;  // Drop Reverse-Path Object
  183.   ClearForwardRoutes;  // Drop each Forward-Path Object
  184.   FForwardRoute.Free;  // Drop Forward-Path List
  185.   FData.Free;          // Drop message data
  186.   inherited Destroy;
  187. end;
  188. procedure TSmtpMessageInformation.AddSelfToReverseRoute;
  189. begin
  190.   // if the most recent Host in the Reverse Route isn't one of mine,
  191.   // then I add my ServerName to the start of the Hosts list of the
  192.   // Reverse-Path
  193.   if not INI.Domain_IsThisOneOfMine(FReverseRoute.Hosts[0]) then
  194.     FReverseRoute.Hosts.Insert(0, INI.ServerName);
  195. end;
  196. function TSmtpMessageInformation.GetForwardRouteCount : Longint;
  197. begin
  198.   Result := FForwardRoute.Count;
  199. end;
  200. function TSmtpMessageInformation.GetForwardRoute(Index : Longint)
  201.          : TMessageRouteInformation;
  202. begin
  203.   Result := nil;
  204.   if (Index >= 0) and (Index < FForwardRoute.Count) then
  205.     Result := FForwardRoute[Index];
  206. end;
  207. procedure TSmtpMessageInformation.Initialize;
  208. begin
  209.   ClearForwardRoutes;        // Drop each Forward-Path Object
  210.   FReverseRoute.Initialize;  // Clear the Reverse-Path Object
  211.   FData.Clear;               // Clear the message data
  212.   AccCtrl_ToLocalUser        := False;
  213.   AccCtrl_ToLocalCount       := 0;
  214.   AccCtrl_FromLocalUser      := False;
  215.   AccCtrl_FromLocalDomain    := False;
  216.   AccCtrl_FromAcceptedDomain := False;
  217.   AccCtrl_FromBannedDomain   := False;
  218.   AccCtrl_FromBannedMailbox  := False;
  219.   AccCtrl_MessgaeSizeInBytes := 0;
  220. end;
  221. procedure TSmtpMessageInformation.InsertReceived;
  222. var
  223.   FromHost : String;
  224. begin
  225.   // Here I insert a line into the top of the message data (presumably
  226.   // the message header).  This line contains "Received" information
  227.   // in the format:
  228.   // Received: FROM [host] BY [ServerName] ; DD Mon YY HH:MM:SS Zone
  229.   //
  230.   // This should happen only once for any message, when it's received.
  231.   //
  232.   if FReverseRoute.Hosts.Count > 0 then FromHost := FReverseRoute.Hosts[0]
  233.     else FromHost := FReverseRoute.Domain;
  234.   FData.Insert(0, 'Received: FROM ' + FromHost + ' BY ' + INI.ServerName +
  235.                   ' ; ' + INI.TimeStamp);
  236. end;
  237. procedure TSmtpMessageInformation.Data_AppendLine(Line : String);
  238. begin
  239.   FData.Add(Line);  // Add this line to the end of the message data
  240. end;
  241. function TSmtpMessageInformation.AddForwardRoute(Route : String) : Boolean;
  242. var
  243.   RouteInfo : TMessageRouteInformation;
  244.   x : Longint;
  245. begin
  246.   // Given a string that specifies a route in the Smtp format
  247.   // <@HostA,@[#.#.#.#]:"mailbox"@HostC>
  248.   // Create a Forward-Path route and add it to the List of ForwardPaths
  249.   //
  250.   RouteInfo := TMessageRouteInformation.Create(mrte_To);
  251.   x := RouteInfo.ParseRoute(Route);
  252.   if x = 0 then begin
  253.     // If I successfullt parsed the route...
  254.     // Remove my domain(s) from top of the to route... it's reached me
  255.     while (RouteInfo.Hosts.Count > 0) and
  256.           (INI.Domain_IsThisOneOfMine(RouteInfo.Hosts[0])) do
  257.       RouteInfo.Hosts.Delete(0);
  258.     // Now add this route to the list of Forward Paths
  259.     FForwardRoute.Add(RouteInfo);
  260.     Result := True;
  261.   end else begin
  262.     // I did not successfully parse the route, so I need to free the route
  263.     // object instead of adding it to the list
  264.     RouteInfo.Free;
  265.     Result := False;
  266.   end;
  267. end;
  268. procedure TSmtpMessageInformation.DeleteForwardRoute(Index : Longint);
  269. var
  270.   RouteInfo : TMessageRouteInformation;
  271. begin
  272.   if (Index >= 0) and (Index < FForwardRoute.Count) then begin
  273.     RouteInfo := FForwardRoute[Index];
  274.     FForwardRoute.Delete(Index);  // Remove from Forward-Path List
  275.     RouteInfo.Free;               // Drop Forward-Path Object
  276.   end;
  277. end;
  278. function TSmtpMessageInformation.LoadFromFile(MailID : String) : Boolean;
  279. var
  280.   SL : TStringList;
  281.   x, y : Longint;
  282.   tempStr : String;
  283.   RouteInfo : TMessageRouteInformation;
  284.   Found : Boolean;
  285. begin
  286.   Result := False;
  287.   if FileExists(INI.MailQueuePath + MailID + '.txt') then begin
  288.     Self.Initialize;
  289.     SL := TStringList.Create;
  290.     SL.LoadFromFile(INI.MailQueuePath + MailID + '.txt');
  291.     if SL.Count > 0 then begin
  292.       // Read Reverse Path...
  293.       Found := False;
  294.       x := 0;
  295.       while (x < SL.Count) and (not Found) do begin
  296.         tempStr := Trim(SL[x]);
  297.         if  UpperCase(tempStr) = '[REVERSE PATH]' then begin
  298.           Inc(x);  // Skip the header line...
  299.           // read the From path...
  300.           if x < SL.Count then begin
  301.             tempStr := Trim(SL[x]);
  302.             if UpperCase(Copy(tempStr, 1, 5)) = 'FROM=' then begin
  303.               tempStr := Copy(tempStr, 6, Length(tempStr));
  304.               if tempStr <> '' then FReverseRoute.ParseRoute(tempStr);
  305.             end;
  306.           end;
  307.           Found := True;
  308.         end else Inc(x);
  309.       end;
  310.       // Read Forward Path...
  311.       Found := False;
  312.       x := 0;
  313.       while (x < SL.Count) and (not Found) do begin
  314.         tempStr := Trim(SL[x]);
  315.         if  UpperCase(tempStr) = '[FORWARD PATH]' then begin
  316.           Inc(x);  // Skip the header line...
  317.           // read the To paths...
  318.           if x < SL.Count then begin
  319.             tempStr := Trim(SL[x]);
  320.             while (x < SL.Count) and (Copy(tempStr, 1, 1) <> '[') do begin
  321.               if UpperCase(Copy(tempStr, 1, 3)) = 'TO=' then begin
  322.                 tempStr := Copy(tempStr, 4, Length(tempStr));
  323.                 if tempStr <> '' then begin
  324.                   RouteInfo := TMessageRouteInformation.Create(mrte_To);
  325.                   y := RouteInfo.ParseRoute(SL[x]);
  326.                   if y = 0 then FForwardRoute.Add(RouteInfo)
  327.                     else RouteInfo.Free;
  328.                 end;
  329.               end;
  330.               Inc(x);
  331.               tempStr := Trim(SL[x]);
  332.             end;
  333.           end;
  334.           Found := True;
  335.         end else Inc(x);
  336.       end;
  337.       // We don't need to read the Retry information here...
  338.       // and we don't really... nut I write the code incase it's
  339.       // necessary in the future.  Also note, this object's LoadFromFile
  340.       // and SaveToFile are nearly identicle to it's sister Object
  341.       // SmtpAgentMessageInformation which reads the same info from
  342.       // the same file, but stores it differently for the purposes
  343.       // of the Smtp Agent Object
  344.       // Read Retry Information...
  345.       Found := False;
  346.       x := 0;
  347.       while (x < SL.Count) and (not Found) do begin
  348.         tempStr := Trim(SL[x]);
  349.         if  UpperCase(tempStr) = '[RETRY]' then begin
  350.           Inc(x);  // Skip the header line...
  351.           // read the retry information
  352.           if x < SL.Count then begin
  353.             tempStr := Trim(SL[x]);
  354.             while (x < SL.Count) and (Copy(tempStr, 1, 1) <> '[') do begin
  355.               if UpperCase(Copy(tempStr, 1, 10)) = 'PERFORMED=' then begin
  356.                 tempStr := Copy(tempStr, 11, Length(tempStr));
  357.                 // := StringToInteger(tempStr, 0);
  358.               end;
  359.               if UpperCase(Copy(tempStr, 1, 10)) = 'REMAINING=' then begin
  360.                 tempStr := Copy(tempStr, 11, Length(tempStr));
  361.                 // := StringToInteger(tempStr, 0);
  362.               end;
  363.               Inc(x);
  364.               tempStr := Trim(SL[x]);
  365.             end;
  366.           end;
  367.           Found := True;
  368.         end else Inc(x);
  369.       end;
  370.       // Read Mail Data first...
  371.       // We know the [Message] data is the last thing in the file... so...
  372.       Found := False;
  373.       x := 0;
  374.       while (x < SL.Count) and (not Found) do begin
  375.         tempStr := Trim(SL[x]);
  376.         if  UpperCase(tempStr) = '[MESSAGE]' then begin
  377.           Inc(x);  // Skip the header line...
  378.           for y := x to SL.Count -1 do FData.Add(SL[y]);  // Copy data in...
  379.           Found := True;
  380.         end else Inc(x);
  381.       end;
  382.       SL.Free;
  383.       Result := True;
  384.     end;
  385.   end;
  386. end;
  387. function TSmtpMessageInformation.SaveToFile : Boolean;
  388. var
  389.   SL : TStringList;
  390.   FilenameOnly : String;
  391.   x : Longint;
  392.   RouteInfo : TMessageRouteInformation;
  393. begin
  394.   FilenameOnly := GetUniqueFilename(INI.MailQueuePath);
  395.   Result := False;
  396.   SL := TStringList.Create;
  397.   SL.Add('[Reverse Path]');
  398.   SL.Add('From=' + FReverseRoute.BuildRoute);
  399.   SL.Add('');
  400.   SL.Add('[Forward Path]');
  401.   for x := 0 to FForwardRoute.Count -1 do begin
  402.     RouteInfo := FForwardRoute[x];
  403.     SL.Add('To=' + RouteInfo.BuildRoute);
  404.   end;
  405.   SL.Add('');
  406.   SL.Add('[Retry]');
  407.   SL.Add('Performed=' + IntToStr(0));
  408.   SL.Add('Remaining=' + IntToStr(INI.Smtp_Retries));
  409.   SL.Add('');
  410.   // Message data must always be last in file!
  411.   SL.Add('[Message]');
  412.   for x := 0 to FData.Count -1 do SL.Add(FData[x]);
  413.   try
  414.     SL.SaveToFile(INI.MailQueuePath + FilenameOnly + '.txt');
  415.     Result := True;
  416.   except
  417.     on E: Exception do Result := False;
  418.   end;
  419.   SL.Free;
  420. end;
  421. function TSmtpMessageInformation.GetHeaderElement(Element : String) : String;
  422. // Assumes : seperates Element and Value and we don't specify colon
  423. var
  424.   x, Len : Longint;
  425.   Found : Boolean;
  426. begin
  427.   Result := '';
  428.   Len := Length(Element);
  429.   x := 0;
  430.   Found := False;
  431.   // Loop through data until we either 1) find out element,
  432.   //                                   2) run out of data, or
  433.   //                                   3) finish the header
  434.   while (not Found) and (x < FData.Count) and (FData[x] <> '') do begin
  435.     // I'll match an element regardless of case...
  436.     if LowerCase(Copy(Data[x], 1, Len +1)) = LowerCase(Element + ':') then begin
  437.       Result := Trim(Copy(Data[x], Len +2, Length(FData[x])));
  438.       Found := True;
  439.     end else Inc(x);
  440.   end;
  441. end;
  442. procedure TSmtpMessageInformation.SetHeaderElement(Element, Value : String);
  443. // Assumes : seperates Element and Value and we don't specify colon
  444. var
  445.   x, Len : Longint;
  446.   Found : Boolean;
  447. begin
  448.   Len := Length(Element);
  449.   x := 0;
  450.   Found := False;
  451.   // Loop through data until we either 1) find out element,
  452.   //                                   2) run out of data, or
  453.   //                                   3) finish the header
  454.   while (not Found) and (x < FData.Count) and (FData[x] <> '') do begin
  455.     // Should I be case insensitive here?
  456.     if LowerCase(Copy(Data[x], 1, Len +1)) = LowerCase(Element + ':') then begin
  457.       // We found the element and must add the value
  458.       // to the name after the ": "
  459.       FData[x] := Copy(Data[x], 1, Len) + ': ' + Value;
  460.       Found := True;
  461.     end else Inc(x);
  462.   end;
  463.   if not Found then begin
  464.     // we did not find the element.  we must add it to the header
  465.     FData.Insert(x, Element + ': ' + Value);
  466.   end;
  467. end;
  468. function TSmtpMessageInformation.GetHeader_Subject : String;
  469. begin
  470.   Result := GetHeaderElement('Subject');
  471. end;
  472. procedure TSmtpMessageInformation.SetHeader_Subject(Subject : String);
  473. begin
  474.   SetHeaderElement('Subject', Subject);
  475. end;
  476. function TSmtpMessageInformation.GetAddress(MailRoute : String) : String;
  477. var
  478.   Route : TMessageRouteInformation;
  479. begin
  480.   // Format a route or address string as an address
  481.   Result := MailRoute;
  482.   Route := TMessageRouteInformation.Create(mrte_Unknown);
  483.   if Route.ParseRoute(MailRoute) = 0 then
  484.     Result := Route.MailBox + '@' + Route.Domain;
  485.   Route.Free;
  486. end;
  487. function TSmtpMessageInformation.GetRoute(MailAddress : String) : String;
  488. var
  489.   Route : TMessageRouteInformation;
  490. begin
  491.   // Format a route or address string as a route
  492.   Result := MailAddress;
  493.   Route := TMessageRouteInformation.Create(mrte_Unknown);
  494.   if Route.ParseRoute(MailAddress) = 0 then
  495.     Result := Route.BuildRoute;
  496.   Route.Free;
  497. end;
  498. procedure TSmtpMessageInformation.GetHeader_From(var Address : String;
  499.                                                  var Route : String);
  500. var
  501.   tempStr : String;
  502. begin
  503.   tempStr := GetHeaderElement('From');
  504.   Address := GetAddress(tempStr);
  505.   Route := GetRoute(tempStr);
  506. end;
  507. procedure TSmtpMessageInformation.SetHeader_From(EMailAddress : String);
  508. begin
  509.   SetHeaderElement('From', GetRoute(EMailAddress));
  510. end;
  511. procedure TSmtpMessageInformation.GetHeader_To(var Address : String;
  512.                                                var Route : String);
  513. var
  514.   tempStr : String;
  515. begin
  516.   tempStr := GetHeaderElement('To');
  517.   Address := GetAddress(tempStr);
  518.   Route := GetRoute(tempStr);
  519. end;
  520. procedure TSmtpMessageInformation.SetHeader_To(EMailAddress : String);
  521. begin
  522.   SetHeaderElement('To', GetRoute(EMailAddress));
  523. end;
  524. procedure TSmtpMessageInformation.GetHeader_ReplyTo(var Address : String;
  525.                                                     var Route : String);
  526. var
  527.   tempStr : String;
  528. begin
  529.   tempStr := GetHeaderElement('Reply-To');
  530.   Address := GetAddress(tempStr);
  531.   Route := GetRoute(tempStr);
  532. end;
  533. procedure TSmtpMessageInformation.SetHeader_ReplyTo(EMailAddress : String);
  534. begin
  535.   SetHeaderElement('Reply-To', GetRoute(EMailAddress));
  536. end;
  537. (******************************************************************************)
  538. (*                                                                            *)
  539. (*  STOP  Message Information Object                                          *)
  540. (*                                                                            *)
  541. (******************************************************************************)
  542. (******************************************************************************)
  543. (*                                                                            *)
  544. (* START ListServer Object                                                    *)
  545. (*                                                                            *)
  546. (* This object is used to process mail addressed to a local list.  It         *)
  547. (* determines if the mail is a command to the list, and acts on that command, *)
  548. (* generally performing an action to the list (sub, unsub, etc.) and sending  *)
  549. (* notification mail back to the original requestor.                          *)
  550. (* If the mail is not a command, it is assumed to be a message for the list,  *)
  551. (* and is re-addressed for delivery to list members.  It is then delivered    *)
  552. (* locally or queued by the Deliver Mail Object                               *)
  553. (*                                                                            *)
  554. (******************************************************************************)
  555. constructor TListServer.Create(ListName, ReversePath : String;
  556.                                Data : TStringList);
  557. var
  558.   x : Longint;
  559.   Member : PMailListMemberInfoRec;
  560.   Route : TMessageRouteInformation;
  561. begin
  562.   inherited Create;
  563.   // Make copy of message to service.
  564.   // we'll have to change forward paths and data for list members
  565.   FMessageInfo := TSmtpMessageInformation.Create;
  566.   FMessageInfo.ReverseRoute.ParseRoute(ReversePath);
  567.   for x := 0 to Data.Count -1 do FMessageInfo.Data_AppendLine(Data[x]);
  568.   // Get List information (name, address and route)
  569.   FListName := ListName;
  570.   FListAddress := FListName + '@' + INI.ServerName;
  571.   Route := TMessageRouteInformation.Create(mrte_Unknown);
  572.   FListRoute := FListAddress;
  573.   if Route.ParseRoute(FListAddress) = 0 then FListRoute := Route.BuildRoute;
  574.   Route.Free;
  575.   // Open mailing list information
  576.   FMailList := TMailListInformation.Create;
  577.   FMailList.LoadFromFile(FListName);
  578.   // Fill To routes of message with list members addresses
  579.   for x := 0 to FMailList.MemberCount -1 do begin
  580.     Member := FMailList.Members[x];
  581.     if Member.Active then FMessageInfo.AddForwardRoute(Member.EMail);
  582.   end;
  583.   // Aquire Sender address and route from routing information
  584.   FSenderAddress := '';
  585.   FSenderAddress := FMessageInfo.ReverseRoute.Mailbox + '@' +
  586.                     FMessageInfo.ReverseRoute.Domain;
  587.   FFromAddress := '';
  588.   FFromRoute := '';
  589.   FMessageInfo.GetHeader_From(FFromAddress, FFromRoute);
  590.   // Don't call Process here... then we couldn't set statusupdate first
  591. end;
  592. destructor TListServer.Destroy;
  593. begin
  594.   FMessageInfo.Free;
  595.   FMailList.Free;
  596.   inherited Destroy;
  597. end;
  598. procedure TListServer.StatusUpdate(Status : String; Level : Integer);
  599. begin
  600.   if Assigned(FOnStatusUpdate) then OnStatusUpdate(Self, Status, Level);
  601. end;
  602. procedure TListServer.AddMessageToArchive;
  603. var
  604.   F : TextFile;
  605.   x : Longint;
  606. begin
  607.   if (FMailList.ArchiveFile <> '') and (FMessageInfo.Data.Count > 0) then begin
  608.     AssignFile(F, FMailList.ArchiveFile);
  609.     try
  610.       Append(F);
  611.       for x := 0 to FMessageInfo.Data.Count -1 do
  612.         Writeln(F, FMessageInfo.Data[x]);
  613.       Writeln(F, '(------------------)');
  614.     except
  615.       on E: Exception do try
  616.         ReWrite(F);
  617.         for x := 0 to FMessageInfo.Data.Count -1 do
  618.           Writeln(F, FMessageInfo.Data[x]);
  619.         Writeln(F, '(------------------)');
  620.       except
  621.         on E: Exception do begin end;
  622.       end;
  623.     end;
  624.     CloseFile(F);
  625.   end;
  626. end;
  627. procedure TListServer.Process;
  628. var
  629.   x : Longint;
  630.   Command, Parameter : String;
  631.   MailToProcess : Boolean; // True if there is mail that must be routed after
  632.                            // we do our list server thing
  633.   MailForList : Boolean;   // True if the mail we processed here goes to the
  634.                            // list instead of a single list member
  635.   UserAddress : String;    // This is the address to send list server replies.
  636.                            // It's chosen by parameter to command, then from,
  637.                            // then reversepath.
  638.   MagicNumber : Integer;   // A random number that's a subscriber's "ID"
  639.   ExpirationDate : TDateTime;  // Date and time when MagicNumber becomes invalid
  640.   Deleted : Boolean;           // Have we removed a list member?
  641.   Accept : Boolean;
  642.   PendingMember : PMailListPendingMemberInfoRec;
  643.   Member : PMailListMemberInfoRec;
  644.   DeliverMail : TDeliverMail;
  645.   Route : TMessageRouteInformation;
  646. begin
  647.   StatusUpdate('Processing Mail for List', STAT_PROCESSINGEVENT);
  648.   ParseSubject(FMessageInfo.GetHeader_Subject, Command, Parameter);
  649.   if Command <> '' then begin
  650.     MailToProcess := False;
  651.     MailForList := False;
  652.     StatusUpdate('Command: ' + Command + ' (' + Parameter + ')',
  653.                  STAT_PROCESSINGEVENT);
  654.     // All commands require an action to the list, and
  655.     // A message sent back to the sender... UserAddress
  656.     // UserAddress is the following... Parameter (if supplied and is address),
  657.     //                                 From Address, From Route Address.
  658.     UserAddress := Parameter;
  659.     if UserAddress = '' then UserAddress := FFromAddress;
  660.     if UserAddress = '' then UserAddress := FSenderAddress;
  661.     Route := TMessageRouteInformation.Create(mrte_From);
  662.     if Route.ParseRoute(UserAddress) = 0 then begin
  663.       if Command = 'SUBSCRIBE' then begin  // I wanna subscribe
  664.         UserAddress := Parameter;
  665.         if UserAddress = '' then UserAddress := FFromAddress;
  666.         if UserAddress = '' then UserAddress := FSenderAddress;
  667.         if UserAddress <> '' then begin
  668.           if FMailList.LB_AllowPublicSubscription then begin
  669.             // Add to Pending Member List
  670.             MagicNumber := FMailList.PendingMember_NewMagicNumber;
  671.             ExpirationDate := Now + 1;
  672.             FMailList.PendingMemberAdd(ExpirationDate, MagicNumber,
  673.                                        UserAddress);
  674.             if FMailList.SaveToFile(FListName, False) then begin
  675.               StatusUpdate('Accepted, Needs confirmation. ' +
  676.                            'Notification sent.', STAT_PROCESSINGEVENT);
  677.               BuildMessageToUser('Subscribe Success', UserAddress,
  678.                                  'FM', MagicNumber, ExpirationDate);
  679.               MailToProcess := True;
  680.            end else begin
  681.               StatusUpdate('List not accessible at this time. ' +
  682.                            'Notification sent.', STAT_PROCESSINGERROR);
  683.               BuildMessageToUser('Subscribe Failure', UserAddress,
  684.                                  'List temporarily inaccessible',
  685.                                  MagicNumber, ExpirationDate);
  686.               MailToProcess := True;
  687.             end;
  688.           end else begin
  689.             StatusUpdate('List not open to public subscription. ' +
  690.                          'Notification sent.', STAT_PROCESSINGERROR);
  691.             BuildMessageToUser('Subscribe Failure', UserAddress,
  692.                                'List is closed to public subscription',
  693.                                MagicNumber, ExpirationDate);
  694.             MailToProcess := True;
  695.           end;
  696.         end else begin
  697.           StatusUpdate('No EMail address supplied. ' +
  698.                        'Notification cannot be sent.', STAT_PROCESSINGERROR);
  699.         end;
  700.       end;
  701.     end;
  702.     Route.Free;
  703.     if Command = 'CONFIRM SUBSCRIBE' then begin  // they confirm sub
  704.       UserAddress := '';
  705.       if UserAddress = '' then UserAddress := FFromAddress;
  706.       if UserAddress = '' then UserAddress := FSenderAddress;
  707.       if UserAddress <> '' then begin
  708.         try  // read magic number
  709.           MagicNumber := StrToInt(Parameter);
  710.         except
  711.           on E: Exception do MagicNumber := -1;
  712.         end;
  713.         if MagicNumber > -1 then begin
  714.           // Find Pending member by magic number
  715.           PendingMember
  716.           := FMailList.PendingMember_FindByMagicNumber(MagicNumber);
  717.           if PendingMember <> nil then begin
  718.             if PendingMember.ExpirationDate >= Now then begin
  719.               // email match?  do I need to check this?  Should I?
  720.               if LowerCase(PendingMember.EMail) =
  721.                  LowerCase(UserAddress) then begin
  722.                 // Add to Members list
  723.                 FMailList.MemberAdd(True, False, PendingMember.EMail);
  724.                 if FMailList.SaveToFile(FListName, False) then begin
  725.                   FMailList.PendingMemberDelete(PendingMember);
  726.                   FMailList.SaveToFile(FListName, False);
  727.                   // they're now on the list...
  728.                   StatusUpdate('Subscribed. ' + 'Notification sent.',
  729.                                STAT_PROCESSINGEVENT);
  730.                   BuildMessageToUser('Subscribe Confirm Success',
  731.                                      PendingMember.EMail,
  732.                                      'FM', MagicNumber, ExpirationDate);
  733.                   MailToProcess := True;
  734.                 end else begin
  735.                   StatusUpdate('List not accessible at this time. ' +
  736.                                'Notification sent.', STAT_PROCESSINGERROR);
  737.                   BuildMessageToUser('Subscribe Confirm Failure',
  738.                                      UserAddress, 'Unable to Subscribe - ' +
  739.                                      'List temporarily inaccessible',
  740.                                      MagicNumber, ExpirationDate);
  741.                   MailToProcess := True;
  742.                 end;
  743.               end else begin
  744.                 StatusUpdate('The sender''s email address does not match! ' +
  745.                              'Notification sent.', STAT_PROCESSINGERROR);
  746.                 BuildMessageToUser('Subscribe Confirm Failure',
  747.                                    UserAddress, 'Your address (' +
  748.                                    UserAddress + ') does ' +
  749.                                    'not match the pending member''s address.',
  750.                                    MagicNumber, ExpirationDate);
  751.                 MailToProcess := True;
  752.               end;
  753.             end else begin
  754.               StatusUpdate('Subscriber ID has expired. ' +
  755.                            'Notification sent.', STAT_PROCESSINGERROR);
  756.               BuildMessageToUser('Subscribe Confirm Failure',
  757.                                  UserAddress,
  758.                                  'Your Subscriber ID number has expired',
  759.                                  MagicNumber, ExpirationDate);
  760.               MailToProcess := True;
  761.               // Remove expired entry
  762.               FMailList.PendingMemberDelete(PendingMember);
  763.               FMailList.SaveToFile(FListName, False);
  764.             end;
  765.           end else begin
  766.             StatusUpdate('Subscriber ID not found in pending. ' +
  767.                          'Notification sent.', STAT_PROCESSINGERROR);
  768.             BuildMessageToUser('Subscribe Confirm Failure', UserAddress,
  769.                                'I couldn''t find your Subscriber ID number ('
  770.                                + IntToStr(MagicNumber) + ')',
  771.                                MagicNumber, ExpirationDate);
  772.             MailToProcess := True;
  773.           end;
  774.         end else begin
  775.           StatusUpdate('Subscriber ID missing. ' +
  776.                        'Notification sent.', STAT_PROCESSINGERROR);
  777.           BuildMessageToUser('Subscribe Confirm Failure',
  778.                              UserAddress,
  779.                              'You didn''t supply a subscriber ID number',
  780.                              MagicNumber, ExpirationDate);
  781.           MailToProcess := True;
  782.         end;
  783.       end else begin
  784.         StatusUpdate('No EMail address supplied. ' +
  785.                      'Notification cannot be sent.', STAT_PROCESSINGERROR);
  786.       end;
  787.     end else
  788.     if Command = 'UNSUBSCRIBE' then begin
  789.       // remove me from this list
  790.       UserAddress := ''; // Parameter;  NO Address parameter in unsubscribe...
  791.       if UserAddress = '' then UserAddress := FFromAddress;
  792.       if UserAddress = '' then UserAddress := FSenderAddress;
  793.       if UserAddress <> '' then begin
  794.         Deleted := False;
  795.         for x := FMailList.MemberCount -1 downto 0 do begin
  796.           Member := FMailList.Members[x];
  797.           // Should I be case sensitive here?
  798.           if LowerCase(Member.EMail) = LowerCase(UserAddress) then begin
  799.             FMailList.MemberDelete(x);
  800.             Deleted := True;
  801.           end;
  802.         end;
  803.         if Deleted and FMailList.SaveToFile(FListName, False) then begin
  804.           // Send them the farewell mail
  805.           StatusUpdate('Member removed from list. ' +
  806.                        'Notification sent.', STAT_PROCESSINGEVENT);
  807.           BuildMessageToUser('Unsubscribe Success', UserAddress,
  808.                              'FM', MagicNumber, ExpirationDate);
  809.           MailToProcess := True;
  810.         end else begin
  811.           if not Deleted then begin
  812.             StatusUpdate('Member not deleted. ' +
  813.                          'Notification sent.', STAT_PROCESSINGERROR);
  814.             BuildMessageToUser('Unsubscribe Failure', UserAddress,
  815.                                'Unable to Unsubscribe - ' +
  816.                                'Member not found for delete.',
  817.                                MagicNumber, ExpirationDate);
  818.           end else begin
  819.             StatusUpdate('List not accessible at this time. ' +
  820.                          'Notification sent.', STAT_PROCESSINGERROR);
  821.             BuildMessageToUser('Unsubscribe Failure', UserAddress,
  822.                                'Unable to Unsubscribe - ' +
  823.                                'List temporarily inaccessible',
  824.                                MagicNumber, ExpirationDate);
  825.           end;
  826.           MailToProcess := True;
  827.         end;
  828.       end else begin
  829.         StatusUpdate('No EMail address supplied. ' +
  830.                      'Notification cannot be sent.', STAT_PROCESSINGERROR);
  831.       end;
  832.     end else
  833.     if Command = 'LIST' then begin  // mail me a list of the members of list
  834.       UserAddress := '';
  835.       if UserAddress = '' then UserAddress := FFromAddress;
  836.       if UserAddress = '' then UserAddress := FSenderAddress;
  837.       if UserAddress <> '' then begin
  838.         if FMailList.LB_DoNotReportListMembers then begin
  839.           StatusUpdate('User requested restricted list membership. ' +
  840.                        'Response sent.', STAT_PROCESSINGEVENT);
  841.           BuildMessageToUser('List Failure', UserAddress, 'FM', 0, Now);
  842.           MailToProcess := True;
  843.         end else begin
  844.           StatusUpdate('User requested list membership. ' +
  845.                        'Response sent.', STAT_PROCESSINGEVENT);
  846.           BuildMessageToUser('List Success', UserAddress, 'FM', 0, Now);
  847.           MailToProcess := True;
  848.         end;
  849.       end else begin
  850.         StatusUpdate('No EMail address supplied. ' +
  851.                      'List cannot be sent.', STAT_PROCESSINGERROR);
  852.       end;
  853.     end else
  854.     if Command = 'HELP' then begin  // I want some help!
  855.       UserAddress := '';
  856.       if UserAddress = '' then UserAddress := FFromAddress;
  857.       if UserAddress = '' then UserAddress := FSenderAddress;
  858.       if UserAddress <> '' then begin
  859.         StatusUpdate('User requested "Help". ' +
  860.                      'Response sent.', STAT_PROCESSINGEVENT);
  861.         BuildMessageToUser('Help', UserAddress, 'FM', 0, Now);
  862.         MailToProcess := True;
  863.       end else begin
  864.         StatusUpdate('No EMail address supplied. ' +
  865.                      'Help cannot be sent.', STAT_PROCESSINGERROR);
  866.       end;
  867.     end else
  868.     begin
  869.       // Command unknown... better treat it as a message
  870.       StatusUpdate('List Command unknown... treating as mail to list.',
  871.                    STAT_PROCESSINGERROR);
  872.       MailForList := True;
  873.     end;
  874.   end else MailForList := True;
  875.   if MailForList then begin
  876.     // Can we accept the submission?
  877.     Accept := True;
  878.     UserAddress := '';
  879.     if UserAddress = '' then UserAddress := FFromAddress;
  880.     if UserAddress = '' then UserAddress := FSenderAddress;
  881.     if FMailList.LB_MemberSubmissionOnly then begin
  882.       Accept := False;
  883.       // Is this UserAddress a list member ?
  884.       for x := 0 to FMailList.MemberCount -1 do begin
  885.         Member := FMailList.Members[x];
  886.         if LowerCase(Member.EMail) = LowerCase(UserAddress) then Accept := True;
  887.       end;
  888.     end;
  889.     if Accept then begin
  890.       MailToProcess := False;
  891.       // Edit the Reply-to if "Force replies to the List"
  892.       if FMailList.LB_ForceRepliesToList then begin
  893.         StatusUpdate('Forcing reply to list.', STAT_PROCESSINGEVENT);
  894.         FMessageInfo.SetHeader_ReplyTo(FListRoute);
  895.       end;
  896.       if FMailList.ArchiveFile <> '' then begin
  897.         StatusUpdate('Adding message to List Archive', STAT_PROCESSINGEVENT);
  898.         AddMessageToArchive;
  899.       end;
  900.       // Messages mailed to list members must be returned (on non-deliverable)
  901.       // to somebody in charge of the list... either the list's "MailErrorsTo"
  902.       // or the "listmaster" of the server
  903.       if FMailList.ErrorsMailedTo = '' then
  904.         FMessageInfo.ReverseRoute.ParseRoute('<' + FormatedAddress('listmaster',
  905.                                              INI.ServerName) + '>')
  906.       else
  907.         FMessageInfo.ReverseRoute.ParseRoute('<' + FMailList.ErrorsMailedTo +
  908.                                              '>');
  909.       DeliverMail := TDeliverMail.Create(FMessageInfo);
  910.       DeliverMail.OnStatusUpdate := FOnStatusUpdate;
  911.       DeliverMail.Deliver;
  912.       DeliverMail.Free;
  913.     end else begin
  914.       // we don't allow non-member submission to the list!
  915.       StatusUpdate('User cannot submit to the list. ' +
  916.                    'They are not a member.', STAT_PROCESSINGERROR);
  917.       BuildMessageToUser('Submission Failure', UserAddress, 'FM', 0, Now);
  918.       MailToProcess := True;
  919.     end;
  920.   end;
  921.   if MailToProcess then begin
  922.     DeliverMail := TDeliverMail.Create(FMessageInfo);
  923.     DeliverMail.OnStatusUpdate := FOnStatusUpdate;
  924.     DeliverMail.Deliver;
  925.     DeliverMail.Free;
  926.   end;
  927.   StatusUpdate('Destination Mailing List Processed', STAT_PROCESSINGEVENT);
  928. end;
  929. procedure TListServer.ParseSubject(Subject : String;
  930.                                    var Command : String;
  931.                                    var Parameter : String);
  932. begin
  933.   // We have a subject, we want to make sure it's a valid list command,
  934.   // and if so, break it into it's command and parameter constituents
  935.   Command := '';
  936.   Parameter := '';
  937.   // All commands to a list must start with a bang (!)
  938.   if Copy(Subject, 1, 1) = '!' then begin
  939.     Subject := Copy(Subject, 2, Length(Subject));
  940.     if UpperCase(Copy(Subject, 1, 9)) = 'SUBSCRIBE' then begin
  941.       Command := 'SUBSCRIBE';
  942.       Parameter := Trim(Copy(Subject, 10, Length(Subject)));
  943.     end else
  944.     if UpperCase(Copy(Subject, 1, 17)) = 'CONFIRM SUBSCRIBE' then begin
  945.       Command := 'CONFIRM SUBSCRIBE';
  946.       Parameter := Trim(Copy(Subject, 18, Length(Subject)));
  947.     end else
  948.     if UpperCase(Copy(Subject, 1, 11)) = 'UNSUBSCRIBE' then begin
  949.       Command := 'UNSUBSCRIBE';
  950.       Parameter := Trim(Copy(Subject, 12, Length(Subject)));
  951.     end else
  952.     if UpperCase(Copy(Subject, 1, 6)) = 'REMOVE' then begin
  953.       Command := 'UNSUBSCRIBE';
  954.       Parameter := Trim(Copy(Subject, 7, Length(Subject)));
  955.     end else
  956.     if UpperCase(Copy(Subject, 1, 4)) = 'LIST' then begin
  957.       Command := 'LIST';
  958.       Parameter := Trim(Copy(Subject, 5, Length(Subject)));
  959.     end else
  960.     if UpperCase(Copy(Subject, 1, 4)) = 'HELP' then begin
  961.       Command := 'HELP';
  962.       Parameter := Trim(Copy(Subject, 5, Length(Subject)));
  963.     end else begin
  964.       Command := '';
  965.       Parameter := '';
  966.     end;
  967.   end;
  968. end;
  969. procedure TListServer.BuildMessageToUser(MessageType, SenderAddress,
  970.                                          FailureMessage : String;
  971.                                          MagicNumber : Longint;
  972.                                          ExpirationDate : TDateTime);
  973. var
  974.   x : Longint;
  975.   Route : TMessageRouteInformation;
  976.   SenderRoute : String;  // Routes are <"x"@[y.z]>
  977.   Member : PMailListMemberInfoRec;
  978. begin
  979.   // Generate the Sender's Route from the address
  980.   Route := TMessageRouteInformation.Create(mrte_Unknown);
  981.   SenderRoute := SenderAddress;
  982.   if Route.ParseRoute(SenderAddress) = 0 then SenderRoute := Route.BuildRoute;
  983.   Route.Free;
  984.   // Messages from the listserver don't have a return path.
  985.   // That's so routing failures are not sent back to us here.
  986.   FMessageInfo.ReverseRoute.ParseRoute('<>'); // no return path
  987.   FMessageInfo.ClearForwardRoutes;
  988.   FMessageInfo.AddForwardRoute(SenderRoute);  // send to sender.
  989.   FMessageInfo.Data.Clear;                    // we'll fill in message below
  990.   MessageType := UpperCase(MessageType);
  991.   if MessageType = 'SUBMISSION FAILURE' then begin
  992.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Submission Failure');
  993.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  994.     FMessageInfo.Data.Add('From: ' + FListRoute);
  995.     FMessageInfo.Data.Add('');
  996.     FMessageInfo.Data.Add('The specified list either does not exist at this ');
  997.     FMessageInfo.Data.Add('server, or is not open to public submission.');
  998.   end else
  999.   if MessageType = 'SUBSCRIBE SUCCESS' then begin
  1000.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Subscription');
  1001.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1002.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1003.     FMessageInfo.Data.Add('');
  1004.     FMessageInfo.Data.Add('You (' + SenderAddress + ') have been subscribed ' +
  1005.                           'to the');
  1006.     FMessageInfo.Data.Add('"' + FListName + '" mailing list at ' +
  1007.                           INI.ServerName + '.');
  1008.     FMessageInfo.Data.Add('');
  1009.     FMessageInfo.Data.Add('To really join the list, you must send mail to: ' +
  1010.                           FListAddress);
  1011.     FMessageInfo.Data.Add('With a subject of: !Confirm Subscribe ' +
  1012.                           IntToStr(MagicNumber));
  1013.     FMessageInfo.Data.Add('By ' + TimeToStr(ExpirationDate) + ' on ' +
  1014.                           DateToStr(ExpirationDate));
  1015.     FMessageInfo.Data.Add('');
  1016.     FMessageInfo.Data.Add('In other words, to join the list, reply to this');
  1017.     FMessageInfo.Data.Add('mail and paste the following line into your e-mail');
  1018.     FMessageInfo.Data.Add('subject before you send it:');
  1019.     FMessageInfo.Data.Add('!Confirm Subscribe ' + IntToStr(MagicNumber));
  1020.     FMessageInfo.Data.Add('');
  1021.     FMessageInfo.Data.Add('If you don''t want to subscribe, do nothing.');
  1022.   end else
  1023.   if MessageType = 'SUBSCRIBE FAILURE' then begin
  1024.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Subscription');
  1025.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1026.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1027.     FMessageInfo.Data.Add('');
  1028.     FMessageInfo.Data.Add('Your subscription to ' + FListAddress +
  1029.                           ' request failed because:');
  1030.     FMessageInfo.Data.Add(FailureMessage);
  1031.   end else
  1032.   if MessageType = 'SUBSCRIBE CONFIRM SUCCESS' then begin
  1033.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Subscription Successful');
  1034.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1035.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1036.     FMessageInfo.Data.Add('');
  1037.     FMessageInfo.Data.Add('To remove yourself, reply with a subject of ' +
  1038.                           '"!Remove" (no quotes)');
  1039.     FMessageInfo.Data.Add('');
  1040.     for x := 0 to FMailList.SL_Welcome.Count -1 do
  1041.       FMessageInfo.Data.Add(FMailList.SL_Welcome[x]);
  1042.   end else
  1043.   if MessageType = 'SUBSCRIBE CONFIRM FAILURE' then begin
  1044.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Subscription');
  1045.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1046.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1047.     FMessageInfo.Data.Add('');
  1048.     FMessageInfo.Data.Add('Your subscription confirmation to ' + FListAddress +
  1049.                           ' failed because:');
  1050.     FMessageInfo.Data.Add(FailureMessage);
  1051.   end else
  1052.   if MessageType = 'UNSUBSCRIBE SUCCESS' then begin
  1053.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Removal Successful');
  1054.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1055.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1056.     FMessageInfo.Data.Add('');
  1057.     for x := 0 to FMailList.SL_Farewell.Count -1 do
  1058.       FMessageInfo.Data.Add(FMailList.SL_Farewell[x]);
  1059.   end else
  1060.   if MessageType = 'UNSUBSCRIBE FAILURE' then begin
  1061.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Removal');
  1062.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1063.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1064.     FMessageInfo.Data.Add('');
  1065.     FMessageInfo.Data.Add('Your removal from ' + FListAddress +
  1066.                           ' failed because:');
  1067.     FMessageInfo.Data.Add(FailureMessage);
  1068.   end else
  1069.   if MessageType = 'LIST SUCCESS' then begin
  1070.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Membership Listing');
  1071.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1072.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1073.     FMessageInfo.Data.Add('');
  1074.     FMessageInfo.Data.Add('Members of "' + FListName + '" (' +
  1075.                           FListAddress +')');
  1076.     FMessageInfo.Data.Add('');
  1077.     for x := 0 to FMailList.MemberCount -1 do begin
  1078.       Member := FMailList.Members[x];
  1079.       if (Member.Active) and (not Member.Hidden) then
  1080.         FMessageInfo.Data.Add(Member.EMail);
  1081.     end;
  1082.   end else
  1083.   if MessageType = 'LIST FAILURE' then begin
  1084.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Membership Listing');
  1085.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1086.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1087.     FMessageInfo.Data.Add('');
  1088.     FMessageInfo.Data.Add('Membership of "' + FListName + '" (' +
  1089.                           FListAddress +') is restricted.');
  1090.   end else
  1091.   if MessageType = 'HELP' then begin
  1092.     FMessageInfo.Data.Add('Subject: ' + FListName + ' Help');
  1093.     FMessageInfo.Data.Add('To: ' + SenderRoute);
  1094.     FMessageInfo.Data.Add('From: ' + FListRoute);
  1095.     FMessageInfo.Data.Add('');
  1096.     FMessageInfo.Data.Add('Hermes SMTP/POP3 Server Mail List Help');
  1097.     FMessageInfo.Data.Add('');
  1098.     FMessageInfo.Data.Add('Send list commands in the subject of your email.');
  1099.     FMessageInfo.Data.Add('The following commands are valid:');
  1100.     FMessageInfo.Data.Add('  Subscribe, Confirm Subscribe,');
  1101.     FMessageInfo.Data.Add('  Unsubscribe, Remove, List, Help');
  1102.     FMessageInfo.Data.Add('');
  1103.     FMessageInfo.Data.Add('Command Syntax:');
  1104.     FMessageInfo.Data.Add('');
  1105.     FMessageInfo.Data.Add('  !Subscribe [Address]');
  1106.     FMessageInfo.Data.Add('  !Confirm Subscribe [Number]');
  1107.     FMessageInfo.Data.Add('  !Unsubscribe');
  1108.     FMessageInfo.Data.Add('  !Remove');
  1109.     FMessageInfo.Data.Add('  !List');
  1110.     FMessageInfo.Data.Add('  !Help');
  1111.     FMessageInfo.Data.Add('');
  1112.     FMessageInfo.Data.Add('Replace [Address] with your full e-mail address.');
  1113.     FMessageInfo.Data.Add('Replace [Number] with the number you were sent ' +
  1114.                           'in the subscription reply.');
  1115.     FMessageInfo.Data.Add('');
  1116.   end;
  1117. end;
  1118. (******************************************************************************)
  1119. (*                                                                            *)
  1120. (* STOP  ListServer Object                                                    *)
  1121. (*                                                                            *)
  1122. (******************************************************************************)
  1123. (******************************************************************************)
  1124. (*                                                                            *)
  1125. (* START Deliver Mail Object                                                  *)
  1126. (*                                                                            *)
  1127. (* This object is used to route mail locally.  It's employed by the           *)
  1128. (* Smtp Server to determine if mail should be processed as a message to a     *)
  1129. (* list, delivered locally, or queued for processing by the Smtp Agent.       *)
  1130. (* ALL mail bound for non-local users must be queued and processed by the     *)
  1131. (*                                                                            *)
  1132. (******************************************************************************)
  1133. constructor TDeliverMail.Create(MessageInfo : TSmtpMessageInformation);
  1134. begin
  1135.   inherited Create;
  1136.   // FMessageInfo was created somewhere else and will be freed by whatever
  1137.   // created it.  We are using this as a link to that object
  1138.   FMessageInfo := MessageInfo;
  1139.   // don't call Deliver here... then we couldn't set statusupdate
  1140. end;
  1141. procedure TDeliverMail.StatusUpdate(Status : String; Level : Integer);
  1142. begin
  1143.   if Assigned(FOnStatusUpdate) then OnStatusUpdate(Self, Status, Level);
  1144. end;
  1145. destructor TDeliverMail.Destroy;
  1146. begin
  1147.   // Do NOT free FMessageInfo... it was not created here, and will be freed
  1148.   // by whatever created it!
  1149.   inherited Destroy;
  1150. end;
  1151. procedure TDeliverMail.ProcessAlias(var UserName : String; Domain : String);
  1152. // Accepts a user name... determines if it's an alias and returns the real
  1153. // user name...
  1154. var
  1155.   MailBox, AliasID, AliasUser : String;
  1156. begin
  1157.   // First, try to fine an Alias qualified with a domain...
  1158.   Mailbox := UserName + '@' + Domain;
  1159.   if INI.Alias_Exists(MailBox) then begin  // The Mailbox is an alias
  1160.     UserName := INI.Alias_Find(MailBox);   // The alias is...
  1161.     INI.Alias_Parse(UserName, AliasID, AliasUser);  // we seperate it to get
  1162.                                                     // the user ID
  1163.     UserName := AliasUser;                 // and return the user ID here
  1164.     StatusUpdate('Destination (' + Mailbox + ') is Alias for ' + UserName,
  1165.                  STAT_PROCESSINGEVENT);
  1166.   end else begin
  1167.     // If we could not find a fully qualified Alias,
  1168.     // then let's look for an unqualified one...
  1169.     MailBox := UserName;
  1170.     if INI.Alias_Exists(MailBox) then begin  // The Mailbox is an alias
  1171.       UserName := INI.Alias_Find(MailBox);   // The alias is...
  1172.       INI.Alias_Parse(UserName, AliasID, AliasUser);  // we seperate it to get
  1173.                                                       // the user ID
  1174.       UserName := AliasUser;                 // and return the user ID here
  1175.       StatusUpdate('Destination (' + Mailbox + ') is Alias for ' + UserName,
  1176.                    STAT_PROCESSINGEVENT);
  1177.     end;
  1178.   end;
  1179. end;
  1180. procedure TDeliverMail.DeliverLocally(UserInfo : TPop3UserInformation);
  1181. // Deliver a copy of the message to a local user.
  1182. var
  1183.   x : Longint;
  1184.   SL : TStringList;  // Copy of the mail data for the user (has specail
  1185.                      // data individual to each user (return path)
  1186. begin
  1187.   if Assigned(UserInfo) then begin
  1188.     SL := TStringList.Create;
  1189.     // Add the Return Route
  1190.     StatusUpdate('Adding Return Path to Message Header', STAT_PROCESSINGEVENT);
  1191.     SL.Add('Return-Path: ' + FMessageInfo.ReverseRoute.BuildRoute);
  1192.     // Add the remaining mail data
  1193.     for x := 0 to FMessageInfo.Data.Count -1 do SL.Add(FMessageInfo.Data[x]);
  1194.     // Save the mail to the user
  1195.     StatusUpdate('Saving Message to User', STAT_PROCESSINGEVENT);
  1196.     UserInfo.SaveMail(SL);
  1197.     SL.Free;
  1198.   end;
  1199. end;
  1200. procedure TDeliverMail.Deliver;
  1201. var
  1202.   MailBox : String;
  1203.   ToRouteIndex : Longint;                  // The Index to the ToRouteArray
  1204.                                            // we are currently trying to deliver
  1205.   ToRouteInfo : TMessageRouteInformation;  // The ToRoute we are currently
  1206.                                            // trying to deliver to...
  1207.   UserInfo : TPop3UserInformation;         // If ToRoute is a local user, we'll
  1208.                                            // need to know about them to make
  1209.                                            // the delivery
  1210.   ListServ : TListServer;                  // If ToRoute proves to be a list,
  1211.                                            // we'll need a ListServer object
  1212.                                            // for further processing
  1213. begin
  1214.   StatusUpdate('Processing Destination Route(s)', STAT_PROCESSINGEVENT);
  1215.   ToRouteIndex := 0;
  1216.   while ToRouteIndex < FMessageInfo.ForwardRouteCount do begin
  1217.     ToRouteInfo := FMessageInfo.ForwardRoute[ToRouteIndex];
  1218.     // Question, should we short-circuit the routing if we're the
  1219.     // Destination host, and there are additional hosts in the forward path ?
  1220.     // I'll say yes.  Let's hope this is cool.
  1221.     if INI.Domain_IsThisOneOfMine(ToRouteInfo.Domain) then begin
  1222.       // This belongs to me, let's see if we can find a user, alias or list
  1223.       // for it...
  1224.       StatusUpdate('Destination Route is Local', STAT_PROCESSINGEVENT);
  1225.       MailBox := ToRouteInfo.Mailbox;
  1226.       ProcessAlias(MailBox, ToRouteInfo.Domain);  // if alias, get real user mailbox
  1227.       if INI.User_Exists(MailBox) then begin
  1228.         StatusUpdate('Destination is User', STAT_PROCESSINGEVENT);
  1229.         // We need to deliver the message to the user...
  1230.         UserInfo := TPop3UserInformation.Create;
  1231.         UserInfo.LoadFromFile(MailBox);
  1232.         if UserInfo.ForwardToAddress = '' then begin
  1233.           // The local user has no forward... we can deliver to the local user
  1234.           DeliverLocally(UserInfo);
  1235.           // Remove this To line, it's been successfully processed
  1236.           StatusUpdate('Removing Destination Route. Delivered.',
  1237.                        STAT_PROCESSINGEVENT);
  1238.           FMessageInfo.DeleteForwardRoute(ToRouteIndex);
  1239.         end else begin
  1240.           // the local user has a forward.
  1241.           // we need to process it just like it were the original
  1242.           StatusUpdate('Forwarding...', STAT_PROCESSINGEVENT);
  1243.           // replace the to route with the new (forward) one
  1244.           if ToRouteInfo.ParseRoute(UserInfo.ForwardToAddress) = 0 then begin
  1245.             // I'm trying the forward address...
  1246.             // Here I re-start delivery process again.
  1247.             // I could have made this a function I call, but then I'm
  1248.             // affraid of the possibility of recursive calling...
  1249.             // for example... user A forwards to B who forwards back to A
  1250.             // For that reason, I'll only process one forward here.
  1251.             // is it local (domain), is it alias? is it user?
  1252.             if INI.Domain_IsThisOneOfMine(ToRouteInfo.Domain) then begin
  1253.               StatusUpdate('Destination Route is Local', STAT_PROCESSINGEVENT);
  1254.               MailBox := ToRouteInfo.Mailbox;
  1255.               ProcessAlias(MailBox, ToRouteInfo.Domain);  // if alias, get real user mailbox
  1256.               if INI.User_Exists(MailBox) then begin
  1257.                 StatusUpdate('Destination is User', STAT_PROCESSINGEVENT);
  1258.                 // We need to deliver the message to the user...
  1259.                 UserInfo := TPop3UserInformation.Create;
  1260.                 // we won't check for forward or mail list...
  1261.                 // we're not allowing user to forward indefinately or
  1262.                 // forward to a list.
  1263.                 DeliverLocally(UserInfo);
  1264.                 // Remove this To line, it's been successfully processed
  1265.                 StatusUpdate('Removing Destination Route. Delivered.',
  1266.                              STAT_PROCESSINGEVENT);
  1267.                 FMessageInfo.DeleteForwardRoute(ToRouteIndex);
  1268.               end else begin
  1269.                 StatusUpdate('Destination Route Local, but no user ' +
  1270.                              'available... Deleting ' + ToRouteInfo.BuildRoute,
  1271.                              STAT_PROCESSINGERROR);
  1272.                 FMessageInfo.DeleteForwardRoute(ToRouteIndex);
  1273.               end;
  1274.             end else begin
  1275.               StatusUpdate('Destination Route Not Local... ' +
  1276.                            'Queueing for Agent...', STAT_PROCESSINGEVENT);
  1277.               Inc(ToRouteIndex);
  1278.             end;
  1279.           end else begin
  1280.             // Unable to forward... route is bad... better deliver locally
  1281.             StatusUpdate('Unable to understand forward address... ' +
  1282.                          'Delivered Locally.', STAT_PROCESSINGERROR);
  1283.             DeliverLocally(UserInfo);
  1284.             // Remove this To line, it's been successfully processed
  1285.             StatusUpdate('Removing Destination Route. Delivered.',
  1286.                          STAT_PROCESSINGEVENT);
  1287.             FMessageInfo.DeleteForwardRoute(ToRouteIndex);
  1288.           end;
  1289.         end;
  1290.         UserInfo.Free;
  1291.       end else
  1292.       if INI.List_Exists(ToRouteInfo.Mailbox) then begin
  1293.         StatusUpdate('Destination Route is a Mailing List',
  1294.                      STAT_PROCESSINGEVENT);
  1295.         // It's a mailing list that requires specail processing,
  1296.         // including exploding the list and generating messages
  1297.         ListServ := TListServer.Create(ToRouteInfo.Mailbox,
  1298.                                        FMessageInfo.ReverseRoute.BuildRoute,
  1299.                                        FMessageInfo.Data);
  1300.         ListServ.OnStatusUpdate := FOnStatusUpdate;
  1301.         ListServ.Process;
  1302.         ListServ.Free;
  1303.         // Remove this To line, it's been successfully processed
  1304.         StatusUpdate('Removing Destination Route. Delivered.',
  1305.                      STAT_PROCESSINGEVENT);
  1306.         FMessageInfo.DeleteForwardRoute(ToRouteIndex);
  1307.       end else
  1308.       begin
  1309.         // This is supposed to be local, but I don't have anywhere to deliver
  1310.         // it.  I checked for this in the RCPT, but I must have messed up to
  1311.         // get here.
  1312.         // I guess I'll have to generate a "Failure Notification" and queue
  1313.         // that for Agent processing which will send that back to the sender
  1314.         SendUndeliverableReply(ToRouteInfo.BuildRoute);
  1315.         StatusUpdate('Destination Route Local, but no user available... ' +
  1316.                      'Generating "Undeliverable" Notification',
  1317.                      STAT_PROCESSINGERROR);
  1318.         FMessageInfo.DeleteForwardRoute(ToRouteIndex);
  1319.       end;
  1320.     end else begin
  1321.       // foreign domain... don't do anything more here
  1322.       // The SMTP Agent will try to send this on to the next host...
  1323.       // just move on to the next To Route
  1324.       StatusUpdate('Destination Route Not Local... queueing for Agent...',
  1325.                    STAT_PROCESSINGEVENT);
  1326.       Inc(ToRouteIndex);
  1327.     end;
  1328.   end;
  1329.   // Save remaining routing info and message (if there is any)
  1330.   if FMessageInfo.ForwardRouteCount > 0 then begin
  1331.     FMessageInfo.SaveToFile; // written to FQueuePath as XXX.ini and XXX.txt
  1332.     // Notify the Server (Main Form) that we've added
  1333.     // a message to the Agent Queue and that it needs attention
  1334.     if INI.Agent_ServiceQueueImmediately then Trigger_ServiceSMTPQueue;
  1335.     StatusUpdate(IntToStr(FMessageInfo.ForwardRouteCount) +
  1336.                  ' Destination Route(s) Queued for Agent Processing',
  1337.                  STAT_PROCESSINGEVENT);
  1338.   end;
  1339.   StatusUpdate('Incoming Mail Processed', STAT_PROCESSINGEVENT);
  1340. end;
  1341. procedure TDeliverMail.SendUndeliverableReply(DestinationPath : String);
  1342. var
  1343.   MessageInfo : TSmtpMessageInformation;
  1344.   Route : TMessageRouteInformation;
  1345.   x : Longint;
  1346. begin
  1347.   MessageInfo := TSmtpMessageInformation.Create;
  1348.   // Set the routes...
  1349.   MessageInfo.ReverseRoute.ParseRoute('<>');  // No return for failure notice
  1350.   MessageInfo.AddForwardRoute(FMessageInfo.ReverseRoute.BuildRoute);
  1351.   // copy the data in...
  1352.   MessageInfo.Data_AppendLine('From: Hermes Server ' + INI.ServerName);
  1353.   MessageInfo.Data_AppendLine('Subject: Undeliverable Mail');
  1354.   MessageInfo.Data_AppendLine('');
  1355.   MessageInfo.Data_AppendLine('The following recepients were not reached:');
  1356.   MessageInfo.Data_AppendLine('');
  1357.   MessageInfo.Data_AppendLine(DestinationPath);
  1358.   MessageInfo.Data_AppendLine('');
  1359.   MessageInfo.Data_AppendLine('so the following message ' +
  1360.                               'could not be delivered.');
  1361.   MessageInfo.Data_AppendLine('');
  1362.   MessageInfo.Data_AppendLine('');
  1363.   for x := 0 to FMessageInfo.Data.Count -1 do
  1364.     MessageInfo.Data_AppendLine(FMessageInfo.Data[x]);
  1365.   // if the forward route is valid, queue for sending
  1366.   if MessageInfo.ForwardRouteCount > 0 then begin
  1367.     Route := MessageInfo.ForwardRoute[0];
  1368.     if Route.BuildRoute <> '<>' then MessageInfo.SaveToFile;
  1369.   end;
  1370.   MessageInfo.Free;
  1371. end;
  1372. (******************************************************************************)
  1373. (*                                                                            *)
  1374. (* STOP  Deliver Mail Object                                                  *)
  1375. (*                                                                            *)
  1376. (******************************************************************************)
  1377. end.