forked from lukele/GPGMail-SL
/
MimePart+GPGMail.m
1940 lines (1698 loc) · 92.7 KB
/
MimePart+GPGMail.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* MimePart+GPGMail.m created by stephane on Mon 10-Jul-2000 */
/*
* Copyright (c) 2000-2008, Stéphane Corthésy <stephane at sente.ch>
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Stéphane Corthésy nor the names of GPGMail
* contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY STÉPHANE CORTHÉSY AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL STÉPHANE CORTHÉSY AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "MimePart+GPGMail.h"
#import "NSData+GPGMail.h"
#import "NSString+GPGMail.h"
#import "GPGMailBundle.h"
#import "GPGMailPatching.h"
#import "Message+GPGMail.h"
#import "NSObject+GPGMail.h"
#import "GPG.subproj/GPGHandler.h"
#import <MimeBody.h>
#import <NSData+Message.h>
#import <NSString+Message.h>
#import <MessageStore.h>
#import <MutableMessageHeaders.h>
#if defined(LEOPARD) || defined(TIGER)
#import <NSDataMessageStore.h>
#endif
#import <Foundation/Foundation.h>
#define DECRYPT_PGP_ATTACHMENTS 1
@implementation MimePart(GPGMail)
GPG_DECLARE_EXTRA_IVARS(MimePart)
- (NSData *) gpgFullBodyPartData
{
#warning FIXME: Does not work when (IMAP) account is offline!
// fullBodyDataForMessage: and dataForMimePart: return nil!!!
// -headerDataForMessage: works even offline
// -bodyDataForMessage: works even offline, but doesn't include attachment data!
NSData *bodyPartData;
// We cannot use [[[[self mimeBody] message] messageStore] dataForMimePart:self]
// because returned data doesn't have part's headers, and we need them to authenticate
// a part.
if([[self mimeBody] topLevelPart] == self){
bodyPartData = [[(Message *)[[self mimeBody] message] messageStore] fullBodyDataForMessage:[[self mimeBody] message]];
// bodyPartData = [[[[self mimeBody] message] messageStore] bodyDataForMessage:[[self mimeBody] message]]; // (no headers, but begins with line separator)
// if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"])
// bodyPartData = [bodyPartData decodeQuotedPrintableForText:YES];
}
else{
// Subpart of multipart
#if 0
NSString *boundary = [NSString stringWithFormat:@"\r\n--%@\r\n", [[self parentPart] bodyParameterForKey:@"boundary"]];
#else
#warning Check patch
// Patched by Tomio (January 20,2002)
NSString *boundary = [NSString stringWithFormat:@"--%@\r\n", [[self parentPart] bodyParameterForKey:@"boundary"]];
#endif
char bytes[2] = {0, 0};
#warning BUG if not on second level!
#warning BUG? We need to check if ends with CRLF
// bodyPartData = [[[self mimeBody] rawData] gpgStandardizedEOLsToCRLF];
bodyPartData = [[[(Message *)[[self mimeBody] message] messageStore] fullBodyDataForMessage:[[self mimeBody] message]] gpgStandardizedEOLsToCRLF]; // Forces download of attachments
if(bodyPartData == nil)
return nil;
#warning Does not return data for attachments!
[bodyPartData getBytes:bytes length:2];
if(bytes[0] != '\r' && bytes[1] != '\n'){
// There are cases where raw data begins directly with the boundary
// Thanks to Tomio Arisaka
NSMutableData *newData;
bytes[0] = '\r';
bytes[1] = '\n';
newData = [NSMutableData dataWithBytes:bytes length:2];
[newData appendData:bodyPartData];
bodyPartData = newData;
}
#warning Not sure that created boundary can be written with ASCII...
// NEW bug? It seems that sometimes(...) we get a range exception here!
#if 0
bodyPartData = [[bodyPartData componentsSeparatedByData:[boundary dataUsingEncoding:NSASCIIStringEncoding]] objectAtIndex:[[[self parentPart] subparts] indexOfObject:self] + 1]; // +1, because first element is empty
#else
{
int anIndex = [[[self parentPart] subparts] indexOfObject:self];
NSArray *components = [bodyPartData componentsSeparatedByData:[boundary dataUsingEncoding:NSASCIIStringEncoding]];
anIndex = MIN(anIndex, [[[self parentPart] subparts] count] - 1); // FIXME: Why this code? Useless!?
// anIndex = MIN(anIndex, [components count] - 1);
bodyPartData = [components objectAtIndex:anIndex + 1]; // +1, because first element is empty
}
#endif
// Patched by Tomio (January 20,2002)
#warning Check patch
{ // removes CRLF from the end of data
NSRange range = {0, [bodyPartData length] - 2};
bodyPartData = [bodyPartData subdataWithRange:range];
}
}
return bodyPartData;
}
/*!
* DEPRECATED - now we can use -bodyData
*/
- (NSData *) gpgBodyPartData
{
#if 0
NSData *bodyPartData = [[[[self mimeBody] message] messageStore] dataForMimePart:self];
if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"])
bodyPartData = [bodyPartData decodeQuotedPrintableForText:YES];
return bodyPartData;
#else
#warning Compare my solution against Tomio
NSData *bodyPartData;
if([[self mimeBody] topLevelPart] == self){
bodyPartData = [[(Message *)[[self mimeBody] message] messageStore] fullBodyDataForMessage:[[self mimeBody] message]]; // (no headers, but begins with line separator)
// bodyPartData = [[[[self mimeBody] message] messageStore] bodyDataForMessage:[[self mimeBody] message]]; // (no headers, but begins with line separator)
if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"])
bodyPartData = [bodyPartData decodeQuotedPrintableForText:YES];
else if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"base64"])
bodyPartData = [bodyPartData decodeBase64];
}
else{
// Subpart of multipart
#if 0 // patched by Tomio (November 29, 2001)(January 20,2002)
NSString *boundary = [NSString stringWithFormat:@"\r\n--%@\r\n", [[self parentPart] bodyParameterForKey:@"boundary"]];
bodyPartData = [[[[[self mimeBody] rawData] gpgStandardizedEOLsToCRLF] componentsSeparatedByData:[boundary dataUsingEncoding:NSASCIIStringEncoding]] objectAtIndex:[[[self parentPart] subparts] indexOfObject:self] + 1]; // +1, because first element is empty
#else
NSString *boundary = [NSString stringWithFormat:@"--%@\r\n", [[self parentPart] bodyParameterForKey:@"boundary"]];
bodyPartData = [[[[[self mimeBody] rawData] gpgStandardizedEOLsToCRLF] componentsSeparatedByData:[boundary dataUsingEncoding:NSASCIIStringEncoding]] objectAtIndex:[[[self parentPart] subparts] indexOfObject:self] + 1];
{ // removes CRLF from the end of data
NSRange range = {0, [bodyPartData length] - 2};
bodyPartData = [bodyPartData subdataWithRange:range];
}
// [bodyPartData writeToFile:@"/tmp/BodyPartData.txt" atomically:YES]; // debug
// NSRunAlertPanel(@"Debug", @"%@", nil, nil, nil, @"BodyPartData"); // debug
#endif
if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"])
bodyPartData = [bodyPartData decodeQuotedPrintableForText:YES];
else if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"base64"])
bodyPartData = [bodyPartData decodeBase64];
}
return bodyPartData;
#endif
}
- (NSData *) gpgBodyPartData2
{
if([[self subparts] count] == 0){
NSData *bodyPartData = [[(Message *)[[self mimeBody] message] messageStore] dataForMimePart:self];
if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"])
bodyPartData = [bodyPartData decodeQuotedPrintableForText:YES];
else if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"base64"])
bodyPartData = [bodyPartData decodeBase64];
return bodyPartData;
}
else{
/*
if([[self mimeBody] topLevelPart] == self){
bodyPartData = [[[[self mimeBody] message] messageStore] fullBodyDataForMessage:[[self mimeBody] message]]; // (no headers, but begins with line separator); forces download of all subparts if necessary
if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"])
bodyPartData = [bodyPartData decodeQuotedPrintableForText:YES];
}
else{
// Subpart of multipart
NSString *boundary = [NSString stringWithFormat:@"\r\n--%@\r\n", [[self parentPart] bodyParameterForKey:@"boundary"]];
bodyPartData = [[[[self mimeBody] message] messageStore] fullBodyDataForMessage:[[self mimeBody] message]]; // (no headers, but begins with line separator); forces download of all subparts if necessary
}*/
return nil;
}
}
- (BOOL) gpgIsOpenPGPEncryptedContainerPart
// Does not look recursively in sub-parts
{
if([[[self type] lowercaseString] isEqualToString:@"multipart"] && [[[self subtype] lowercaseString] isEqualToString:@"encrypted"])
#warning Workaround!
// Sometimes, we can't get the bodyParameterForKey:@"protocol",
// despite there is one! Let's ignore it...
if([self bodyParameterForKey:@"protocol"] == nil || [[[self bodyParameterForKey:@"protocol"] lowercaseString] isEqualToString:@"application/pgp-encrypted"])
// The multipart/encrypted MUST consist of exactly two parts
if([[self subparts] count] == 2){
BOOL recognizesVersion = NO;
MimePart *versionPart = [self subpartAtIndex:0];
MimePart *dataPart = [self subpartAtIndex:1];
// The first MIME body part must have a content type of "application/pgp-encrypted". This body contains the control information.
if([[[versionPart type] lowercaseString] isEqualToString:@"application"] && [[[versionPart subtype] lowercaseString] isEqualToString:@"pgp-encrypted"] && [[[dataPart type] lowercaseString] isEqualToString:@"application"] && ([[[dataPart subtype] lowercaseString] isEqualToString:@"octet-stream"] || [[[dataPart subtype] lowercaseString] isEqualToString:@"pgp-signature"])){
// Quirk mode: FireGPG < 0.7.1 declared 'application/pgp-signature' instead of 'application/octet-stream'
NSData *bodyData = [versionPart bodyData]; // (it doesn't matter if data has been decoded; normally there was nothing to decode)
if(bodyData){
// bodyData might be nil if mimePart has not yet been downloaded
// from server. We assume that part is PGP encrypted though.
//#warning Do we use the right encoding?
NSString *string = [[NSString alloc] initWithData:bodyData encoding:NSASCIIStringEncoding]; // [versionPart textEncoding] ?
NSString *lowercaseString = [string lowercaseString];
//NSLog(@"-[MimePart gpgIsOpenPGPEncryptedContainerPart]: [versionPart textEncoding] = %d = %@", [versionPart textEncoding], [NSString localizedNameOfStringEncoding:[versionPart textEncoding]]);
// A message complying with this standard MUST contain a "Version: 1" field in this body.
// NOTE: to insure compatibility with Mulberry, we need to check against "Version : 1" too
// Note also that "Version: 1" is not necessarily ended by a CR! (Ximian Evolution)
// EudoraGPG (Windows) adds one more newline after the "version" line; fix provided by Georg Wedemeyer
// FIXME: Wouldn't support versions 10 and later, or versions 1.x
if([lowercaseString rangeOfString:@"version: 1"].location != NSNotFound || [lowercaseString rangeOfString:@"version : 1"].location != NSNotFound)
recognizesVersion = YES;
[string release];
}
}
if(recognizesVersion)
return YES;
}
return NO;
}
- (BOOL) _gpgLooksLikeBinaryPGPAttachment
{
BOOL looksLikePGP = NO;
NSString *aType = [[self type] lowercaseString];
NSString *aSubtype = [[self subtype] lowercaseString];
if([aType isEqualToString:@"application"] && [aSubtype isEqualToString:@"octet-stream"]){
NSData *bodyData = [self bodyData]; // We use only the body (no headers), and decoded!
if(bodyData != nil){
// This is an octet stream. Is it a PGP-encrypted file?
// Let's do a pre-test: like magic(5), we check some bytes of the data. If it looks like PGP data,
// then we will try decryption.
unsigned length = [bodyData length];
unsigned char *bytes = (unsigned char *)[bodyData bytes];
if(length > 1){
if(bytes[0] == 0x0085 && bytes[1] == 0x02)
looksLikePGP = YES;
else if(bytes[0] == 0x00a6 && bytes[1] == 0x00)
looksLikePGP = YES;
else if(bytes[0] == 0x0085 && bytes[1] == 0x01) // According to http://lists.gnupg.org/pipermail/gnupg-devel/1999-September/016052.html
looksLikePGP = YES;
}
if(!looksLikePGP){
// If filename ends with .gpg, .pgp or .asc, we suppose it's a PGP file
NSString *pathExtension = [[[self attachmentFilename] pathExtension] lowercaseString];
if(pathExtension != nil && ([pathExtension isEqualToString:@"gpg"] || [pathExtension isEqualToString:@"pgp"] || [pathExtension isEqualToString:@"asc"]))
looksLikePGP = YES;
}
}
}
return looksLikePGP;
}
- (BOOL) _gpgIsNonOpenPGPEncryptedPart
{
// We accept only text/plain or application/octet-stream
// As with plain text messages, contains simple PGP encrypted block
NSData *bodyData;
NSString *aType = [[self type] lowercaseString];
NSString *aSubtype = [[self subtype] lowercaseString];
BOOL isPlainText = ([aType isEqualToString:@"text"] && [aSubtype isEqualToString:@"plain"]);
// We don't support text/html
// It's rather difficult, even if it's not impossible:
// We should search for armor in HTMLDocument
// If there is a text/plain alternative, then user can see decrypted message
if(!isPlainText){
BOOL isApplicationPGP = ([aType isEqualToString:@"application"] && [aSubtype isEqualToString:@"pgp"]);
if(isApplicationPGP && [[[self bodyParameterForKey:@"format"] lowercaseString] isEqualToString:@"text"]){
// Content-Type: application/pgp; format=text; x-action=encryptsign
// Content-Type: application/pgp; format=text; x-action=sign
// Content-Type: application/pgp; format=text
// Content-Type: application/pgp; format=text; x-action=encrypt
// Here we don't case about the x-action.
return YES;
}
else{
#if DECRYPT_PGP_ATTACHMENTS
if([self _gpgLooksLikeBinaryPGPAttachment]) // TODO: there are problems to fix with message body: message should cache decrypted message body, it's not part's task
return YES;
else
#endif
return NO;
}
}
// If message contains a plain text PGP attachment, don't consider the part to be encrypted;
// user will have to do it manually.
if([self isAttachment])
return NO;
bodyData = [self bodyData]; // We use only the body (no headers), and decoded!
if(bodyData != nil)
return [GPGHandler pgpEncryptionBlockRangeInData:[bodyData gpgStandardizedEOLsToCRLF]].location != NSNotFound;
else
return NO;
}
- (BOOL) gpgIsEncrypted
// Checks MIME headers/parameters (OpenPGP) and content, also in sub-parts
// Maybe we should NOT check deeply in sub-parts, as long as
// we cannot show block boundaries in viewer.
{
int i;
NSArray *parts;
// Special case for attached messages: if a message is embedded in the main message, don't look at
// that message content. User will see the embedded message as a separate message.
if([[[self type] lowercaseString] isEqualToString:@"message"] && [[[self subtype] lowercaseString] isEqualToString:@"rfc822"])
return NO;
if([self gpgIsOpenPGPEncryptedContainerPart])
return YES;
parts = [self subparts];
i = [parts count];
if(i > 0){
for(i = i - 1; i >= 0; i--)
if([[parts objectAtIndex:i] gpgIsEncrypted])
return YES;
}
// If we don't put the "else", it means that MIME message could have an armored
// PGP body extending over many parts!
else if([self _gpgIsNonOpenPGPEncryptedPart])
return YES;
return NO;
}
- (NSData *) _gpgDecryptedPGPMIMEDataWithPassphraseDelegate:(id)passphraseDelegate signatures:(NSArray **)signaturesPtr
{
// Only for OpenPGP/MIME
// It doesn't matter if decrypted data contains headers too.
NSData *bodyPartData = [self /*gpgBodyPartData2*/bodyData];
if(bodyPartData != nil){
GPGContext *aContext = [[GPGContext alloc] init];
GPGData *inputData = [[GPGData alloc] initWithData:bodyPartData];
if(GPGMailLoggingLevel & GPGMailDebug_SaveInputDataMask){
NSString *filename = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"asc"]];
if([bodyPartData writeToFile:filename atomically:NO])
NSLog(@"[DEBUG] Data to decrypt in %@", filename);
else
NSLog(@"[DEBUG] FAILED to write data to decrypt in %@", filename);
}
[aContext setPassphraseDelegate:passphraseDelegate];
[aContext setUsesArmor:YES];
[aContext setUsesTextMode:YES];
NS_DURING
GPGData *outputData = [aContext decryptedData:inputData signatures:signaturesPtr /*encoding:[self textEncoding]*/]; // Can raise an exception
NSData *decryptedData;
decryptedData = [[[outputData data] retain] autorelease]; // Because context will be released
if(signaturesPtr != NULL)
[[*signaturesPtr retain] autorelease]; // Because context will be released
[aContext release];
[inputData release];
NS_VALUERETURN(decryptedData, NSData *);
NS_HANDLER
[inputData release];
[aContext release];
[localException raise];
return nil;
NS_ENDHANDLER
}
else{
// Happens if part has not been downloaded
[NSException raise:NSGenericException format:NSLocalizedStringFromTableInBundle(@"DATA_NOT_AVAILABLE", @"GPGMail", [NSBundle bundleForClass:[GPGHandler class]], "")];
return nil;
}
#ifdef LEOPARD
return nil; // Never reached, but mutes gcc warning
#endif
}
- (NSData *) _gpgDecryptedInlineDataWithPassphraseDelegate:(id)passphraseDelegate signatures:(NSArray **)signaturesPtr
{
NSData *data = [self bodyData]; // At this stage, bodyData returns decoded data (if raw data was quoted-printable/base64)
NSRange pgpRange;
NSData *pgpData;
volatile NSData *decryptedData = nil;
GPGContext *aContext = [[GPGContext alloc] init];
GPGData *inputData;
BOOL isText;
// TODO: add support for multiple ASCII armors in same part
[aContext setPassphraseDelegate:passphraseDelegate];
#if DECRYPT_PGP_ATTACHMENTS
if([self _gpgLooksLikeBinaryPGPAttachment]){
pgpRange = NSMakeRange(0, [data length]);
pgpData = data;
[aContext setUsesArmor:NO];
[aContext setUsesTextMode:NO];
isText = NO;
}
else
#endif
{
data = [data gpgStandardizedEOLsToCRLF];
pgpRange = [GPGHandler pgpEncryptionBlockRangeInData:data];
NSAssert(pgpRange.location != NSNotFound, @"Not encrypted!");
pgpData = [data subdataWithRange:pgpRange];
[aContext setUsesArmor:YES];
[aContext setUsesTextMode:YES];
isText = YES;
}
inputData = [[GPGData alloc] initWithData:pgpData];
if(GPGMailLoggingLevel & GPGMailDebug_SaveInputDataMask){
NSString *filename = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:(isText ? @"asc":@"gpg")]];
if([pgpData writeToFile:filename atomically:NO])
NSLog(@"[DEBUG] Data to decrypt in %@", filename);
else
NSLog(@"[DEBUG] FAILED to write data to decrypt in %@", filename);
}
#warning CHECK: Do we support decrypted-then-verified messages?
NS_DURING
GPGData *outputData = [aContext decryptedData:inputData signatures:signaturesPtr /*encoding:[self textEncoding]*/]; // Can raise an exception
decryptedData = [[[outputData data] retain] autorelease]; // Because context will be released
if(signaturesPtr != NULL)
[[*signaturesPtr retain] autorelease]; // Because context will be released
NS_HANDLER
[inputData release];
[aContext release];
[localException raise];
NS_ENDHANDLER
[aContext release];
[inputData release];
NSMutableData *newEncodedBody = [NSMutableData dataWithCapacity:[data length]]; // Not the exact size, but approximately correct...
if(isText){
[newEncodedBody appendData:[data subdataWithRange:NSMakeRange(0, pgpRange.location)]];
[newEncodedBody appendData:(NSData *)decryptedData];
[newEncodedBody convertNetworkLineEndingsToUnix];
[newEncodedBody appendData:[data subdataWithRange:NSMakeRange(NSMaxRange(pgpRange), [data length] - NSMaxRange(pgpRange))]];
}
else
[newEncodedBody setData:(NSData *)decryptedData];
// Don't forget to reencode data according to content-transfer-encoding!
if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"])
[newEncodedBody setData:[newEncodedBody encodeQuotedPrintableForText:YES]];
else if([[[self contentTransferEncoding] lowercaseString] isEqualToString:@"base64"])
[newEncodedBody setData:[newEncodedBody encodeBase64]];
/* {
// HACK: Just to tell that there is no header part...
// We need to do this, because of the way we handle data later.
NSMutableData *prefixData = [[NSMutableData alloc] initWithData:[@"\n" dataUsingEncoding:NSASCIIStringEncoding]];
[prefixData appendData:newEncodedBody];
[newEncodedBody setData:prefixData];
[prefixData release];
}*/
return newEncodedBody;
}
- (void) _gpgDecryptInlineDataWithPassphraseDelegate:(id)passphraseDelegate signatures:(NSArray **)signaturesPtr intoData:(NSMutableData *)decryptedData fromData:(NSData *)fullBodyData
{
NSData *partDecryptedData = [self _gpgDecryptedInlineDataWithPassphraseDelegate:passphraseDelegate signatures:signaturesPtr];
if(partDecryptedData != nil){
NSRange partRange = [self range];
[decryptedData setData:[fullBodyData subdataWithRange:NSMakeRange(0, partRange.location)]];
[decryptedData appendData:partDecryptedData];
[decryptedData appendData:[fullBodyData subdataWithRange:NSMakeRange(NSMaxRange(partRange), [fullBodyData length] - (NSMaxRange(partRange)))]];
}
}
/*!
* Invoked for part even when not directly encrypted/signed. In this case, must forward message to subparts.
*/
- (NSData *) gpgDecryptedDataWithPassphraseDelegate:(id)passphraseDelegate signatures:(NSArray **)signaturesPtr
// We would give back the signatures (or nil) of the latest part!
// Returning nil means that we could not decrypt data
{
NSArray *parts = [self subparts];
int partCount = [parts count];
if([self gpgIsOpenPGPEncryptedContainerPart]){
// The second MIME body part MUST contain the actual encrypted data. It must be labeled with a content type of "application/octet-stream".
MimePart *aPart = [parts objectAtIndex:1];
NSData *decryptedData = [aPart _gpgDecryptedPGPMIMEDataWithPassphraseDelegate:passphraseDelegate signatures:signaturesPtr]; // Can raise an exception
#warning Do we really need to convert CRLF to LF as Tomio does?
// Signature authentication should do it by itself
// TODO: support recursive decryption - embedded encrypted parts. Decrypted part could contain encrypted sub-parts too.
// MimePart *decryptedPart = [MimePart bodyPartWithData:decryptedData];
//
// if([decryptedPart gpgIsEncrypted])
// return [decryptedPart gpgDecryptedDataForReceiver:receiver passphrase:passphrase signatures:signaturesPtr];
// else
return decryptedData;
}
else if(partCount > 0){
int anIndex;
NSMutableData *decryptedData = nil;
NSData *fullBodyData = nil;
for(anIndex = 0; anIndex < partCount; anIndex++){
MimePart *aPart = [parts objectAtIndex:anIndex];
if([aPart _gpgIsNonOpenPGPEncryptedPart]){
if(!fullBodyData){
fullBodyData = [[(Message *)[[self mimeBody] message] messageStore] fullBodyDataForMessage:[[self mimeBody] message]];
decryptedData = [NSMutableData dataWithData:fullBodyData];
}
[aPart _gpgDecryptInlineDataWithPassphraseDelegate:passphraseDelegate signatures:signaturesPtr intoData:decryptedData fromData:fullBodyData];
#if 0
NSData *partDecryptedData = [aPart _gpgDecryptedInlineDataWithPassphraseDelegate:passphraseDelegate signatures:signaturesPtr];
if(partDecryptedData != nil){
NSRange partRange = [aPart range];
[decryptedData setData:[fullBodyData subdataWithRange:NSMakeRange(0, partRange.location)]];
[decryptedData appendData:partDecryptedData];
[decryptedData appendData:[fullBodyData subdataWithRange:NSMakeRange(NSMaxRange(partRange), [fullBodyData length] - (NSMaxRange(partRange)))]];
}
#endif
}
#warning No support yet for deeper levels...
}
if(!decryptedData){
#warning NOT HANDLED (embedded encrypted parts)
// NSBeep();
NSLog(@"### GPGMail: unable (yet) to decrypt embedded MIME parts");
/* for(i = i - 1; i >= 0; i--){
[[parts objectAtIndex:i] gpgDecryptedDataForReceiver:receiver passphrase:passphrase signatures:signaturesPtr];
}*/
// We need to recreate sub-parts...
[NSException raise:NSInternalInconsistencyException format:NSLocalizedStringFromTableInBundle(@"UNSUPPORTED_ENCRYPTION_FORMAT", @"GPGMail", [NSBundle bundleForClass:[GPGHandler class]], "")];
// return nil; // Let's return original data as we cannot decrypt it
}
else{
/* // HACK: Just to tell that there is no header part...
// We need to do this, because of the way we handle data later.
NSMutableData *prefixData = [[NSMutableData alloc] initWithData:[@"\n" dataUsingEncoding:NSASCIIStringEncoding]];
[prefixData appendData:decryptedData];
[decryptedData setData:prefixData];
[prefixData release];*/
}
return decryptedData;
}
else if([self _gpgIsNonOpenPGPEncryptedPart])
return [self _gpgDecryptedInlineDataWithPassphraseDelegate:passphraseDelegate signatures:signaturesPtr];
else
return nil; // Let's return original data as we cannot decrypt it
}
- (BOOL) gpgIsOpenPGPSignedContainerPart
// Checks MIME headers/parameters, in this part only
{
MimePart *aPart;
NSArray *parts;
NSString *hashAlgorithmName;
// OpenPGP signed messages are denoted by the "multipart/signed" content
// type, described in [2], with a "protocol" parameter which MUST have a
// value of "application/pgp-signature" (MUST be quoted).
if(![[[self type] lowercaseString] isEqualToString:@"multipart"] && ![[[self subtype] lowercaseString] isEqualToString:@"signed"])
return NO; // We don't go further in subparts in this method
#warning Workaround!
// Sometimes, we can't get the bodyParameterForKey:@"protocol",
// despite there is one! Let's ignore it...
if([self bodyParameterForKey:@"protocol"] && ![[[self bodyParameterForKey:@"protocol"] lowercaseString] isEqualToString:@"application/pgp-signature"])
return NO;
// The "micalg" parameter for the "application/pgp-signature" protocol
// MUST contain exactly one hash-symbol of the format "pgp-<hash-
// identifier>", where <hash-identifier> identifies the Message
// Integrity Check (MIC) algorithm used to generate the signature.
hashAlgorithmName = [[self bodyParameterForKey:@"micalg"] lowercaseString];
// To insure compatibility with Sylpheed, we don't consider it an error
// if micalg is missing (anyway, we don't need to pass it to gpg).
#warning Workaround!
// Sometimes, we can't get the bodyParameterForKey:@"micalg",
// despite there is one! Let's ignore it...
#if 0
if(hashAlgorithmName){
if(![hashAlgorithmName hasPrefix:@"pgp-"])
return NO;
else
hashAlgorithmName = [hashAlgorithmName substringFromIndex:4];
if(![[[GPGHandler handler] knownHashAlgorithms] containsObject:hashAlgorithmName])
return NO;
}
#endif
parts = [self subparts];
// The multipart/signed body MUST consist of exactly two parts.
if([parts count] != 2)
return NO;
// The second body MUST contain the OpenPGP digital signature. It
// MUST be labeled with a content type of "application/pgp-signature".
aPart = [parts objectAtIndex:1];
if([[[aPart type] lowercaseString] isEqualToString:@"application"] && [[[aPart subtype] lowercaseString] isEqualToString:@"pgp-signature"])
return YES;
else
return NO;
}
- (BOOL) _gpgIsNonOpenPGPSignedPart
{
// As with plain text messages, contains simple PGP signed block
NSData *bodyData;
// We don't support text/html
// It's rather difficult, even if it's not impossible:
// We should search for armor in HTMLDocument
// If there is a text/plain alternative, then user can see decrypted message
if(([self type] && ![[[self type] lowercaseString] isEqualToString:@"text"]) || ([self subtype] && ![[[self subtype] lowercaseString] isEqualToString:@"plain"]))
#warning Test new recognition
if(([self type] && ![[[self type] lowercaseString] isEqualToString:@"application"]) || ([self subtype] && ![[[self subtype] lowercaseString] isEqualToString:@"pgp"]))
return NO;
// If message contains a plain text PGP attachment, don't consider the part to be a PGP part;
// user will have to do it manually.
if([self isAttachment])
return NO;
// In non-OpenPGP case, we assume that mailer signed displayed data,
// i.e. non quoted-printable/base64 data
bodyData = [[self bodyData] gpgStandardizedEOLsToCRLF]; // WARNING: in this case can contain 8bit characters
#warning Assertion not respected if body not loaded?
#warning ADDED TEST
#if 0
NSParameterAssert(bodyData != nil);
return [GPGHandler pgpSignatureBlockRangeInData:bodyData].location != NSNotFound;
#else
if(bodyData != nil)
return [GPGHandler pgpSignatureBlockRangeInData:bodyData].location != NSNotFound;
else
return NO;
#endif
}
- (BOOL) gpgHasSignature
// Checks MIME headers/parameters (OpenPGP) and content, also in sub-parts
// Maybe we should NOT check deeply in sub-parts, as long as
// we cannot show block boundaries in viewer.
{
int i;
NSArray *parts;
// Special case for attached messages: if a message is embedded in the main message, don't look at
// that message content. User will see the embedded message as a separate message.
if([[[self type] lowercaseString] isEqualToString:@"message"] && [[[self subtype] lowercaseString] isEqualToString:@"rfc822"])
return NO;
if([self gpgIsOpenPGPSignedContainerPart])
return YES;
parts = [self subparts];
i = [parts count];
if(i > 0){
for(i = i - 1; i >= 0; i--)
if([[parts objectAtIndex:i] gpgHasSignature])
return YES;
}
// If we don't put the "else", it means that signature could extend on more that one part?!?
else if([self _gpgIsNonOpenPGPSignedPart])
return YES;
return NO;
}
- (BOOL) gpgIsOpenPGPKeyPart
{
return ([[[self type] lowercaseString] isEqualToString:@"application"] && [[[self subtype] lowercaseString] isEqualToString:@"pgp-keys"]);
}
- (BOOL) _gpgIsNonOpenPGPKeyPart
{
// As with plain text messages, contains simple PGP signed block
NSData *bodyData;
// We don't support text/html
// It's rather difficult, even if it's not impossible:
// We should search for armor in HTMLDocument
// If there is a text/plain alternative, then user can see decrypted message
if(([self type] && ![[[self type] lowercaseString] isEqualToString:@"text"]) || ([self subtype] && ![[[self subtype] lowercaseString] isEqualToString:@"plain"]))
#warning Test new recognition
if(([self type] && ![[[self type] lowercaseString] isEqualToString:@"application"]) || ([self subtype] && ![[[self subtype] lowercaseString] isEqualToString:@"pgp"]))
return NO;
#if 0
// If message contains a plain text PGP attachment, don't consider the part to be a PGP part;
// user will have to do it manually.
if([self isAttachment])
return NO;
#endif
// In non-OpenPGP case, we assume that mailer signed displayed data,
// i.e. non quoted-printable/base64 data
bodyData = [[self bodyData] gpgStandardizedEOLsToCRLF]; // WARNING: in this case can contain 8bit characters
#warning Assertion not respected if body not loaded?
#warning ADDED TEST
#if 0
NSParameterAssert(bodyData != nil);
return [GPGHandler pgpPublicKeyBlockRangeInData:bodyData].location != NSNotFound;
#else
if(bodyData != nil)
return [GPGHandler pgpPublicKeyBlockRangeInData:bodyData].location != NSNotFound;
else
return NO;
#endif
}
- (BOOL) gpgContainsKeyBlock
{
int i;
NSArray *parts;
if([self gpgIsOpenPGPKeyPart])
return YES;
parts = [self subparts];
i = [parts count];
if(i > 0){
for(i = i - 1; i >= 0; i--)
if([[parts objectAtIndex:i] gpgContainsKeyBlock])
return YES;
}
// If we don't put the "else", it means that signature could extend on more that one part?!?
else if([self _gpgIsNonOpenPGPKeyPart])
return YES;
return NO;
}
- (BOOL) gpgAllAttachmentsAreAvailable
{
NSArray *subparts = [self subparts];
int subpartsCount = [subparts count];
if(subpartsCount == 0)
// Using [[[[self mimeBody] message] messageStore] dataForMimePart:self]
// always returns nil for .sig attachments! Because they are attachments,
// not displayed inline??
return [[(Message *)[[self mimeBody] message] messageStore] hasCachedDataForMimePart:self];
else{
// A multipart part never has anything in the cache
for(--subpartsCount; subpartsCount >= 0; subpartsCount--)
if(![[subparts objectAtIndex:subpartsCount] gpgAllAttachmentsAreAvailable])
return NO;
return YES;
}
}
- (GPGSignature *) gpgAuthenticationSignature
{
NSArray *parts = [self subparts];
int i = [parts count];
if([self gpgIsOpenPGPSignedContainerPart]){
MimePart *aPart;
NSData *signedData;
NSString *signatureFile = nil;
GPGContext *aContext = [[GPGContext alloc] init];
GPGData *inputData, *signatureInputData;
NSString *aTempFile;
// We can not use _attachDir and -fileWrapperForBody, because we flushed data => _attachDir is nil
// We can anyway create a temporary file, for example in /tmp,
// which will contain the signature.
aTempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSProcessInfo processInfo] globallyUniqueString]];
signatureFile = [aTempFile stringByAppendingPathExtension:@"sig"];
#if 1
aPart = [parts objectAtIndex:1];
NSAssert1([aPart gpgSaveBodyToFile:signatureFile], @"Unable to save temporary file %@", signatureFile);
signedData = [NSMutableData dataWithContentsOfFile:signatureFile];
[(NSMutableData *)signedData gpgNormalizeDataForVerifying];
NSAssert1([signedData writeToFile:signatureFile atomically:YES], @"Unable to save (again) temporary file %@", signatureFile);
aPart = [parts objectAtIndex:0];
signedData = [[aPart gpgFullBodyPartData] gpgNormalizedDataForVerifying];
#warning We should ensure that it is not decoded if it was quoted-printable/base64??
#else
#warning Test patch from Tomio
{
#warning BUG! Does not take data for attachments!!!
NSData *bodyData;
NSString *aString, *boundary;
NSStringEncoding originalEncoding;
NSRange range;
unsigned int point;
aPart = [parts objectAtIndex:0]; // text part
aString = [aPart bodyParameterForKey:@"charset"];
// considers whether "Content-Transfer-Encoding" is "quoted-printable" or "base64"
originalEncoding = NSISOLatin1StringEncoding;
if(![[[aPart contentTransferEncoding] lowercaseString] isEqualToString:@"quoted-printable"]){
if(aString != nil)
originalEncoding = [NSString gpgEncodingForMIMECharset:aString];
bodyData = [[aPart /*gpgBodyPartData*/bodyData] gpgNormalizedDataForVerifying];
}else if(![[[aPart contentTransferEncoding] lowercaseString] isEqualToString:@"base64"]){
if(aString != nil)
originalEncoding = [NSString gpgEncodingForMIMECharset:aString];
bodyData = [[aPart /*gpgBodyPartData*/bodyData] gpgNormalizedDataForVerifying];
}else{
#warning Unknown method!!!
// bodyData = [[aPart gpgBodyPartDataWithoutDecodeQuotePrintable] gpgNormalizedDataForVerifying];
bodyData = [[aPart /*gpgBodyPartData*/bodyData] gpgNormalizedDataForVerifying];
}
aString = [[NSString alloc] initWithData:bodyData encoding:originalEncoding];
boundary = [NSString stringWithFormat:@"--%@\r\n", [[aPart parentPart] bodyParameterForKey:@"boundary"]];
range = [aString rangeOfString:boundary];
point = range.location + range.length;
if(point > [aString length])
point = 0;
bodyData = [[aString substringFromIndex:point] dataUsingEncoding:originalEncoding]; // removes a line-separator
[aString release];
aPart = [parts objectAtIndex:1]; // signed part
signedData = [[aPart bodyData] gpgStandardizedEOLsToCRLF];
// Stephane: no, encoding can be different; why does it need to go to string, back to data??
// aString = [[NSString alloc] initWithData:signedData encoding:originalEncoding];
// signedData = [aString dataUsingEncoding:originalEncoding];
NSAssert1([signedData writeToFile:signatureFile atomically:YES], @"Unable to save temporary file %@", signatureFile);
[aString release];
signedData = bodyData;
}
#endif
inputData = [[GPGData alloc] initWithData:signedData];
if(GPGMailLoggingLevel & GPGMailDebug_SaveInputDataMask){
NSString *signedDataFile = [aTempFile stringByAppendingPathExtension:@"asc"];
if([signedData writeToFile:signedDataFile atomically:NO])
NSLog(@"[DEBUG] Data to verify in %@", signedDataFile);
else
NSLog(@"[DEBUG] FAILED to write data to verify in %@", signedDataFile);
}
signatureInputData = [[GPGData alloc] initWithContentsOfFile:signatureFile];
[aContext setUsesArmor:YES];
[aContext setUsesTextMode:YES];
NS_DURING
GPGSignature *aSignature;
(void)[aContext verifySignatureData:signatureInputData againstData:inputData /*encoding:[self textEncoding]*/]; // Can raise an exception
if(!(GPGMailLoggingLevel & GPGMailDebug_SaveInputDataMask))
(void)[[NSFileManager defaultManager] removeFileAtPath:signatureFile handler:nil];
aSignature = [[[[aContext signatures] lastObject] retain] autorelease]; // Because context will be released
#warning Workaround for gpgme 0.4.x bug: does not return an error when CRC or BADARMOR error!
if(aSignature == nil)
[[NSException exceptionWithName:GPGException reason:[[GPGMailBundle sharedInstance] gpgErrorDescription:GPGErrorChecksumError] userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:GPGErrorChecksumError], GPGErrorKey, nil]] raise];
[aContext release];
[inputData release];
[signatureInputData release];
NS_VALUERETURN(aSignature, GPGSignature *);
NS_HANDLER
if(!(GPGMailLoggingLevel & GPGMailDebug_SaveInputDataMask))
(void)[[NSFileManager defaultManager] removeFileAtPath:signatureFile handler:nil];
[inputData release];
[signatureInputData release];
if([[localException name] isEqualToString:GPGException]){
NSArray *signatures = [[[aContext signatures] retain] autorelease]; // Because context will be released
[aContext release];
if([signatures count])
// In this case, signature will contain the exception in its status
return [signatures lastObject];
}
else
[aContext release];
[localException raise];
return nil;
NS_ENDHANDLER
}
else if(i > 0){
// Not handled correctly... We should retrieve a dict with sigs per part.
// Currently we stop on the first retrieved sig (from the end!)
for(i = i - 1; i >= 0; i--){
GPGSignature *aSignature = [[parts objectAtIndex:i] gpgAuthenticationSignature]; // Can raise an exception
if(aSignature != nil)
return aSignature;
}
return nil;
}
// Is the "else" really necessary?!
// If we don't put the "else", it means that signature could extend on more that one part?!?
else if([self _gpgIsNonOpenPGPSignedPart]){
// In non-OpenPGP case, we assume that mailer signed displayed data,
// i.e. non quoted-printable/base64 data
NSData *data = [self bodyData]; // At this stage, bodyData returns decoded data (if raw data was quoted-printable/base64)
NSRange pgpRange;
NSData *pgpData;
GPGHandler *aHandler;
GPGContext *aContext = [[GPGContext alloc] init];
GPGData *inputData;
#warning WARNING By converting EOL to CRLF, we break signature! I thought EOLs were not used...
data = [data gpgStandardizedEOLsToCRLF]; // WARNING: in this case can contain 8bit characters
pgpRange = [GPGHandler pgpSignatureBlockRangeInData:data]; // Expects CRLF EOLs
NSAssert(pgpRange.location != NSNotFound, @"Not signed!");
pgpData = [data subdataWithRange:pgpRange];
pgpData = [pgpData gpgStandardizedEOLsToLF]; // Back to LF??
aHandler = [GPGHandler handler];
// There was a bug in previous GPGMailv11; some signed messages would have
// an invalid signature now...
// First we try the right way, and if it doesn't work, we try the old way.
// Problem happens with messages containing non-ASCII characters.
inputData = [[GPGData alloc] initWithData:pgpData];
if(GPGMailLoggingLevel & GPGMailDebug_SaveInputDataMask){
NSString *filename = [NSTemporaryDirectory() stringByAppendingPathComponent:[[[NSProcessInfo processInfo] globallyUniqueString] stringByAppendingPathExtension:@"asc"]];
if([pgpData writeToFile:filename atomically:NO])
NSLog(@"[DEBUG] Data to verify in %@", filename);
else
NSLog(@"[DEBUG] FAILED to write data to verify in %@", filename);
}
[aContext setUsesArmor:YES];
[aContext setUsesTextMode:YES];
#if 0
NS_DURING
// The following call should be enough...
(void)[aContext verifySignedData:inputData /*encoding:[self textEncoding]*/]; // Can raise an exception
[inputData release];
inputData = nil;
NS_VALUERETURN([[[aContext autorelease] signatures] lastObject], GPGSignature *);
NS_HANDLER
NSException *originalException = localException;
volatile NSArray *originalSignatures = [[[aContext signatures] retain] autorelease]; // Because context will be released
data = [[self bodyData] gpgStandardizedEOLsToCRLF];
pgpRange = [GPGHandler pgpSignatureBlockRangeInData:data];
NSAssert(pgpRange.location != NSNotFound, @"Not signed!");
pgpData = [data subdataWithRange:pgpRange];
NS_DURING
NSArray *verificationSignatures;
[inputData release];
inputData = [[GPGData alloc] initWithData:pgpData];
verificationSignatures = [aContext verifySignedData:inputData /*encoding:[self textEncoding]*/]; // Can raise an exception
[[verificationSignatures retain] autorelease]; // Because context will be released