-- 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.Command_Line;
with Ada.Exceptions;
with Ada.Text_IO;
with Externals.Mail;
with Externals.Simple;
with Misc;             use Misc;
with Version_ID;

package body Workaround is

   -- Read on stdin, write on stdout.
   -- Take a single email.
   -- If simple, then simply change the MIME Content-Type.
   -- If not simple, rewrite it in a multipart/misc wrapper.
   procedure Fix_Email is
      Stdin     : constant String  := Temp_File_Name("wfestdin");
      CT        : constant String  := Temp_File_Name("wfect");
      Output    : constant String  := Temp_File_Name("wfeoutput");
      Input2    : constant String  := Temp_File_Name("wfeinput");
      Header2   : constant String  := Temp_File_Name("wfeheader");
      Body2     : constant String  := Temp_File_Name("wfebody");
      Part1A    : constant String  := Temp_File_Name("wfepart1a");
      Part1     : constant String  := Temp_File_Name("wfepart1");
      Part2     : constant String  := Temp_File_Name("wfepart2");
      MCO       : constant String  := Temp_File_Name("wfemco");
      MCO2      : constant String  := Temp_File_Name("wfemco2");
      Mangle    : Boolean := False;
      Encrypted : Boolean := False;
   begin
      -- First, save off the standard input into a temp file.
      Debug("Saving off stdin...");
      Externals.Simple.Cat_Stdin_Out(Stdin);
      Debug("Done save off of stdin.");
      -- Get the content-type line.
      Externals.Mail.Formail_Concat_Extract_InOut("Content-Type:",
                                                  Stdin,
                                                  CT);
      -- Sort out the Mangle and Encrypted flags.
      if Externals.Simple.Grep_I_InOut("multipart/signed",
                                       CT,
                                       "/dev/null") = 0 then
         Mangle := True;
      elsif Externals.Simple.Grep_I_InOut("multipart/encrypted",
                                          CT,
                                          "/dev/null") = 0 then
         Mangle := True;
         Encrypted := True;
      end if;
      -- Do we mangle?
      if Mangle then
         if Config.Boolean_Opts(FE_Simple) then
            -- Simple mangling.
            if Encrypted then
               Externals.Simple.Sed_InOut("1,/^[[:space:]]*$/ { s!^Content-Type: *multipart/encrypted!Content-Type: application/x-topal-encrypted! ; }",
                                          Source => Stdin,
                                          Target => Output);
               Externals.Simple.Cat(Output);
            else
               Externals.Simple.Sed_InOut("1,/^[[:space:]]*$/ { s!^Content-Type:  *multipart/signed!Content-Type: application/x-topal-signed! ; }",
                                          Source => Stdin,
                                          Target => Output);
               Externals.Simple.Cat(Output);
            end if;
         else
            -- Rewrite mangling.
            -- Drop the content-type header.
            Externals.Mail.Formail_Drop_InOut(UBS_Array'(1 => ToUBS("Content-Type:"),
                                                         2 => ToUBS("Content-Transfer-Encoding:"),
                                                         3 => ToUBS("MIME-Version:")),
                                              Source => Stdin,
                                              Target => Input2);
            -- Split the header and body apart.
            Externals.Mail.Extract_Header(Input2,
                                          Header2);
            Externals.Mail.Extract_Body(Input2,
                                        Body2);
            -- Now, create a new email: all the same headers, except the
            -- removed content-type.  The body is a multipart/mixed,
            -- comprising just the (slightly modified) original body.
            -- Create a simple body.
            Externals.Simple.Echo_Out("Multipart email processed by Topal "
                                     & Version_ID.Release & ".",
                                     Part1A);
            Externals.Mail.Mimeconstruct_Subpart(Infile       => Part1A,
                                                 Outfile      => Part1,
                                                 Content_Type => "text/plain",
                                                 Dos2UnixU    => True,
                                                 Use_Encoding => False,
                                                 Encoding     => "");
            -- Create this modified body, which is the original email
            -- with just the content-type header.
            Externals.Simple.Echo_Out_N("Content-Type: ", Part2);
            Externals.Simple.Cat_Append(CT, Part2);
            Externals.Simple.Echo_Append("", Part2);
            Externals.Simple.Cat_Append(Body2, Part2);
            Externals.Mail.Mimeconstruct_Mixed(Filenames =>
                                                 UBS_Array'(1 => ToUBS(Part1),
                                                            2 => ToUBS(Part2)),
                                               Outfile    => MCO);
            Externals.Simple.Cat_Out(Header2, Output);
            Externals.Simple.Echo_Append("X-Mangled-By: topal "
                                         & Version_ID.Release,
                                         Output);
            -- We don't want the first part of the multipart mixed.
            Externals.Simple.Sed_InOut("5,9 d", MCO, MCO2);
            Externals.Simple.Cat_Append(MCO2, Output);
            Externals.Simple.Cat(Output);
         end if;
      else
         -- Just output the original input.
         Externals.Simple.Cat(Stdin);
      end if;
      Ada.Command_Line.Set_Exit_Status(Ada.Command_Line.Success);
   exception
      when The_Exception: others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Workaround.Fix_Email");
         -- This is bad news.  Write it all out to the emergency log.
         declare
            Log : constant String
              := ToStr(Topal_Directory) & "/workaround-error-log";
            use Externals.Simple;
         begin
            Echo_Append(Integer'Image(Our_PID) &
                        "Exception raised in Workaround.Fix_Email",
                        Log);
            Echo_Append(Integer'Image(Our_PID) &
                        "Exception raised: "
                        & Ada.Exceptions.Exception_Name(The_Exception),
                        Log);
            Echo_Append(Integer'Image(Our_PID) &
                        Ada.Exceptions.Exception_Information(The_Exception),
                        Log);
         end;
         -- Dump put the original output.  Let's hope that it all works okay.
         Externals.Simple.Cat(Stdin);
         raise;
   end Fix_Email;

   procedure Fix_Folder (Folder        : in String;
                         Topal_Command : in String;
                         The_Date      : in String) is
      Backup_Name : constant String
        := Folder & ".topal-fix-folder-" & The_Date;
      New_Name    : constant String := Folder & ".topal-new";
   begin
      -- Backup.
      Ada.Text_IO.Put_Line("  Backup file is `" & Backup_Name & "'");
      Externals.Simple.Cat_Out(Folder, Backup_Name);
      -- Invoke formail on this folder.
      Externals.Mail.Formail_Action_InOut(Source => Folder,
                                          Target => New_Name,
                                          Action =>
                                            Topal_Command & " --fix-email");
      -- Do a diff.
      if Externals.Simple.Diff_Brief(Folder, New_Name) then
         Ada.Text_IO.Put_Line("  Made some changes!");
         -- Overwrite original with mangled version.
         Externals.Simple.Mv_F(New_Name, Folder);
      else
         Ada.Text_IO.Put_Line("  No changes made.");
         Externals.Simple.Rm_File(New_Name);
      end if;
      -- Done.
      Ada.Text_IO.Put_Line("  Finished with `" & Folder & "'");
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Workaround.Fix_Folder");
         raise;
   end Fix_Folder;


   procedure Fix_Folders (Folders : in UBS_Array) is
      Topal_Command : constant String := Ada.Command_Line.Command_Name;
      The_Date      : constant String := Externals.Simple.Date_String;
   begin
      -- Loop through each folder.
      for I in Folders'Range loop
         Ada.Text_IO.Put_Line("Processing folder `"
                              & ToStr(Folders(I)) & "'...");
         Fix_Folder(ToStr(Folders(I)), Topal_Command, The_Date);
      end loop;
      Ada.Command_Line.Set_Exit_Status(Ada.Command_Line.Success);
   exception
      when others =>
         Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
                              "Exception raised in Workaround.Fix_Folders");
         raise;
   end Fix_Folders;

end Workaround;
