diff options
Diffstat (limited to 'test/test_milter_package.adb')
| -rw-r--r-- | test/test_milter_package.adb | 294 | 
1 files changed, 294 insertions, 0 deletions
| diff --git a/test/test_milter_package.adb b/test/test_milter_package.adb new file mode 100644 index 0000000..fddb5e7 --- /dev/null +++ b/test/test_milter_package.adb @@ -0,0 +1,294 @@ +-- The Ada Milter API test milter +-- Copyright 2012 - 2013 B. Persson, Bjorn@Rombobeorn.se +-- +-- This program is free software: you can redistribute it and/or modify it +-- under the terms of the GNU General Public License version 3, as published +-- by the Free Software Foundation. + + +with Ada.Strings.Fixed; use Ada.Strings.Fixed; +with Milter_API; use Milter_API; +with Berkeley_Exit_Codes; +with System_Log; use System_Log; +with Ada.Unchecked_Deallocation; +with Interfaces.C; +with Ada.Exceptions; +with Ada.Directories; +with Ada.Text_IO; +with GNAT.OS_Lib; + +package body Test_Milter_Package is + + +   type Test_Action is (None, Test_Reject, Test_Discard, Test_Fail_Temporarily); + + +   type Message_Data is limited new Milter_Data with record +      Test_Message : Boolean; +      Action       : Test_Action; +   end record; +   type Message_Data_Pointer is access all Message_Data; +   procedure Free is new Ada.Unchecked_Deallocation(Message_Data, +                                                    Message_Data_Pointer); + +   Socket_Obstructed : exception; + + +   function Private_Data(Context : SMFICTX_Pointer) return Message_Data_Pointer +   is +   begin +      return Message_Data_Pointer(Milter_API.Private_Data(Context)); +   end Private_Data; + + +   function Handle_Connection +      (Context        : SMFICTX_Pointer; +       Client_Name    : String; +       Client_Address : Sockaddr) +      return Action +   is +      Local_Client : Boolean := False; +      -- Allocate a message data record for this SMTP session. +      Data : constant Message_Data_Pointer := new Message_Data; +   begin +      Log(Debug, "Handle_Connection"); +      if Milter_API.Private_Data(Context) /= null then +         Log(Warning, +             "The private data pointer isn't null in Handle_Connection. " & +             "Memory is probably leaking."); +      end if; +      -- Remember the pointer to the message data record. +      Set_Private_Data(Context, Milter_Data_Pointer(Data)); +      declare +         use type Interfaces.Unsigned_8; +         Addr : constant IP_Address := Address(Client_Address); +      begin +         case Addr.Family is +            when IPv4 => +               Local_Client := Addr.IPv4_Address(1) = 127; +            when IPv6 => +               Local_Client := Addr.IPv6_Address = (1..15 => 0, 16 => 1); +         end case; +      exception +         when No_Address => +            Log(Warning, "The MTA didn't provide the client's IP address."); +         when Unknown_Address_Type => +            Log(Error, "The client address is of an unknown type."); +      end; +      Log(Debug, +          "client address: " & Address(Client_Address) & +          ", local client: " & Boolean'Image(Local_Client)); +      if Local_Client then +         return Continue; +      else +         -- The test milter won't touch messages from this connection. +         return Accept_Definitely; +      end if; +   end Handle_Connection; + + +   function Handle_Helo +      (Context     : SMFICTX_Pointer; +       Stated_Name : String) +      return Action +   is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Helo"); +      return Continue; +   end Handle_Helo; + + +   function Handle_Sender +      (Context   : SMFICTX_Pointer; +       Sender    : String; +       Arguments : Arguments_Handle) +      return Action +   is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Sender"); +      -- Initialize the message data record, or clear it of data from the +      -- previous message in the SMTP session. +      Data.Test_Message := False; +      Data.Action := None; +      return Continue; +   end Handle_Sender; + + +   function Handle_Recipient +      (Context   : SMFICTX_Pointer; +       Recipient : String; +       Arguments : Arguments_Handle) +      return Action +   is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Recipient " & Recipient); +      if Index(Recipient, "Ada_Milter_API_test_milter") /= 0 then +         Data.Test_Message := True; +      end if; +      return Continue; +   end Handle_Recipient; + + +   function Handle_Data(Context : SMFICTX_Pointer) return Action is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Data"); +      if Data.Test_Message then +         return Continue; +      else +         -- This message is not intended for the test milter. +         return Accept_Definitely; +      end if; +   end Handle_Data; + + +   function Handle_Unknown_Command +      (Context : SMFICTX_Pointer; +       Command : String) +      return Action +   is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Unknown_Command"); +      return Continue; +   end Handle_Unknown_Command; + + +   function Handle_Header +      (Context : SMFICTX_Pointer; +       Name    : String; +       Value   : String) +      return Action +   is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Header " & Name); +      return Continue; +   end Handle_Header; + + +   function Handle_End_Of_Headers(Context : SMFICTX_Pointer) return Action is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_End_Of_Headers"); +      return Continue; +   end Handle_End_Of_Headers; + + +   function Handle_Body +      (Context    : SMFICTX_Pointer; +       Body_Chunk : String) +      return Action +   is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Body"); +      return Continue; +   end Handle_Body; + + +   function Handle_End_Of_Message(Context : SMFICTX_Pointer) return Action is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_End_Of_Message"); +      return Reject; +   end Handle_End_Of_Message; + + +   procedure Handle_Abort(Context : SMFICTX_Pointer) is +      Data : constant Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Abort"); +   end Handle_Abort; + + +   procedure Handle_Close(Context : SMFICTX_Pointer) is +      Data : Message_Data_Pointer := Private_Data(Context); +   begin +      Log(Debug, "Handle_Close"); +      -- Deallocate the message data record. +      Free(Data); +      Set_Private_Data(Context, null); +   end Handle_Close; + + +   procedure Clean_And_Set_Socket is +      Socket_Name : constant String := "/var/spool/test_milter/milter_socket"; +      function umask(mask : Interfaces.C.unsigned) return Interfaces.C.unsigned; +      pragma import(C, umask); +      mask : Interfaces.C.unsigned;  -- dummy to soak up the result from umask +      pragma Unreferenced(mask); +   begin +      -- Delete the socket file if it exists, assuming it was left behind +      -- because of a crash. +      if Ada.Directories.Exists(Socket_Name) then +         Log(Warning, Socket_Name & " exists. Deleting it."); +         -- GNAT's implementation of Ada.Directories.Delete_File calls a +         -- function named Is_Regular_File and refuses to delete a socket file, +         -- so GNAT.OS_Lib.Delete_File must be used instead. +         -- http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56055 +         declare +            OK : Boolean; +         begin +            GNAT.OS_Lib.Delete_File(Socket_Name, OK); +            if not OK then +               raise Socket_Obstructed with +                  Socket_Name & " can't be created because a file with that " & +                  "name exists and can't be deleted."; +            end if; +         end; +      end if; +      -- Clear the permissions mask to allow the MTA to use the socket. +      mask := umask(0); +      -- Tell the milter library where to create the socket. +      Set_Socket("unix:" & Socket_Name); +   end Clean_And_Set_Socket; + + +   function Run return Ada.Command_Line.Exit_Status is +      use Ada.Exceptions; +      use Berkeley_Exit_Codes; +      use Ada.Text_IO; +   begin +      Log(Info, +          "Starting. Milter API version " & Milter_API.Binding_Version_String & +          ", Libmilter version " & Milter_API.Libmilter_Version_String); +      Clean_And_Set_Socket; +      Register(Name                         => "test_milter/libmilter", +               Connected                    => Handle_Connection'Access, +               Helo                         => Handle_Helo'Access, +               Sender                       => Handle_Sender'Access, +               Recipient                    => Handle_Recipient'Access, +               Data                         => Handle_Data'Access, +               Unknown_Command              => Handle_Unknown_Command'Access, +               Header                       => Handle_Header'Access, +               End_Of_Headers               => Handle_End_Of_Headers'Access, +               Body_Chunk                   => Handle_Body'Access, +               End_Of_Message               => Handle_End_Of_Message'Access, +               Aborted                      => Handle_Abort'Access, +               Closed                       => Handle_Close'Access); +      Milter_API.Main; +      return Ada.Command_Line.Success; +   exception +      when E : Milter_API.Failure => +         Log(Error, Exception_Message(E)); +         return Ada.Command_Line.Failure; +      when E : Milter_API.Unknown_Error => +         Log(Error, Exception_Message(E)); +         return Software_Error; +      when E : Socket_Obstructed => +         Log(Error, Exception_Message(E)); +         return Cannot_Create_File; +      when E : others => +         Put_Line(Standard_Error, Exception_Information(E)); +         Log(Error, +             "Unexpected error: " & Exception_Name(E) & ": " & +             Exception_Message(E)); +         return Software_Error; +   end Run; + + +end Test_Milter_Package; |