File : adagio-g2-listener.adb


------------------------------------------------------------------------------

--                         ADAGIO - ADALID - AENEA.                         --

--                                                                          --

--                            Copyright (C) 2003                            --

--                                 A. Mosteo.                               --

--                                                                          --

--  Authors: A. Mosteo. (adagio@mosteo.com)                                 --

--                                                                          --

--  If you have any questions in regard to this software, please address    --

--  them to the above email.                                                --

--                                                                          --

--  This program is free software; you can redistribute it and/or modify    --

--  it under the terms of the GNU General Public License as published by    --

--  the Free Software Foundation; either version 2 of the License, or (at   --

--  your option) any later version.                                         --

--                                                                          --

--  This program is distributed in the hope that it will be useful, but     --

--  WITHOUT ANY WARRANTY; without even the implied warranty of              --

--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       --

--  General Public License for more details.                                --

--                                                                          --

--  You should have received a copy of the GNU General Public License       --

--  along with this library; if not, write to the Free Software Foundation, --

--  Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.          --

--                                                                          --

--  You are not allowed to use any part of this code to develop a program   --

--  whose output would be used to harass or prosecute other users of the    --

--  networks Adagio connects with. All data collected with Adagio or a tool --

--  containing Adagio code about other network users must remain            --

--  confidential and cannot be made public by any mean, nor be used to      --

--  harass or legally prosecute these users.                                --

------------------------------------------------------------------------------

--  $Id: adagio-g2-listener.adb,v 1.12 2004/01/21 21:05:26 Jano Exp $


-- G2 listeners: TCP and UDP


with Adagio.G2.Packet;
with Adagio.G2.Packet.Parsing;
with Adagio.G2.Upload_client;
with Adagio.Globals;
with Adagio.Globals.Options;
with Adagio.Routing;
with Adagio.Socket;
with Adagio.Trace;
with Adagio.Upload.Queue.Manager;

with Gnat.Sockets;   use Gnat;

package body Adagio.G2.Listener is

   procedure Start(this: in out Object; Port: Natural) is
   begin
      -- Start accepter:

      this.Accepter := new Accepter_task;

      -- Start listener:

      this.Listener := new Listener_task;
      this.Listener.Start(this'Unrestricted_access, Port);
   end Start;

   -- Retrieve the UDP connected endpoint:

   function Get_udp (this : in Object) return Socket.Object_access is
   begin
      return this.Udp'Unrestricted_Access;
   end Get_udp;

   -- Shutdown

   procedure Shutdown(this: in out Object) is
   begin
      this.Listener.Shutdown;
   end Shutdown;

   -------------------

   -- Listener Task --

   -------------------


   task body Listener_task is
      Listening_port:   Natural;
      Exit_requested:   Boolean:= false;
      Parent:           Object_access;

      -- Connect

      procedure Connect is
      begin
         -- Tcp listener:

         Socket.Create_stream (Parent.Tcp);
         Socket.Bind          (Parent.Tcp, "0.0.0.0", Listening_port);
         Socket.Listen        (Parent.Tcp, 15);
         Parent.Accepter.Accept_socket (Parent, Parent.Tcp);
         -- Udp listener:

         Socket.Create_datagram (Parent.Udp);
         Socket.Bind            (Parent.Udp, "0.0.0.0", Listening_port);
      end Connect;

      -- Disconnect

      procedure Disconnect is
      begin
         begin
            Socket.Shutdown (Parent.Tcp);
            Socket.Close    (Parent.Tcp);
         exception
            when E : others =>
               Trace.Log ("G2.Listener: " & Trace.Report (E), Trace.Debug);
         end;
         begin
            Socket.Shutdown (Parent.Udp);
            Socket.Close    (Parent.Udp);
         exception
            when E : others =>
               Trace.Log ("G2.Listener: " & Trace.Report (E), Trace.Debug);
         end;
      end Disconnect;

   begin
      select
         accept Start(this: in Object_access; Port: in Natural) do
            Trace.Log ("Listening at" & Natural'Image (Port));
            Listening_port:= Port;
            Parent:= this;
            -- Establish connections

            Connect;
         end Start;
      or
         terminate;
      end select;
      loop
         begin
            select
               -- Exit signaled by a superior being:

               accept Shutdown do
                  Exit_requested:= true;
               end;
            or
               -- Add a new client to our pipes:

               -- However, we pass it directly to the queue manager.

               accept Get_client(S: in Socket.Object) do
                  Upload.Queue.Manager.Object.Enqueue (
                     G2.Upload_client.Create (S));
               end;
            or
               -- Well earned sleep:

               delay 60.0;
            end select;

            exit when Exit_requested;
         exception
            when E: others =>
               Trace.Log("G2.Listener_task [loop]: " & Trace.Report(E),
                  Trace.Error);
         end;
      end loop;
      Disconnect;
      Trace.Log("G2.Listener_task exited", Trace.Informative);
   exception
      when E: Others =>
         Trace.Log("G2.Listener_task [body]: " & Trace.Report(E),
            Trace.Error);
   end Listener_task;

   -------------------

   -- Accepter Task --

   -------------------


   task body Accepter_task is
      Conn:       Socket.Object;
      New_client: Socket.Object;
      Parent:     Object_access;
   begin
      select
         accept Accept_Socket
           (this: in Object_access; S: in Socket.Object) do
            Conn:= S;
            Parent:= this;
         end Accept_socket;
      or
         terminate;
      end select;
      Main: loop
         declare
            Proceed : Boolean;
         begin
            -- Wait for incomings:

            begin
               Proceed := false;
               Socket.Accept_socket(Conn, New_client);
               Proceed := true;
            exception
               when E : Socket.Socket_error =>
                  exit Main when Globals.Requested_exit;
                  Trace.Log ("G2.Listener [Accept]: " & Trace.Report (E),
                     Trace.Error);
               when Socket.Security_ban =>
                  Proceed := false;
                  Trace.Log ("G2.Listener: Rejected incoming connection " &
                     "[security ban]", Trace.Informative);
            end;

            if Proceed then
               if Routing.TCP_Reachable (
                     Socket.Image (Socket.Get_peer_name (New_client))) then
                  -- Pass it to listener:

                  Parent.Listener.Get_client(New_client);
                  -- Remove it:

               else
                  Socket.Close (New_client);
                  Trace.Log ("G2.Listener: Closing unreachable connection.");
               end if;
            end if;

            exit when Globals.Requested_exit;
            delay 1.0;
         exception
            when E: others =>
               Trace.Log("G2.Accepter_task [loop]: " & Trace.Report(E),
                  Trace.Error);
         end;
      end loop Main;
      Socket.Close (Conn);
      Trace.Log("G2.Accepter_task exited", Trace.Informative);
   end Accepter_task;

end Adagio.G2.Listener;