-- Topal: GPG/GnuPG and Alpine/Pine integration
-- Copyright (C) 2001--2022  Phillip J. Brooke
--
-- 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.
--
-- 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 program.  If not, see <http://www.gnu.org/licenses/>.

with Ada.Containers.Vectors;
with Ada.IO_Exceptions;
with Ada.Strings.Fixed;
with Ada.Strings.Maps.Constants;
with Ada.Text_IO;
with Externals;                  use Externals;
with Externals.GPG;
with Externals.Mail;
with Externals.Ops;
with Externals.Simple;           use Externals.Simple;
with Globals;                    use Globals;
with Menus;                      use Menus;
with Misc;                       use Misc;
with Readline;
with Remote_Mode;

package body Receiving is
   
   -- A local subprogram common to both Display and Mime_Display to sort
   -- out whether to process the block, and whether to use cache and how.
   procedure Process_Check (Decrypt       : in     Boolean;
			    SMIME         : in     Boolean;
                            Cached        : in     Boolean;
                            Process_Block :    out Boolean;
                            Use_Cache     :    out Boolean;
                            Write_Cache   :    out Boolean;
                            Remote_Block  :    out Boolean) is
      YNR : YNR_Index;
      Decrypt_Possible : Boolean := True;
   begin
      Debug("+Receiving.Process_Check");
      Process_Block := False;
      Use_Cache := False;
      Write_Cache := False;
      Remote_Block := False;
      if Decrypt then
         -- Decrypt?
         if Cached then
            if Config.Positive_Opts(Decrypt_Cached) = 1 then
               Ada.Text_IO.Put_Line("Cache file exists, using it instead of decrypting");
               Process_Block := True;
            elsif Config.Positive_Opts(Decrypt_Cached) = 2 then
               YNR := YNR_Menu("Cache file exists; decrypt? (yes/no/remote) ");
               Process_Block := YNR = Yes or YNR = Remote;
               Remote_Block := YNR = Remote;
            end if;
            if Process_Block then
               if Config.Positive_Opts(Decrypt_Cached_Use_Cache) = 1 then
                  Ada.Text_IO.Put_Line("Using existing cache file");
                  Use_Cache := True;
               elsif Config.Positive_Opts(Decrypt_Cached_Use_Cache) = 2 then
                  Ada.Text_IO.Put_Line("Replacing existing cache file");
                  Write_Cache := True;
               elsif Config.Positive_Opts(Decrypt_Cached_Use_Cache) = 3 then
                  Ada.Text_IO.Put_Line("Replacing existing cache file");
                  Write_Cache := YN_Menu("Replace existing cache file? ") = Yes;
                  -- Config.Positive_Opts(Decrypt_Cached_Use_Cache) = 4: Change nothing.
               elsif Config.Positive_Opts(Decrypt_Cached_Use_Cache) = 5 then
                  declare
                     Selection : URI_Index;
                  begin
                     Selection := URI_Menu;
                     if Selection = UUse then
                        Use_Cache := True;
                     elsif Selection = Replace then
                        Write_Cache := True;
                     end if;
                  end;
               end if;
            end if;
         else
            -- It's not cached.  Do we decrypt it?
            -- First check if decrypt-prereq is set.
            if ToStr(Config.UBS_Opts(Decrypt_Prereq)) /= "" then
               -- Run the prereq, collect the return value.  If it
               --  returns 0, good.  Anything, else, set the
               --  Decrypt_Possible flag to false.
               if Externals.Ops.Test_Decrypt_Prerequisite > 0 then
                  Decrypt_Possible := False;
               end if;
            end if;
	    -- FIXME: the next bit is a shortcircuit for SMIME.
	    if SMIME then
	       Decrypt_Possible := True;
	    end if;
            -- Perhaps ask the user what to do.
            if Decrypt_Possible then
               if Config.Positive_Opts(Decrypt_Not_Cached) = 1 then
                  Ada.Text_IO.Put_Line("No cache file exists, decrypting");
                  Process_Block := True;
               elsif Config.Positive_Opts(Decrypt_Not_Cached) = 2 then
                  YNR := YNR_Menu("No cache file exists; decrypt? (yes/no/remote) ");
                  Process_Block := YNR = Yes or YNR = Remote;
                  Remote_Block := YNR = Remote;
               end if;
            else
               Ada.Text_IO.Put_Line("Decrypt prerequisite false, not decrypting.");
            end if;
            -- If we're processing the block, sort out the cache usage.
            if Process_Block then
               if Config.Positive_Opts(Decrypt_Not_Cached_Use_Cache) = 1 then
                  Ada.Text_IO.Put_Line("Will write cache file");
                  Write_Cache := True;
               elsif Config.Positive_Opts(Decrypt_Not_Cached_Use_Cache) = 2 then
                  Write_Cache := YN_Menu("Write cache file? ") = Yes;
               end if;
            end if;
         end if;
      else
         -- Verify?
         if Cached then
            if Config.Positive_Opts(Verify_Cached) = 1 then
      Ada.Text_IO.Put_Line("Cache file exists, using it instead of verifying");
               Process_Block := True;
            elsif Config.Positive_Opts(Verify_Cached) = 2 then
               Process_Block := YN_Menu("Cache file exists; verify? ") = Yes;
            end if;
            if Process_Block then
               if Config.Positive_Opts(Verify_Cached_Use_Cache) = 1 then
                  Ada.Text_IO.Put_Line("Using existing cache file");
                  Use_Cache := True;
               elsif Config.Positive_Opts(Verify_Cached_Use_Cache) = 2 then
                  Ada.Text_IO.Put_Line("Replacing existing cache file");
                  Write_Cache := True;
               elsif Config.Positive_Opts(Verify_Cached_Use_Cache) = 3 then
                  Ada.Text_IO.Put_Line("Replacing existing cache file");
                  Write_Cache := YN_Menu("Replace existing cache file? ") = Yes;
                  -- Config.Positive_Opts(Verify_Cached_Use_Cache) = 4: Change nothing.
               elsif Config.Positive_Opts(Verify_Cached_Use_Cache) = 5 then
                  declare
                     Selection : URI_Index;
                  begin
                     Selection := URI_Menu;
                     if Selection = UUse then
                        Use_Cache := True;
                     elsif Selection = Replace then
                        Write_Cache := True;
                     end if;
                  end;
               end if;
            end if;
         else
            if Config.Positive_Opts(Verify_Not_Cached) = 1 then
               Ada.Text_IO.Put_Line("No cache file exists, verifying");
               Process_Block := True;
            elsif Config.Positive_Opts(Verify_Not_Cached) = 2 then
               Process_Block := YN_Menu("No cache file exists; verify? ") = Yes;
            end if;
            if Process_Block then
               if Config.Positive_Opts(Verify_Not_Cached_Use_Cache) = 1 then
                  Ada.Text_IO.Put_Line("Will write cache file");
                  Write_Cache := True;
               elsif Config.Positive_Opts(Verify_Not_Cached_Use_Cache) = 2 then
                  Write_Cache := YN_Menu("Write cache file? ") = Yes;
               end if;
            end if;
         end if;
      end if;
      Debug("-Receiving.Process_Check");
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Receiving.Process_Check");
         raise;
   end Process_Check;

   type Block_Record is
      record
         PGP  : Boolean := False;
         Decrypt : Boolean := False;
      end record;

   package BAP
   is new Ada.Containers.Vectors (Index_Type   => Positive,
				  Element_Type => Block_Record);
   subtype BAV is BAP.Vector;

   -- We want to verify or decrypt a message.
   procedure Display (Tmpfile : in String) is
      Cache_Dir : constant UBS := ToUBS(ToStr(Topal_Directory)
                                          & "/cache");
      Originfile     : constant String := Temp_File_Name("origin");
      -- We are going to break the message up into a sequence of blocks.
      -- Each block has a record associated with it.  Of the three fields,
      -- Used indicates if that block has, funnily enough, been used. PGP
      -- is True if we're going to send it to GPG (either decrypt or
      -- verify).  Decrypt True if the message starts -----BEGIN PGP
      -- MESSAGE----- (i.e., we need to decrypt it) and is False if the
      -- message starts -----BEGIN PGP SIGNED MESSAGE----- (i.e., we won't
      -- need a passphrase).

      Blocks : BAV;

      -- Flag indicating if we should wait at the end.
      Do_Wait : Boolean := False;
   begin
      Debug("+Receiving.Display");
      -- Save the original input.
      Externals.Simple.Cat_Out(Tmpfile, Originfile);
      -- Initialize Blocks.
      Blocks := BAP.Empty_Vector;
      -- Make sure that the cache directory exists.
      Mkdir_P(ToStr(Cache_Dir));
      -- Turn the received message in Tmpfile into blocks.
      -- Block names are of the form Temp_File_Name("blocknnn").
      declare
         use Ada.Text_IO;
         -- The handle for the tmpfile:
         TF : File_Type;
         -- The handle for the current block:
         BF : File_Type;
         -- The state machine is fairly noddy.
         type State_Machine_Modes is (Starting, Verbatim, Message, Signed);
         State : State_Machine_Modes := Starting;
         -- The strings we're interested in....
         Message_Start : constant String := "-----BEGIN PGP MESSAGE-----";
         Signed_Start  : constant String := "-----BEGIN PGP SIGNED MESSAGE-----";
         Message_End   : constant String := "-----END PGP MESSAGE-----";
         Signed_End    : constant String := "-----END PGP SIGNATURE-----";
      begin
         -- Open the file.  Let exceptions here propogate.
         begin
            Open(File => TF,
                 Mode => In_File,
                 Name => Tmpfile);
         exception
            when others =>
               Error("Exception generated while opening input file "
                     & "for receive.");
               raise;
         end;
     Tmp_Read_Loop:
         loop
            begin
               declare
                  The_Line : constant String := ToStr(Unbounded_Get_Line(TF));

                  function Block_Name return String is
                  begin
                     return Temp_File_Name("block"
                                           & Trim_Leading_Spaces(Integer'Image(Integer(Blocks.Length)+1)),
                                           Use_Sequence_Number => False);
                  exception
                     when others =>
                        Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                                             "Exception raised in Receiving.Display.Block_Name");
                        raise;
                  end Block_Name;

                  procedure Set_BR (PGP     : Boolean := False;
                                    Decrypt : Boolean := False) is
                  begin
                     Blocks.Append(Block_Record'(PGP     => PGP,
						 Decrypt => Decrypt));
                  exception
                     when others =>
                        Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                                             "Exception raised in Receiving.Display.Set_BR");
                        raise;
                  end Set_BR;

               begin
                  case State is
                     when Starting =>
                        if The_Line'Length >= Message_Start'Length
                          and then The_Line(The_Line'First..
                                    The_Line'First+Message_Start'Length-1)
                                     = Message_Start then
                           -- This line starts a PGP message.
                           -- Open a new block.
                           begin
                              Create(File => BF,
                                     Mode => Out_File,
                                     Name => Block_Name);
                           exception
                              when others =>
                                 Error("Exception generated when opening block"
                                       & " to write 1: `" & Block_Name & "'");
                           end;
                           -- Write this line.
                           Put_Line(BF, The_Line);
                           -- Set the arrays and state appropriately.
                           Set_BR(PGP     => True,
                                  Decrypt => True);
                           State := Message;
                        elsif The_Line'Length >= Signed_Start'Length
                          and then The_Line(The_Line'First..
                                    The_Line'First+Signed_Start'Length-1)
                                     = Signed_Start then
                           -- This line starts a PGP signed message.
                           -- Open a new block.
                           begin
                              Create(File => BF,
                                     Mode => Out_File,
                                     Name => Block_Name);
                           exception
                              when others =>
                                 Error("Exception generated when opening block"
                                       & " to write 2: `" & Block_Name & "'");
                           end;
                           -- Write this line.
                           Put_Line(BF, The_Line);
                           -- Set the arrays and state appropriately.
                           Set_BR(PGP  => True);
                           State := Signed;
                        else
                           -- This line is verbatim.
                           -- Open a new block.
                           begin
                              Create(File => BF,
                                     Mode => Out_File,
                                     Name => Block_Name);
                           exception
                              when others =>
                                 Error("Exception generated when opening block"
                                       & " to write 3: `" & Block_Name & "'");
                           end;
                           -- Write this line.
                           Put_Line(BF, The_Line);
                           -- Set the arrays and state appropriately.
                           Set_BR;
                           State := Verbatim;
                        end if;
                     when Verbatim =>
                        if The_Line'Length >= Message_Start'Length
                          and then The_Line(The_Line'First..
                                    The_Line'First+Message_Start'Length-1)
                                     = Message_Start then
                           -- This line starts a PGP message.
                           -- Close the existing block.
                           Close(BF);
                           -- Open a new block.
                           begin
                              Create(File => BF,
                                     Mode => Out_File,
                                     Name => Block_Name);
                           exception
                              when others =>
                                 Error("Exception generated when opening block"
                                       & " to write 4: `" & Block_Name & "'");
                           end;
                           -- Write this line.
                           Put_Line(BF, The_Line);
                           -- Set the arrays and state appropriately.
                           Set_BR(PGP     => True,
                                  Decrypt => True);
                           State := Message;
                        elsif The_Line'Length >= Signed_Start'Length
                          and then The_Line(The_Line'First..
                                    The_Line'First+Signed_Start'Length-1)
                                     = Signed_Start then
                           -- This line starts a PGP signed message.
                           -- Close the existing block.
                           Close(BF);
                           -- Open a new block.
                           begin
                              Create(File => BF,
                                     Mode => Out_File,
                                     Name => Block_Name);
                           exception
                              when others =>
                                 Error("Exception generated when opening block"
                                       & " to write 5: `" & Block_Name & "'");
                           end;
                           -- Write this line.
                           Put_Line(BF, The_Line);
                           -- Set the arrays and state appropriately.
                           Set_BR(PGP  => True);
                           State := Signed;
                        else
                           -- This line is verbatim.
                           -- Write it in the current block.
                           -- Write this line.
                           Put_Line(BF, The_Line);
                        end if;
                     when Message =>
                        if The_Line'Length >= Message_End'Length
                          and then The_Line(The_Line'First..
                                    The_Line'First+Message_End'Length-1)
                                     = Message_End then
                           -- This line ends the current PGP message.
                           -- Write this line.
                           Put_Line(BF, The_Line);
                           -- Close the existing block.
                           Close(BF);
                           -- Set the arrays and state appropriately.
                           State := Starting;
                        else
                           -- Write this into the current block.
                           Put_Line(BF, The_Line);
                        end if;
                     when Signed =>
                        if The_Line'Length >= Signed_End'Length
                          and then The_Line(The_Line'First..
                                    The_Line'First+Signed_End'Length-1)
                                     = Signed_End then
                           -- This line ends the current PGP message.
                           -- Write this line.
                           Put_Line(BF, The_Line);
                           -- Close the existing block.
                           Close(BF);
                           -- Set the arrays and state appropriately.
                           State := Starting;
                        else
                           -- Write this into the current block.
                           Put_Line(BF, The_Line);
                        end if;
                  end case;
               end;
            exception
               when Ada.IO_Exceptions.End_Error =>
                  -- Run out of lines to read.  We're done.
                  exit Tmp_Read_Loop;
            end;
         end loop Tmp_Read_Loop;
         -- Tidy up -- close the existing files.
         if Is_Open(BF) then
            Close(BF);
         end if;
         if Is_Open(TF) then
            Close(TF);
         end if;
      exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Receiving.Display (Tmpfile -> Blocks)");
         raise;
      end; -- declare: The Tmpfile -> Blocks bit.

      -- Delete the Tmpfile.  We're going to append to it instead.
      Rm_File(Tmpfile);

      -- Now, run through the entire vector.
  Block_Use_Loop:
      for I in 1 .. Integer(Blocks.Length) loop
         if Blocks.Element(I).PGP then
            declare
               Process_Block   : Boolean;
               Cached          : Boolean;
               Cached2         : Boolean;
               Write_Cache     : Boolean;
               Use_Cache       : Boolean;
               Remote_Block    : Boolean;
               This_Block_Wait : Boolean := True;
               -- Get temp filenames.
               Com_File   : constant String := Temp_File_Name("com"
                            & Trim_Leading_Spaces(Integer'Image(I)));
               Out_File   : constant String := Temp_File_Name("out"
                            & Trim_Leading_Spaces(Integer'Image(I)));
               Err_File   : constant String := Temp_File_Name("err"
                            & Trim_Leading_Spaces(Integer'Image(I)));
               SFD_File   : constant String := Temp_File_Name("sfd"
                            & Trim_Leading_Spaces(Integer'Image(I)));
               Blk_File   : constant String := Temp_File_Name("blk"
                            & Trim_Leading_Spaces(Integer'Image(I)));
               -- We'll need to read in the cache line.
               MD5        : UBS;
            begin
               -- Is this file cached?
               -- Get the cache filename.
               MD5 := Ops.Get_MD5Sum_Of_File(Temp_File_Name("block"
                                                            & Trim_Leading_Spaces(Integer'Image(I)),
                                                            Use_Sequence_Number => False));
               -- So the cache filename is ToStr(Cache_Dir) & "/" & ToStr(MD5).
               -- Does it exist?
               Cached := Test_R(ToStr(Cache_Dir) & "/" & ToStr(MD5));
               Cached2 := Test_R(ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out");
               if Cached and (not Cached2) then
                  Ada.Text_IO.Put_Line("Hmm. Missing .out file from cache....");
               end if;
               -- At this point, we know whether or not the file is cached
               -- (Cached) and also if this is a decrypt or verify
               -- (Block_Decrypt(I)).  Now check what we need to do in
               -- terms of Process_Block, Use_Cache, Write_Cache and Remote_Block.
               Process_Check(Blocks.Element(I).Decrypt,
			     False, -- Not SMIME.
                             Cached,
                             Process_Block,
                             Use_Cache,
                             Write_Cache,
                             Remote_Block);
               -- Now we know if we're going to process the block, and
               -- whether or not we'll read from a cache, or write to a
               -- cache.
               if Remote_Block then
                  -- Copy this particular block over to the remote
                  --  server.  We know it's a decrypt, because that's
                  --  the only time we set Remote_Block true.
                  Remote_Mode.Decrypt(Temp_File_Name("block"
                                                     & Trim_Leading_Spaces(Integer'Image(I)),
                                                     Use_Sequence_Number => False),
                                      False);
                  -- Write some text noting that this has been handled remotely.
                  Echo_Append("----- Topal: Remote decryption used here. -----",
                              Tmpfile);
               elsif Process_Block then
                  -- Run GPG or get stuff from cache..
                  if Use_Cache then
                     -- It's cached, copy the cache file.
                     -- Append the cache file to the tmpfile.
                     Echo_Append("----- Topal: Using cache file `"
                                 & ToStr(Cache_Dir) & "/" & ToStr(MD5)
                                 & "'-----",
                                 Com_File);
                     Cat_Append(ToStr(Cache_Dir) & "/" & ToStr(MD5), Com_File);
                     if Config.Boolean_Opts(Inline_Separate_Output) then
                        Pager(Com_File);
                        if Cached2 then
                           Cat_Append(ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out",
                                      Tmpfile);
                        else
                           Error("Need .out!  Have you cleared the cache since upgrading to Topal >=0.7.8??");
                        end if;
                     else
                        Cat_Append(Com_File, Tmpfile);
                        if Blocks.Element(I).Decrypt then
                           Echo_Append("----- Topal: Decrypted output starts -----", Tmpfile);
                        if Cached2 then
                           Cat_Append(ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out",
                                      Tmpfile);
                        else
                           Error("Need .out!  Have you cleared the cache since upgrading to Topal >=0.7.8?");
                        end if;
                           Echo_Append("----- Topal: Decrypted output ends -----", Tmpfile);
                        else
                           Echo_Append("----- Topal: Original message starts -----", Tmpfile);
                           Cat_Append(Temp_File_Name("block"
                                                     & Trim_Leading_Spaces(Integer'Image(I)), Use_Sequence_Number => False), Tmpfile);
                           Echo_Append("----- Topal: Original message ends -----", Tmpfile);
                        end if;

                     end if;
                     -- Sort out the This_Block_Wait.
                     if Blocks.Element(I).Decrypt then
                        This_Block_Wait := not Config.Boolean_Opts(Decrypt_Cached_Fast_Continue);
                     else
                        This_Block_Wait := not Config.Boolean_Opts(Verify_Cached_Fast_Continue);
                     end if;
                  else
                     -- Run GPG!
                     declare
                        GPG_Return_Value : Integer;
			GPG_Return_Okay  : Boolean := False;
                        Include_Out_File : Boolean := True;
                     begin
                        -- Actually run GPG.
                        GPG_Return_Value
                          := Externals.GPG.GPG_Tee(Temp_File_Name("block"
                                                        & Trim_Leading_Spaces(Integer'Image(I)),
                                                        Use_Sequence_Number => False),
                                         Out_File,
					 Err_File,
						   SFD_File);
			if Blocks.Element(I).Decrypt then
			   GPG_Return_Okay := Externals.GPG.Grep_Status(SFD_File,
									"DECRYPTION_OKAY");
			else
			   GPG_Return_Okay := Externals.GPG.Grep_Status(SFD_File,
									"GOODSIG");
			end if;
                        -- Now, according to the GPG man page: ``The
                        --  program returns 0 if everything was fine,
                        --  1 if at least a signature was bad, and
                        --  other error codes for fatal errors.''
                        -- So, we're not bothered here about bad
                        --  signatures (it's up to the user to decide
                        --  what to do), but anything else means that
                        --  it's broken and we can't do much else.
                        -- Exception: if the bad signature is due to a
                        --  duff character set, then if
                        --  config.ask_charset is set, then we'll
                        --  offer a conversion and re-run.
                        if (not GPG_Return_Okay)
			  and Externals.GPG.Grep_Status(SFD_File,
							"BADSIG")
			  and (not Blocks.Element(I).Decrypt)
                          and Config.Boolean_Opts(Ask_Charset) then
                           -- This procedure is dealing with inline
                           --  blocks.  These can get messed up by the
                           --  locales.  If Config.Boolean_Opts(Ask_Charset) is set,
                           --  then find out the current locale, ask
                           --  the user which is should be, and if
                           --  different, run iconv.
                           declare
                              Current_Charset  : UBS;
                              Desired_Charset  : UBS;
                              use type UBS;
                           begin
                              Ada.Text_IO.New_Line(5);
                              Ada.Text_IO.Put_Line("GPG's error code suggests that the signature was bad.  This could be caused");
                              Ada.Text_IO.Put_Line("by using the wrong character set.");
                              Current_Charset := ToUBS(Externals.Simple.Get_Charmap);
                              Ada.Text_IO.Put_Line("The current charset is " & ToStr(Current_Charset) & ".");
                              Readline.Add_History(ToStr(Current_Charset));
                              Desired_Charset := ToUBS(Readline.Get_String("Which charset should we use? (empty means no change) "));
                              -- If no charset is given, don't convert and don't re-run.
                              if ToStr(Desired_Charset)'Length > 0
                                -- But with a non-empty response, convert if they're different.
                                and then Current_Charset /= Desired_Charset then
                                 Externals.Simple.Convert_Charmap(Temp_File_Name("block"
                                                                                 & Trim_Leading_Spaces(Integer'Image(I)), Use_Sequence_Number => False),
                                                                  ToStr(Current_Charset),
                                                                  ToStr(Desired_Charset));
                                 -- Remove the first Out_File.
                                 Externals.Simple.Rm_File(Out_File);
                                 -- Now, re-run GPG.
                                 GPG_Return_Value
                                   := Externals.GPG.GPG_Tee(Temp_File_Name("block"
                                                                 & Trim_Leading_Spaces(Integer'Image(I)), Use_Sequence_Number => False),
                                                  Out_File,
                                                  Err_File,
						  SFD_File);
				 GPG_Return_Okay := Externals.GPG.Grep_Status(SFD_File,
									      "GOODSIG");

                              end if;
                           end;
                        end if;
                        if (not GPG_Return_Okay) then
                           -- Certainly don't cache this thing.
                           Write_Cache := False;
                           -- Omit anything that deals with the output file
                           -- (because it probably doesn't exist).
                           Include_Out_File := False;
                           Ada.Text_IO.Put_Line("Topal: GPG return value > 1; was = "
                                                & Trim_Leading_Spaces(Integer'Image(GPG_Return_Value))
                                                & "; this is not good -- omitting output.");
                        end if;
                        -- Append GPG's stdout, stderr, some wrappers to block
                        -- result file.
                        Echo_Out_N("----- Topal: Output generated on ",
                                   Blk_File);
                        Date_Append(Blk_File);
                        Echo_Append("----- Topal: GPG output starts -----",
                                    Blk_File);
                        Cat_Append(Err_File, Blk_File);
                        Echo_Append("----- Topal: GPG output ends -----",
                                    Blk_File);
                        -- Copy GPG's block result file and the separate
                        -- output to cache files.
                        -- But only if we really want to.
                        if Write_Cache then
                           Cat_Out(Blk_File,
                                   ToStr(Cache_Dir) & "/" & ToStr(MD5));
                           Cat_Out(Out_File,
                                   ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out");
                        end if;
                        if Config.Boolean_Opts(Inline_Separate_Output) then
                           Pager(Blk_File);
                           Cat_Append(Out_File, Tmpfile);
                        else
                           if Blocks.Element(I).Decrypt
                             and Include_Out_File then
                              -- Copy the processed output with wrappers.
                              Echo_Append("----- Topal: Decrypted output starts -----",
                                          Blk_File);
                              Cat_Append(Out_File, Blk_File);
                              Echo_Append("----- Topal: Decrypted output ends -----",
                                          Blk_File);
                           else
                              -- Copy the original file, with wrappers.
                              Echo_Append("----- Topal: Original message starts -----",
                                          Blk_File);
                              Cat_Append(Temp_File_Name("block"
                                                        & Trim_Leading_Spaces(Integer'Image(I)), Use_Sequence_Number => False),
                                         Blk_File);
                              Echo_Append("----- Topal: Original message ends -----",
                                          Blk_File);
                           end if;
                           -- Append block result file to Pine tmpfile..
                           Cat_Append(Blk_File, Tmpfile);
                        end if;
                        -- Sort out the This_Block_Wait.
                        if not Blocks.Element(I).Decrypt then
                           This_Block_Wait
                             := not Config.Boolean_Opts(Verify_Not_Cached_Fast_Continue);
                        end if;
                     end;
                  end if;
               else
                  -- Don't process the block; just append it.
                  Cat_Append(Temp_File_Name("block"
                                            & Trim_Leading_Spaces(Integer'Image(I)),
                                            Use_Sequence_Number => False),
                             Tmpfile);
                  -- Don't wait on non-processed blocks.
                  This_Block_Wait := False;
               end if;
               -- Sort out the wait part.
               Do_Wait := Do_Wait or This_Block_Wait;
            end; -- Of declare block.
         else
            -- Verbatim block; just append it.
            Cat_Append(Temp_File_Name("block"
                                      & Trim_Leading_Spaces(Integer'Image(I)), Use_Sequence_Number => False),
                       Tmpfile);
            -- Verbatim blocks don't affect the Do_Wait setting.
         end if;
      end loop Block_Use_Loop;

      if Do_Wait then
         Debug("Receiving.Display: Waiting...");
         Wait;
      else
         Debug("Receiving.Display: Not waiting...");
      end if;
      Debug("-Receiving.Display");
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Receiving.Display");
         raise;
   end Display;

   procedure Mime_Display (Infile       : in String;
                           Content_Type : in String) is
      Cache_Dir : constant UBS := ToUBS(ToStr(Topal_Directory)
                                          & "/cache");
      Boundary : UBS;
      -- If this subprogram has been called, we expect to be decoding a
      -- MIME RFC2015 message.  This will be split into two parts, possibly
      -- with some junk at the head and tail.  We use the boundary to get
      -- the two parts.

      -- The temp filenames.
      Originfile    : constant String := Temp_File_Name("origin");
      OrigCT        : constant String := Temp_File_Name("origct");
      Part_One      : constant String := Temp_File_Name("part1");
      Part_One_Hdr  : constant String := Temp_File_Name("part1h");
      Part_One_CT   : constant String := Temp_File_Name("part1ct");
      Part_One_CS   : constant String := Temp_File_Name("part1cs");
      Part_Two      : constant String := Temp_File_Name("part2");
      Out_File      : constant String := Temp_File_Name("out");
      Err_File      : constant String := Temp_File_Name("err");
      SFD_File      : constant String := Temp_File_Name("sfd");
      Blk_File      : constant String := Temp_File_Name("blk");
      P7S           : constant String := Temp_File_Name("p7s");
      -- Other similar stuff to the previous subprogram.
      Decrypt       : Boolean;
      Process_Block : Boolean;
      Cached        : Boolean;
      Write_Cache   : Boolean;
      Use_Cache     : Boolean;
      Remote_Block  : Boolean;
      -- We'll need to read in the cache line.
      MD5        : UBS;
      -- Character set horrors.
      Local_Charset   : UBS;
      Guessed_Charset : UBS;
      Desired_Charset : UBS;
      -- Messing with GPG.
      GPG_Return_Value    : Integer;
      GPG_Return_Okay     : Boolean := False;
      Include_Out_File    : Boolean := True;
      SMIME               : Boolean := False;
      SMIME_Opaque        : Boolean := False;
      Reattempt           : Boolean := False;
   begin
      Debug("+Receiving.Mime_Display");
      Debug("Infile=" & Infile);
      Debug("Content_Type=" & Content_Type);
      -- Save the original input and content-type.
      Externals.Simple.Cat_Out(Infile, Originfile);
      Externals.Simple.Echo_Out(Content_Type, OrigCT);
      -- Make sure that the cache directory exists.
      Mkdir_P(ToStr(Cache_Dir));
      -- Is this file cached?
      -- Get the cache filename.
      MD5 := Ops.Get_MD5Sum_Of_File(Infile);
      -- So the cache filename is ToStr(Cache_Dir) & "/" & ToStr(MD5).
      -- Does it exist?
      Cached := Test_R(ToStr(Cache_Dir) & "/" & ToStr(MD5));
      -- Is this a verify or decrypt?
      -- Need to use Ada.Strings.Maps.Constants.Lower_Case_Map
      declare
         CT : constant String := Ada.Strings.Fixed.Translate(Content_Type,
                                    Ada.Strings.Maps.Constants.Lower_Case_Map);
      begin
         if (CT'Length >= 16 and then CT(1..16) =   "multipart/signed")
           or (CT'Length >= 26 and then CT(1..26) = "application/x-topal-signed") then
            Decrypt := False;
         elsif (CT'Length >= 19 and then CT(1..19) = "multipart/encrypted")
           or (CT'Length >= 29 and then CT(1..29) =  "application/x-topal-encrypted") then
            Decrypt := True;
         elsif (CT'Length >= 22 and then CT(1..22) = "application/pkcs7-mime")
           or (CT'Length >= 24 and then CT(1..24) =  "application/x-pkcs7-mime") then
	    SMIME := True;
         else
            -- Have no idea what to do -- bail out.
            Error("Don't know what to do with Content-Type: "
                  & CT);
         end if;
	 if SMIME then
	    if Ada.Strings.Fixed.Index(CT, "signed-data") /= 0 then
	       Decrypt := False;
	       SMIME_Opaque := True;
	    elsif Ada.Strings.Fixed.Index(CT, "enveloped-data") /= 0 then
	       Decrypt := True;
	    else
	       -- Guess that it's encrypted.
	       Decrypt := True;
	       -- FIXME: the standard also allows for certs-only.
	    end if;	       
	 end if;
      end;
      -- We need to figure out what the boundary is and split the message.
      -- But not if we're dealing with an SMIME decryption.
      if not ((Decrypt and SMIME) or SMIME_Opaque) then
	 -- First, figure out what the boundary is.
	 Boundary := Mail.Find_Mime_Boundary(Infile);
	 Debug("Boundary=" & ToStr(Boundary));
	 -- Get the two parts.
	 Mail.Split_Two_Parts(Infile, Part_One, Part_Two, Boundary);
	 -- Now, we have the two parts.  
      end if;
      -- We need to check if it is cached (but we'll ignore this for
      --  now).  So, sort out what to do with this block.
      Process_Check(Decrypt,
		    SMIME,
                    Cached,
                    Process_Block,
                    Use_Cache,
                    Write_Cache,
                    Remote_Block);
      -- And now we either use the cache, or process the block.
      if Remote_Block then
         -- Copy the entire original email over to the remote server.
         --  We know it's a MIME decrypt, because that's the only time
         --  we set Remote_Block true here.
         Remote_Mode.Decrypt(Originfile, True, Content_Type);
      elsif Process_Block then
         if Use_Cache then
            -- It's cached, copy the cache file.
            -- Append the cache file to the tmpfile.
            Echo_Append("----- Topal: Using cache file `"
                        & ToStr(Cache_Dir) & "/" & ToStr(MD5)
                        & "'-----",
                        Blk_File);
            Cat_Append(ToStr(Cache_Dir) & "/" & ToStr(MD5), Blk_File);
            -- This is MIME, so we also retrieve the out file.
            -- But verifying a MIME message means we ignore the outfile....
            if Decrypt then
               Cat_Out(ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out", Out_File);
            else
               -- Verify case: test and warn.
               if Test_R(ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out") then
                  Ada.Text_IO.Put_Line("Hmm. Unexpected .out file in cache....");
                  Cat_Out(ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out",
                          Out_File);
               end if;
            end if;
         else
            if Decrypt then
               -- Just pass the second part to GPG (or GPGSM).
	       if SMIME then
		  -- Guess if we've got a base64 (i.e., PEM without
		  --  header lines) or a binary file, and call GPGSM.
		  GPG_Return_Value := Externals.GPG.GPGSM_Tee(Infile, Out_File, Err_File, Misc.Guess_SMIME_Encoding(Infile), SFD_File, False);
	       else
		  GPG_Return_Value := Externals.GPG.GPG_Tee(Part_Two, Out_File, Err_File, SFD_File);
	       end if;
	       GPG_Return_Okay := Externals.GPG.Grep_Status(SFD_File,
							    "DECRYPTION_OKAY");
	    elsif SMIME_Opaque then
	       -- Caching etc. is stuffed here.  Opaque signing is not a good thing.  So disable caching.
	       Write_Cache := False;
	       GPG_Return_Value := Externals.GPG.GPGSM_Tee(Infile, Out_File, Err_File, Misc.Guess_SMIME_Encoding(Infile), SFD_File, True);
	       GPG_Return_Okay := Externals.GPG.Grep_Status(SFD_File,
							    "GOODSIG");
	       if GPG_Return_Okay then
		  -- Put the output where we expect to find it.
		  Externals.Simple.Mv_F(Out_File, Part_One);
	       end if;
            else
               -- We're doing verification, so we need to sort out the
               --  character sets.
               -- Given the two parts, try and find a header in the first one
               --  and a charset.
               Mail.Extract_Header(Part_One, Part_One_Hdr);
               Mail.Extract_Content_Type_From_Header(Part_One_Hdr, Part_One_CT, Ignore_Missing => True, Substitute => True);
               Simple.Sed_InOut("/charset/I ! d; s/^.*charset=//i; s/[; ].*$//; s/""//g",
                                Part_One_CT, Part_One_CS);
               Guessed_Charset := Read_Fold(Part_One_CS);
               Local_Charset := ToUBS(Externals.Simple.Get_Charmap);
               if ToStr(Guessed_Charset) = "" then
                  Guessed_Charset := Local_Charset;
               end if;
               Debug("Got guessed charset=" & ToStr(Guessed_Charset));
               -- We'll use both parts.  But we have to turn part one into
               -- canonical line-end first.
               Dos2Unix_U(Part_One);
               -- Convert from the current character set to CS.
               declare
                  use type UBS;
               begin
                  if Local_Charset /= Guessed_Charset then
                     Externals.Simple.Convert_Charmap(Part_One,
                                                      ToStr(Local_Charset),
                                                      ToStr(Guessed_Charset));
                  end if;
               end;
               -- Use both parts....
               -- But first: are we looking at a S/MIME message here?
               -- Read the first line.  If it includes the string `
               declare
                  CTF   : constant String := Temp_File_Name("ct");
                  B64   : constant String := Temp_File_Name("b64");
                  CT    : UBS;
               begin
                  Externals.Mail.Extract_Content_Type_From_Header(Part_Two, CTF, Ignore_Missing => True);
                  CT := Read_Fold(CTF);
                  if Ada.Strings.Fixed.Index(ToStr(CT), "application/x-pkcs7-signature") > 0
                    or Ada.Strings.Fixed.Index(ToStr(CT), "application/pkcs7-signature") > 0 then
                     SMIME := True;
                     -- Split part two into two further parts.
                     Externals.Mail.Extract_Body(Part_Two, B64);
                     -- Decode the base 64 attachment.
                     Externals.Mail.Base64_Decode(B64, P7S);
                  end if;
                  if SMIME then
                     GPG_Return_Value := Externals.GPG.GPGSM_Verify_Tee(Input_File  => Part_One,
                                                            Sig_File    => P7S,
                                                            Output_File => Out_File,
                                                            Err_File    => Err_File,
							    Status_Filename => SFD_File);
                  else
                     GPG_Return_Value := Externals.GPG.GPG_Verify_Tee(Input_File  => Part_One,
                                                            Sig_File    => Part_Two,
                                                            Output_File => Out_File,
                                                            Err_File    => Err_File,
							    Status_Filename => SFD_File);
                  end if;
		  GPG_Return_Okay := Externals.GPG.Grep_Status(SFD_File,
							       "GOODSIG");
               end;
            end if;
            -- Now, according to the GPG man page: ``The
            -- program returns 0 if everything was fine, 1 if
            -- at least a signature was bad, and other error
            -- codes for fatal errors.''
            -- So, we're not bothered here about bad signatures
            -- (it's up to the user to decide what to do), but
            -- anything else means that it's broken and we can't
            -- do much else.
            -- Exception: if the bad signature is due to a
            --  duff character set, then if
            --  config.ask_charset is set, then we'll
            --  offer a conversion and re-run.
            if (not GPG_Return_Okay)
	      and Externals.GPG.Grep_Status(SFD_File,
					    "BADSIG")
              and Config.Boolean_Opts(Ask_Charset) then
               -- This procedure is dealing with a MIME input.  If
               --  Config.Boolean_Opts(Ask_Charset) is set, then find out the
               --  current locale, ask the user which is should be,
               --  and if different, run iconv.
               declare
                  use type UBS;
               begin
                  Ada.Text_IO.New_Line(5);
                  Ada.Text_IO.Put_Line("GPG's error code suggests that the signature was bad.  This could be caused");
                  Ada.Text_IO.Put_Line("by using the wrong character set.");
                  Ada.Text_IO.Put_Line("The current charset is " & ToStr(Guessed_Charset) & ".");
                  Readline.Add_History(ToStr(Guessed_Charset));
                  Desired_Charset := ToUBS(Readline.Get_String("Which charset should we use? (empty means no change) "));
                  -- If no charset is given, don't convert and don't re-run.
                  if ToStr(Desired_Charset)'Length > 0
                    -- But with a non-empty response, convert if they're different.
                    and then Guessed_Charset /= Desired_Charset then
                     Externals.Simple.Convert_Charmap(Part_One,
                                                      ToStr(Guessed_Charset),
                                                      ToStr(Desired_Charset));
                     -- Remove the first Out_File.
                     Externals.Simple.Rm_File(Out_File);
                     -- Now, re-run GPG.
                     if SMIME then
                        GPG_Return_Value := Externals.GPG.GPGSM_Verify_Tee(Input_File  => Part_One,
                                                                           Sig_File    => P7S,
                                                                           Output_File => Out_File,
                                                                           Err_File    => Err_File,
									   Status_Filename => SFD_File);
                     else
                        GPG_Return_Value := Externals.GPG.GPG_Verify_Tee(Input_File  => Part_One,
                                                                         Sig_File    => Part_Two,
                                                                         Output_File => Out_File,
                                                                         Err_File    => Err_File,
									 Status_Filename => SFD_File);
                     end if;
		     GPG_Return_Okay := Externals.GPG.Grep_Status(SFD_File,
								  "GOODSIG");
                  end if;
               end;
            end if;
            if ((not GPG_Return_Okay ) and SMIME and Decrypt)
	      and then Externals.GPG.Grep_Status(SFD_File, "ERROR decrypt.algorithm")
	      and then Externals.GPG.Grep_Status(SFD_File, "DECRYPTION_FAILED")
	    then
	       -- Reattempt in case it's a signed message.
	       Reattempt := True;
	       Write_Cache := False;
	       declare
		  Dummy : Integer;
		  pragma Unreferenced(Dummy);
	       begin
		  Dummy := Externals.Simple.System("topal -mime "
						     & Infile
						     & " ""application/x-pkcs7-mime; smime-type=signed-data""");
	       end;
            elsif not GPG_Return_Okay then
               -- Certainly don't cache this thing.
               Write_Cache := False;
               -- Omit anything that deals with the output file
               -- (because it probably doesn't exist).
               Include_Out_File := False;
               Ada.Text_IO.Put_Line("Topal: GPG return value > 1; was = "
                                    & Trim_Leading_Spaces(Integer'Image(GPG_Return_Value))
                                    & "; this is not good -- omitting output.");
            end if;
	    if not Reattempt then
	       -- Append GPG's stdout, stderr, some wrappers to block
	       -- result file.
	       Echo_Out_N("----- Topal: Output generated on ",
			  Blk_File);
	       Date_Append(Blk_File);
	       Echo_Append("----- Topal: GPG output starts -----",
                        Blk_File);
	       Cat_Append(Err_File, Blk_File);
	       Echo_Append("----- Topal: GPG output ends -----",
			   Blk_File);
	    end if;
            -- Copy GPG's block result file to cache file.
            -- But only if we really want to.
            if Write_Cache then
               Cat_Out(Blk_File, ToStr(Cache_Dir) & "/" & ToStr(MD5));
               -- For MIME decrypt stuff, we also save the `out' file.
               if Decrypt then
                  Cat_Out(Out_File,
                          ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out");
               else
                  -- Check for the existence of an out file for verify....
                  if Test_R(Out_File) then
                     Ada.Text_IO.Put_Line("Hmm. Unexpected output file exists....");
                     Cat_Out(Out_File,
                             ToStr(Cache_Dir) & "/" & ToStr(MD5) & ".out");
                  end if;
               end if;
            end if;
         end if;
	 if not Reattempt then
	    -- Output the results.
	    Pager(Blk_File);
	    -- If we've verified a signature, we need to then show the nice
	    -- version of the file via metamail.  If it was decrypted, then we
	    -- show off the Out_File.  Note that if the next object in is also
	    -- a MIME object, we might be invoked again.
	    if Decrypt then
	       if Include_Out_File then
		  -- If this was False, then GPG failed.  So there's nothing for
		  -- View_MIME to deal with.
		  Dos2Unix(Out_File);
		  Externals.Mail.View_MIME(Out_File, True);
	       end if;
	    else
	       Dos2Unix(Part_One);
	       Externals.Mail.View_MIME(Part_One, False);
	    end if;
	 end if;
      else
         -- If we're not to process the block, then just dump out original
         -- input.
         Pager(Infile);
      end if;
      Debug("-Receiving.Mime_Display");
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Receiving.Mime_Display");
         raise;
   end Mime_Display;

   -- Our approach to displaying application/pgp content-types is to hand
   -- off the input file to Display and get it to do the work.  We simply
   -- display it.
   procedure Mime_Display_APGP (Infile       : in String;
                                Content_Type : in String) is
      CT : constant String := Ada.Strings.Fixed.Translate(Content_Type,
                                   Ada.Strings.Maps.Constants.Lower_Case_Map);
   begin
      Debug("+Receiving.Mime_Display_APGP");
      -- Don't save off the original file here: the later call to
      --  Display will do that.
      if CT = "application/pgp" then
         -- We handle all of this nasty, deprecated mode here.
         declare
            Tempfile : constant String := Temp_File_Name("apgptemp");
         begin
            -- Copy the input to the temporary file.
            Cat_Out(Infile, Tempfile);
            -- Invoke Display on the temporary file.
            Display(Tempfile);
            -- Then display it using less.
            Pager(Tempfile);
         end;
      else
         -- Have no idea what to do -- bail out.
         Error("Don't know what to do with Content-Type: "
               & CT);
      end if;
      Debug("-Receiving.Mime_Display_APGP");
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Receiving.Mime_Display_APGP");
         raise;
   end Mime_Display_APGP;
   
   procedure Process_Stdin is
      In_File        : constant String := Temp_File_Name("psstdin");
      Header_File    : constant String := Temp_File_Name("pshdr");
      Body_File      : constant String := Temp_File_Name("psbody");
      CT_File        : constant String := Temp_File_Name("psct");
      CT             : UBS;
      Current_Header : UBS := ToUBS(Header_File);
      Current_Body   : UBS := ToUBS(Body_File);
   begin
      -- Eat stdin.
      Cat_Stdin_Out(D => In_File);
      -- Now we want to open /dev/tty and use that as stdin.
      Externals.Ops.Open_TTY;
      -- Split the header and body.
      Externals.Mail.Extract_Header(Email_Filename  => In_File, 
				    Target_Filename => Header_File);
      Externals.Mail.Extract_Body(Email_Filename  => In_File, 
				  Target_Filename => Body_File);
      -- We try to work around emails that are a multipart/mixed
      --  wrapper where the first attachment is multipart/signed.
      --  Testing suggests that I can't get formail to extract this
      --  header, so I'll try using grep.
      -- Does the header include a content-type of multipart/mixed?
      declare
	 Header_Grep  : constant String := Temp_File_Name("pshdrgrep");
	 Header_File2 : constant String := Temp_File_Name("pshdr2");
	 Header_File3 : constant String := Temp_File_Name("pshdr3");
	 Body_File2   : constant String := Temp_File_Name("psbody2");
	 Body_File3   : constant String := Temp_File_Name("psbody3");
	 Grep_RV      : Integer;
	 Grep_RV2     : Integer;  pragma Unreferenced(Grep_RV2);
      begin
	 --  If so, we'll try to split the body again.
	 Grep_RV := Externals.Simple.Grep_I_InOut
	   (T  => "Content-Type:  *multipart/mixed",
	    D1 => Header_File,
	    D2 => Header_Grep,
	    E  => True);
	 if Grep_RV = 0 then
	    -- It's a multipart/mixed.
	    Ada.Text_IO.New_Line;
	    Ada.Text_IO.Put_Line("This appears to be a multipart/mixed email.");
	    Ada.Text_IO.Put_Line("I'll look for an attachment I can process.");
	    Ada.Text_IO.New_Line;
	    -- We now need to split the body into a second header/body pair.
	    Externals.Mail.Extract_Header(Email_Filename  => Body_File, 
					  Target_Filename => Header_File2);
	    -- Under the current hypothesis, we'll get some MIME
	    --  boundaries included.  Get rid of them.
	    Grep_RV2 := Externals.Simple.Grep_I_InOut
	      (T => "^--.*$",
	       D1 => Header_File2,
	       D2 => Header_File3,
	       E  => True,
	       V  => True);
	    Externals.Mail.Extract_Body(Email_Filename  => Body_File, 
					Target_Filename => Body_File2);
	    -- Similarly, to avoid confusing the boundary detection
	    --  code, we'll blow away the *last* line of the body file
	    --  to avoid getting the last boundary.
	    -- FIXME: This is a horrible hack.  It should be replaced
	    --  with proper multipart handling.
	    Simple.Sed_InOut("$ d",
			     Body_File2,
			     Body_File3);
	    Current_Header := ToUBS(Header_File3);
	    Current_Body   := ToUBS(Body_File3);
	 end if;
      end;
      -- Determine if it's inline or MIME.
      Mail.Formail_Concat_Extract_InOut(Source => ToStr(Current_Header), 
					Target => CT_File,
					Header => "Content-Type");
      CT := Read_Fold(CT_File);
      Ada.Text_IO.Put_Line("CT = """ & ToStr(CT) & """");
      -- FIXME do we need to guess the character set?
      Ada.Text_IO.Put_Line("FIXME, do we need to guess the character set?");
      declare
	 CT1 : constant String := ToStr(CT);
	 use Ada.Strings.Fixed;
	 CTS : constant String := CT1(Index_Non_Blank(CT1) .. CT1'Last);
      begin
	 if (Index(CTS, "multipart/signed") > 0)
	   or (Index(CTS, "multipart/encrypted") > 0)
	   or (Index(CTS, "application/x-topal-signed") > 0)
	   or (Index(CTS, "multipart/x-topal-encrypted") > 0)
	   or (Index(CTS, "application/x-topal-encrypted") > 0)
	   or (Index(CTS, "application/pkcs7-mime") > 0)
	   or (Index(CTS, "application/x-pkcs7-mime") > 0) then
	    Ada.Text_IO.Put_Line("Handle as MIME...");
	    Mime_Display(ToStr(Current_Body), CTS);
	 elsif (Index(CTS, "application/pgp") > 0) then
	    Ada.Text_IO.Put_Line("Handle as application/pgp...");
	    Mime_Display_APGP(ToStr(Current_Body), CTS);
	 else
	    Ada.Text_IO.Put_Line("Handle as inline...");
	    Display(ToStr(Current_Body));
	    Pager(ToStr(Current_Body));
	 end if;
      end;
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Receiving.Process_Stdin");
         raise;
   end Process_Stdin;

end Receiving;
