1
-- Topal: GPG/GnuPG and Alpine/Pine integration
2
-- Copyright (C) 2001--2008 Phillip J. Brooke
4
-- This program is free software: you can redistribute it and/or modify
5
-- it under the terms of the GNU General Public License version 3 as
6
-- published by the Free Software Foundation.
8
-- This program is distributed in the hope that it will be useful,
9
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
10
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
-- GNU General Public License for more details.
13
-- You should have received a copy of the GNU General Public License
14
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
6
16
with Ada.Command_Line;
9
20
with Externals; use Externals;
10
21
with Externals.GPG;
27
25
with Menus; use Menus;
28
26
with Misc; use Misc;
30
30
package body Sending is
32
32
Exit_Send_Loop : exception; -- Subroutine says `bail out, please'.
34
34
procedure Key_Preselect (K : in out Keys.Key_List;
35
Recipients : in UBS_Array) is
35
Recipients : in UBS_Array;
36
Alt_Sign : in String := "") is
37
38
-- Can we preselect any keys?
38
39
Keys.Empty_Keylist(K);
39
40
Keys.Use_Keylist(Recipients, K);
41
-- Add our own key. If Alt_Sign is non-empty, then try and add
42
-- keys for that instead.
45
Found : Boolean := False;
44
if ToStr(Config.My_Key) = "" then
45
Ada.Text_IO.Put_Line("my-key is empty; not selecting any keys for self");
47
Keys.Add_Keys_By_Fingerprint(Value_Nonempty(Config.My_Key),
49
if Alt_Sign /= "" then
50
Ada.Text_IO.Put_Line("Looking for signing key for `"&Alt_Sign&"' on SAKE list...");
51
for J in 1..Config.SAKE_Count loop
52
if ToStr(UAP.Value(Config.SAKE_Email, J)) = Alt_Sign then
53
Keys.Add_Keys_By_Fingerprint(UAP.Value(Config.SAKE_Key, J),
56
Ada.Text_IO.Put_Line("Added key `"&ToStr(UAP.Value(Config.SAKE_Key, J))&"' for signing...");
57
Config.My_Key := UAP.Value(Config.SAKE_Key, J);
59
Ada.Text_IO.Put_Line("Couldn't add key `"&ToStr(UAP.Value(Config.SAKE_Key, J))&"' for signing...");
64
if Alt_Sign /= "" and (not Found) then
65
Keys.Empty_Keylist(AKL);
66
Ada.Text_IO.Put_Line("Looking for signing key for `"&Alt_Sign&"' via keyring (& XK list)...");
67
Keys.Add_Secret_Keys(ToUBS(Alt_Sign),
70
-- Any keys to remove on the SXK list?
71
for J in 1..Config.SXK_Count loop
72
Keys.Remove_Key(UAP.Value(Config.SXK_Key, J), AKL);
74
KC := Keys.Count(AKL);
51
Ada.Text_IO.Put_Line("Info: Added `my-key' key(s) for self");
53
Ada.Text_IO.Put_Line("Warning: my-key="
54
& ToStr(Config.My_key)
55
& " does not find keys");
78
Ada.Text_IO.Put_Line("Added one signing key...");
80
Keys.First_Key_From_List(AKL, The_Key);
81
Keys.Add_Key(The_Key, K, Keys.Both);
82
Config.My_Key := The_Key;
85
Ada.Text_IO.Put_Line("Found multiple possible signing keys...");
86
-- Choose one. If aborted, fall-back to my-key.
87
Keys.Select_Key_From_List(AKL, The_Key, Found);
88
-- Found is not aborted...
91
Keys.Add_Key(The_Key, K, Keys.Both);
92
Config.My_Key := The_Key;
94
Ada.Text_IO.Put_Line("Aborted selection of secret key, will consider my-key instead");
98
Ada.Text_IO.Put_Line("Can't find alternative key, trying my-key...");
101
if Alt_Sign = "" or (not Found) then
102
if ToStr(Config.My_Key) = "" then
103
Ada.Text_IO.Put_Line("my-key is empty; not selecting any keys for self");
105
Keys.Add_Keys_By_Fingerprint(Value_Nonempty(Config.My_Key),
110
Ada.Text_IO.Put_Line("Added `my-key' key(s) for self");
112
Ada.Text_IO.Put_Line("Warning: my-key="
113
& ToStr(Config.My_key)
114
& " does not find keys");
105
170
Ada.Command_Line.Set_Exit_Status(Ada.Command_Line.Failure);
106
171
raise Exit_Send_Loop;
107
172
elsif Mime_Selection = Multipart then
108
Echo_Out("Content-Type: text/plain",
109
Temp_File_Name("pct"));
110
Echo_Append("", Temp_File_Name("pct"));
111
Cat_Append(Tmpfile, Temp_File_Name("pct"));
112
Mv_F(Temp_File_Name("pct"), Tmpfile);
174
PCT : constant String := Temp_File_Name("pct");
176
Echo_Out("Content-Type: text/plain", PCT);
177
Echo_Append("", PCT);
178
Cat_Append(Tmpfile, PCT);
180
-- Call out to attachments in case we have to modify Tmpfile.
181
Attachments.Replace_Tmpfile(Tmpfile, AL);
170
247
when Multipart => -- RFC2015 multipart
171
248
-- This is the nasty one.
173
Blk1 : String := Temp_File_Name("mp1");
174
Blk2 : String := Temp_File_Name("mp2");
175
MC : String := Temp_File_Name("mc");
250
Blk1 : constant String := Temp_File_Name("mp1");
251
Blk2 : constant String := Temp_File_Name("mp2");
252
MC : constant String := Temp_File_Name("mc");
177
254
-- We first create the two blocks.
178
255
-- The first block is easy:
221
298
Mime : in Boolean;
222
299
Mimefile : in String;
223
300
Send_Keys : in Keys.Key_List;
224
Selection : in Send_Index) is
225
Out_File : String := Temp_File_Name("out");
226
DTBL_File : String := Temp_File_Name("dtbl");
227
QP_File : String := Temp_File_Name("qp");
228
TDS_File : String := Temp_File_Name("tds");
301
Selection : in Send_Index;
302
AL : in Attachments.Attachment_List) is
303
Out_File : constant String := Temp_File_Name("out");
304
DTBL_File : constant String := Temp_File_Name("dtbl");
305
QP_File : constant String := Temp_File_Name("qp");
306
TDS_File : constant String := Temp_File_Name("tds");
229
307
Mime_Selection : MIME_Send_Index;
261
348
Header => "Content-Type: text/plain",
262
349
Use_Encoding => True,
263
350
Encoding => "quoted-printable");
351
-- Call out to attachments in case we have to modify QP_File.
352
Attachments.Replace_Tmpfile(QP_File, AL);
264
353
-- Then we clearsign it.
265
GPG.GPG_Wrap(ToStr(Config.Gpg_Options)
354
Externals.GPG.GPG_Wrap(ToStr(Config.Gpg_Options)
266
355
& " --detach-sign --armor --local-user "
267
356
& Value_Nonempty(Config.my_key)
365
458
-- This is the nasty one.
366
459
-- We are using the combined method allowed in section 6.2.
368
Blk1 : String := Temp_File_Name("mp1");
369
Blk2 : String := Temp_File_Name("mp2");
370
MC : String := Temp_File_Name("mc");
461
Blk1 : constant String := Temp_File_Name("mp1");
462
Blk2 : constant String := Temp_File_Name("mp2");
463
MC : constant String := Temp_File_Name("mc");
372
465
-- We first create the two blocks.
373
466
-- The first block is easy:
396
489
-- encapsulating, so we have to sign then
399
Blk1 : String := Temp_File_Name("mp1");
400
Blk2 : String := Temp_File_Name("mp2");
401
MCS : String := Temp_File_Name("mcs");
402
MC : String := Temp_File_Name("mc");
492
Blk1 : constant String := Temp_File_Name("mp1");
493
Blk2 : constant String := Temp_File_Name("mp2");
494
MCS : constant String := Temp_File_Name("mcs");
495
MC : constant String := Temp_File_Name("mc");
404
497
-- We first create the two blocks.
405
Mail.Mimeconstruct_Subpart(Infile => TDS_File,
498
Externals.Mail.Mimeconstruct_Subpart(Infile => TDS_File,
407
500
Content_Type => "application/pgp-signature",
408
501
Dos2UnixU => False,
409
502
Use_Header => False,
410
503
Use_Encoding => False);
411
Mail.Mimeconstruct2(Part1_Filename => QP_File,
504
Externals.Mail.Mimeconstruct2(Part1_Filename => QP_File,
412
505
Part2_Filename => Blk2,
413
506
Output_Filename => MCS,
414
507
Content_Type => "multipart/signed; protocol=""application/pgp-signature""; micalg="""
415
& GPG.Micalg_From_Filename(TDS_File)
508
& Externals.GPG.Micalg_From_Filename(TDS_File)
417
510
-- Now the encrypt bit....
418
511
Rm_File(Tmpfile);
419
GPG.GPG_Wrap(ToStr(Config.Gpg_Options)
512
Externals.GPG.GPG_Wrap(ToStr(Config.Gpg_Options)
420
513
& " --encrypt --armor "
421
514
& ToStr(Config.general_options)
472
565
Non_Pine : in Boolean;
473
566
Mime : in Boolean;
474
567
Mimefile : in String;
475
Selection : in Send_Index) is
476
Out_File : String := Temp_File_Name("out");
477
DTBL_File : String := Temp_File_Name("dtbl");
478
QP_File : String := Temp_File_Name("qp");
479
TDS_File : String := Temp_File_Name("tds");
568
Selection : in Send_Index;
569
AL : in Attachments.Attachment_List) is
570
Out_File : constant String := Temp_File_Name("out");
571
DTBL_File : constant String := Temp_File_Name("dtbl");
572
QP_File : constant String := Temp_File_Name("qp");
573
TDS_File : constant String := Temp_File_Name("tds");
480
574
Mime_Selection : MIME_Send_Index;
512
606
Header => "Content-Type: text/plain",
513
607
Use_Encoding => True,
514
608
Encoding => "quoted-printable");
609
-- Call out to attachments in case we have to modify QP_File.
610
Attachments.Replace_Tmpfile(QP_File, AL);
515
611
-- Then we clearsign it.
516
GPG.GPG_Wrap(ToStr(Config.Gpg_Options)
612
Externals.GPG.GPG_Wrap(ToStr(Config.Gpg_Options)
517
613
& " --detach-sign --armor --local-user "
518
614
& Value_Nonempty(Config.my_key)
586
686
-- This is the _really_ nasty one.
587
687
-- At this point, we have QP_File, the quoted-printable, and TDS_File, then detached signature.
589
Blk2 : String := Temp_File_Name("mp2");
590
MC : String := Temp_File_Name("mc");
689
Blk2 : constant String := Temp_File_Name("mp2");
690
MC : constant String := Temp_File_Name("mc");
592
Mail.Mimeconstruct_Subpart(Infile => TDS_File,
692
Externals.Mail.Mimeconstruct_Subpart(Infile => TDS_File,
594
694
Content_Type => "application/pgp-signature",
595
695
Dos2UnixU => False,
596
696
Use_Header => False,
597
697
Use_Encoding => False);
598
Mail.Mimeconstruct2(Part1_Filename => QP_File,
698
Externals.Mail.Mimeconstruct2(Part1_Filename => QP_File,
599
699
Part2_Filename => Blk2,
600
700
Output_Filename => MC,
601
701
Content_Type => "multipart/signed; protocol=""application/pgp-signature""; micalg="""
602
& GPG.Micalg_From_Filename(TDS_File)
702
& Externals.GPG.Micalg_From_Filename(TDS_File)
604
704
-- Now we need to split these up.
605
705
Mail.Extract_Content_Type_From_Header(MC, Mimefile);
606
706
Mail.Extract_Body(MC, Tmpfile);
675
770
Mimefile : in String := "") is
676
771
Send_Keys : Keys.Key_List;
677
772
Selection : Send_Index;
773
Originfile : constant String := Temp_File_Name("origin");
774
Hdrfile : constant String := Temp_File_Name("hdr");
775
Tmpfile2 : constant String := Temp_File_Name("tmp2");
776
Fromfile : constant String := Temp_File_Name("from");
777
Fromfile2 : constant String := Temp_File_Name("from2");
779
AL : Attachments.Attachment_List;
782
-- Save the original input.
783
Externals.Simple.Cat_Out(Tmpfile, Originfile);
784
-- If Config.All_Headers is set, then split tmpfile into header
785
-- and body. But we'll copy the tmpfile into another tmp
786
-- first, then put the body alone back in Tmpfile.
787
if Config.All_Headers then
788
Externals.Simple.Mv_F(Tmpfile, Tmpfile2);
789
-- Make sure the line endings are correct.
790
Externals.Simple.Dos2Unix(Tmpfile2);
791
Externals.Mail.Extract_Header(Email_Filename => Tmpfile2,
792
Target_Filename => Hdrfile);
793
Externals.Mail.Extract_Body(Email_Filename => Tmpfile2,
794
Target_Filename => Tmpfile);
796
-- If Config.Read_From is set, then parse the header to find the
797
-- fromline and set my-key appropriately. This implies that
798
-- All_Headers was set, too.
799
if Config.Read_From then
800
Externals.Mail.Formail_Concat_Extract_InOut("From:",
803
Externals.Simple.Sed_InOut("s/^.*<//; s/>.*$//",
804
Fromfile, Fromfile2);
806
F : Ada.Text_IO.File_Type;
808
Ada.Text_IO.Open(File => F,
809
Mode => Ada.Text_IO.In_File,
811
Alt_Sign := Unbounded_Get_Line(F);
812
Ada.Text_IO.Close(F);
680
815
-- Sort out the send menus/msgs.
681
Key_Preselect(Send_Keys, Recipients);
816
Key_Preselect(Send_Keys, Recipients, ToStr(Alt_Sign));
817
-- Initialise attachments.
818
Attachments.Empty_Attachment_List(AL);
682
819
-- Loop around doingstuff, now.
685
Ada.Text_IO.New_Line(2);
822
Ada.Text_IO.New_Line(5);
823
Ada.Text_IO.Put_Line("** Main send menu:");
824
Ada.Text_IO.Put(Integer'Image(Keys.Count(Send_Keys))
825
& " key(s) in key list ");
827
Ada.Text_IO.New_Line;
687
828
Selection := Send_Menu_NP;
830
Ada.Text_IO.Put_Line(Integer'Image(Attachments.Count(AL))
689
832
Selection := Send_Menu_Pine;
699
842
Ada.Text_IO.Put_Line("my-key is empty; not selecting any keys for self");
701
844
Keys.Add_Keys_By_Fingerprint(Value_Nonempty(Config.My_Key),
848
when EditOwn => -- Change own key
849
Configuration.Edit_Own_Key;
850
when ViewMail => -- View the input email.
852
when EditMail => -- Edit the input email.
854
Editor_Command : UBS;
858
pragma Unreferenced(Dummy);
861
Put_Line("Edit input email:");
863
:= ToUBS(Readline.Get_String("Which editor command? (filename will be appended) "));
864
if ToStr(Editor_Command)'Length > 0 then
865
Dummy := Externals.Simple.System(Editor_Command & ToUBS(" " & Tmpfile));
868
when AttachEdit => -- Edit the attachment list.
869
Attachments.List(AL);
704
870
when Encrypt | EncryptPO => -- Do GPG: encrypt
705
871
Encrypt(Tmpfile, Non_Pine,
707
Send_Keys, Selection);
873
Send_Keys, Selection,
709
876
when SignEncrypt | SignEncryptPO => -- Do GPG: sign-encrypt
710
877
Sign_Encrypt(Tmpfile, Non_Pine,
712
Send_Keys, Selection);
879
Send_Keys, Selection,
714
882
when Clearsign | ClearSignPO => -- Do GPG: clearsign
715
883
Clearsign(Tmpfile, Non_Pine,
719
888
when DetachSignPO => -- Do GPG: create detached signature
720
889
Detached_Sig(Tmpfile);
726
895
when NoGPG => -- Pass thu' unchanged.
727
896
Send_Unchanged(Tmpfile, Non_Pine, Mime, Mimefile);
898
when Remote => -- Run remote connection
899
-- Restore original tmpfile from origin.
900
Externals.Simple.Cat_Out(Originfile, Tmpfile);
901
Remote_Mode.Send(Tmpfile, Mime, Mimefile, Recipients);
731
905
when Exit_Send_Loop =>