-- 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/>.

separate(Sending)
procedure Sign_Encrypt  (Tmpfile   : in String;
			 Non_Pine  : in Boolean;
			 Mime      : in Boolean;
			 Mimefile  : in String;
			 Send_Keys : in Keys.Key_List;
			 Selection : in Send_Modes;
			 Mime_Selection : in MIME_Modes;
			 AL        : in Attachments.Attachment_List;
			 Hdrfile   : in String;
			 Recipients : in UBS_Array;
			 Actual_Send : in Boolean;
			 New_Headers : out UVV;
			 AS_Content_Type : in String) is
   Out_File       : constant String   := Temp_File_Name("out");
   SFD_File       : constant String   := Temp_File_Name("sfd");
   SFD2_File      : constant String   := Temp_File_Name("sfd2");
   QP_File        : constant String   := Temp_File_Name("qp");
   TDS_File       : constant String   := Temp_File_Name("tds");
begin
   -- Run GPG.
   if Mime and then (Mime_Selection = MultipartEncap 
		       or Mime_Selection = SMIME) Then
      -- We need to do something different with Tmpfile.
      -- First, we delete all trailing blank lines.
      begin
	 Prepare_For_Clearsign(Tmpfile,
			       QP_File,
			       AS_Content_Type);
	 -- Call out to attachments in case we have to modify QP_File.
	 Attachments.Replace_Tmpfile(QP_File, AL);
	 -- Then we clearsign it.
	 if Mime_Selection = SMIME then
	    Externals.GPG.GPGSM_Wrap(" --base64 --sign --local-user "
				     & Value_Nonempty(Config.UBS_Opts(my_key))
				     & " "
				     & " --output "
				     & TDS_File
				     & " "
				     & QP_File,
				   TDS_File,
				   SFD_File);
	 else
	    Externals.GPG.GPG_Wrap(" --detach-sign --textmode --armor --local-user "
				     & Value_Nonempty(Config.UBS_Opts(my_key))
				     & " "
				     & UGA_Str(Signing => True)
				     & " --output "
				     & TDS_File
				     & " "
				     & QP_File,
				   TDS_File,
				   SFD_File);
	 end if;
      exception
	 when others =>
	    Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
				 "Exception raised in Sending.Sign_Encrypt (GPG MIME/4 block)");
	    raise;
      end;

   else

      if Mime and then Mime_Selection = Multipart then
	 -- We need to do something different with Tmpfile.
	 -- First, we delete all trailing blank lines.
	 begin
	    Prepare_For_Clearsign(Tmpfile,
				  QP_File,
				  AS_Content_Type);
	    -- Call out to attachments in case we have to modify QP_File.
	    Attachments.Replace_Tmpfile(QP_File, AL);
	    -- Then rename QP_File to Tmpfile...
	    Mv_F(QP_File, Tmpfile);
	 exception
	    when others =>
	       Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
				    "Exception raised in Sending.Sign_Encrypt (Pre-GPG MIME/3 block)");
	       raise;
	 end;
      end if;

      begin
	 Externals.GPG.GPG_Wrap(" --encrypt --sign --textmode --armor --local-user "
				  & Value_Nonempty(Config.UBS_Opts(my_key))
				  & " "
				  & UGA_Str(Signing => True)
				  & " --output "
				  & Out_File
				  & " "
				  & Keys.Processed_Recipient_List(Send_Keys)
				  & " "
				  & Tmpfile,
				Out_File,
			        SFD_File);
      exception
	 when others =>
	    Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
				 "Exception raised in Sending.Sign_Encrypt (GPG block)");
	    raise;
      end;

   end if;

   begin
      if not (Mime and then (Mime_Selection = MultipartEncap
			       or Mime_Selection = SMIME)) then
	 if Selection = SignEncryptPO then
	    -- Rename the file appropriately.
	    Mv_F(Out_File, Tmpfile & ".asc");
	 else
	    Mv_F(Out_File, Tmpfile);
	 end if;
      end if;
   exception
      when others =>
	 Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
			      "Exception raised in Sending.Sign_Encrypt (MV block)");
	 raise;
   end;

   begin
      if Mime then
	 case Mime_Selection is
	    when InlinePlain => -- Inline plain text
	       Echo_Out("Content-Type: text/plain",
			Mimefile);
	       New_Headers.Append(ToUBS("Content-Type: text/plain"));
	    when AppPGP => -- application/pgp
	       Echo_Out("Content-Type: application/pgp; format=text; x-action=encrypt",
			Mimefile);
	       New_Headers.Append(ToUBS("Content-Type: application/pgp; format=text; x-action=encrypt"));
	    when Multipart => -- RFC2015 multipart
			      -- This is the nasty one.
			      -- We are using the combined method allowed in section 6.2.
	       declare
		  Blk1   : constant String := Temp_File_Name("mp1");
		  Blk2   : constant String := Temp_File_Name("mp2");
		  MC     : constant String := Temp_File_Name("mc");
	       begin
		  -- We first create the two blocks.
		  -- The first block is easy:
		  Echo_Out("Content-Type: application/pgp-encrypted",
			   Blk1);
		  Echo_Append("", Blk1);
		  Echo_Append("Version: 1", Blk1);
		  -- The second block starts with a
		  -- content-type, then is the tmpfile we've
		  -- put together.
		  Echo_Out("Content-Type: application/octet-stream",
			   Blk2);
		  Echo_Append("Content-Disposition: attachment; filename=""message.asc""", Blk2);
		  Echo_Append("", Blk2);
		  Cat_Append(Tmpfile, Blk2);
		  -- Now we put them together.
		  Externals.Mail.Mimeconstruct2(Part1_Filename  => Blk1,
						Part2_Filename  => Blk2,
						Output_Filename => MC,
						Content_Type    => "multipart/encrypted; protocol=""application/pgp-encrypted""",
						Prolog          => "This is an OpenPGP/MIME encrypted message (RFC2440, RFC3156).");
		  -- Now we need to split these up.
		  Mail.Extract_Content_Type_From_Header(MC, Mimefile);
		  New_Headers.Append(Read_Fold(Mimefile));
		  Mail.Extract_Body(MC, Tmpfile);
	       end;
	    when MultipartEncap => -- RFC2015 multipart encapsulated
				   -- This is the REALLY nasty one.  We are
				   -- encapsulating, so we have to sign then
				   -- encrypt.
	       declare
		  Blk1   : constant String := Temp_File_Name("mp1");
		  Blk2   : constant String := Temp_File_Name("mp2");
		  MCS    : constant String := Temp_File_Name("mcs");
		  MC     : constant String := Temp_File_Name("mc");
	       begin
		  -- We first create the two blocks.
		  Externals.Mail.Mimeconstruct_Subpart(Infile       => TDS_File,
						       Outfile      => Blk2,
						       Content_Type => "application/pgp-signature",
						       Dos2UnixU    => False,
						       Use_Encoding => False,
						       Attachment_Name => "signature.asc",
						       Disposition => Externals.Mail.Attachment);
		  Externals.Mail.Mimeconstruct2(Part1_Filename  => QP_File,
						Part2_Filename  => Blk2,
						Output_Filename => MCS,
						Content_Type    => "multipart/signed; protocol=""application/pgp-signature""; micalg="""
						  & Externals.GPG.Micalg_From_Filename(TDS_File)
						  & """",
						Prolog          => "This is an OpenPGP/MIME signed message (RFC2440, RFC3156).");
		  -- Now the encrypt bit....
		  Rm_File(Tmpfile);
		  Externals.GPG.GPG_Wrap(" --encrypt --armor "
					   & " "
					   & " --output "
					   & Tmpfile
					   & " "
					   & Keys.Processed_Recipient_List(Send_Keys)
					   & " "
					   & MCS,
					 Tmpfile,
					 SFD_File);
		  -- The first block is easy:
		  Echo_Out("Content-Type: application/pgp-encrypted",
			   Blk1);
		  Echo_Append("", Blk1);
		  Echo_Append("Version: 1", Blk1);
		  -- The second block starts with a
		  -- content-type, then is the tmpfile we've
		  -- put together.
		  Echo_Out("Content-Type: application/octet-stream",
			   Blk2);
		  Echo_Append("Content-Disposition: attachment; filename=""message.asc""", Blk2);
		  Echo_Append("", Blk2);
		  Cat_Append(Tmpfile, Blk2);
		  -- Now we put them together.
		  Externals.Mail.Mimeconstruct2(Part1_Filename => Blk1,
						Part2_Filename => Blk2,
						Output_Filename => MC,
						Content_Type => "multipart/encrypted; protocol=""application/pgp-encrypted""",
						Prolog          => "This is an OpenPGP/MIME encrypted message (RFC2440, RFC3156).");
		  -- Now we need to split these up.
		  Mail.Extract_Content_Type_From_Header(MC, Mimefile);
		  New_Headers.Append(Read_Fold(Mimefile));
		  Mail.Extract_Body(MC, Tmpfile);
	       end;
	    when SMIME =>
	       -- Similar to RFC2015 multipart encapsulated.
	       declare
		  Blk2   : constant String := Temp_File_Name("mp2");
		  MM1    : constant String := Temp_File_Name("mm1");
		  MM2    : constant String := Temp_File_Name("mm2");
		  MM3    : constant String := Temp_File_Name("mm3");
	       begin
		  -- We first create the two blocks.
		  Echo_Out("Content-Type: application/pkcs7-mime; smime-type=signed-data; name=""smime.p7s""",
			   Blk2);
		  Echo_Append("Content-Transfer-Encoding: base64",
			      Blk2);
		  Echo_Append("Content-Disposition: attachment; filename=""smime.p7s""",
			      Blk2);
		  Echo_Append("Content-Description: S/MIME Cryptographic Signature",
			      Blk2);
		  Echo_Append("", Blk2);
		  Cat_Append(TDS_File, Blk2);
		  -- Now the encrypt bit....
		  Rm_File(Tmpfile);
		  Externals.GPG.GPGSM_Wrap_Encrypt(MM1,
						   SFD2_File,
						   Blk2,
						   Send_Keys);
		  if Actual_Send then
		     New_Headers.Append(ToUBS("Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=""smime.p7m"""));
		     New_Headers.Append(ToUBS("Content-Transfer-Encoding: base64"));
		     New_Headers.Append(ToUBS("Content-Disposition: attachment; filename=""smime.p7m"""));
		     Externals.Simple.Mv_F(MM1, Tmpfile);
		  else
		     -- Then concatenate it all together.
		     -- Same workaround as in sending-encrypt.adb.
		     Echo_Out("Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=""smime.p7m""",
			      MM2);
		     Echo_Append("Content-Transfer-Encoding: base64",
				 MM2);
		     Echo_Append("Content-Disposition: attachment; filename=""smime.p7m""",
				 MM2);
		     Echo_Append("", MM2);
		     Cat_Append(MM1, MM2);
		     Mail.Mimeconstruct_Mixed(UBS_Array'(1 => ToUBS(MM2)),
					      MM3);
		     Mail.Extract_Content_Type_From_Header(MM3, Mimefile);
		     New_Headers.Append(Read_Fold(Mimefile));
		     Mail.Extract_Body(MM3,Tmpfile);
		  end if;
	       end;
	 end case;
      end if;
   exception
      when others =>
	 Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
			      "Exception raised in Sending.Sign_Encrypt (MIME block 2)");
	 raise;
   end;
   
   if not Actual_Send then
      Check_Send(Tmpfile, Non_Pine, Mime, Mimefile, Hdrfile, Recipients);
   end if;

exception
   when others =>
      Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error,
			   "Exception raised in Sending.Sign_Encrypt");
      raise;
end Sign_Encrypt;
