Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F18825745
pgpmime.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
26 KB
Subscribers
None
pgpmime.c
View Options
/* pgpmime.c - Try to handle PGP/MIME for Outlook
* Copyright (C) 2005 g10 Code GmbH
*
* This file is part of GPGol.
*
* GPGol is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* GPGol 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
EXPLAIN what we are doing here.
*/
#ifdef HAVE_CONFIG_H
#include
<config.h>
#endif
#include
<stdio.h>
#include
<stdlib.h>
#include
<assert.h>
#include
<string.h>
#define COBJMACROS
#include
<windows.h>
#include
<objidl.h>
/* For IStream. */
#include
<gpgme.h>
#include
"mymapi.h"
#include
"mymapitags.h"
#include
"rfc822parse.h"
#include
"intern.h"
#include
"util.h"
#include
"pgpmime.h"
#include
"engine.h"
/* The maximum length of a line we ar able to porcess. RFC822 alows
only for 1000 bytes; thus 2000 seems to be a reasonable value. */
#define LINEBUFSIZE 2000
/* The reverse base-64 list used for base-64 decoding. */
static
unsigned
char
const
asctobin
[
256
]
=
{
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0x3e
,
0xff
,
0xff
,
0xff
,
0x3f
,
0x34
,
0x35
,
0x36
,
0x37
,
0x38
,
0x39
,
0x3a
,
0x3b
,
0x3c
,
0x3d
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0x00
,
0x01
,
0x02
,
0x03
,
0x04
,
0x05
,
0x06
,
0x07
,
0x08
,
0x09
,
0x0a
,
0x0b
,
0x0c
,
0x0d
,
0x0e
,
0x0f
,
0x10
,
0x11
,
0x12
,
0x13
,
0x14
,
0x15
,
0x16
,
0x17
,
0x18
,
0x19
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0x1a
,
0x1b
,
0x1c
,
0x1d
,
0x1e
,
0x1f
,
0x20
,
0x21
,
0x22
,
0x23
,
0x24
,
0x25
,
0x26
,
0x27
,
0x28
,
0x29
,
0x2a
,
0x2b
,
0x2c
,
0x2d
,
0x2e
,
0x2f
,
0x30
,
0x31
,
0x32
,
0x33
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
,
0xff
};
/* The context object we use to track information. */
struct
pgpmime_context
{
HWND
hwnd
;
/* A window handle to be used for message boxes etc. */
rfc822parse_t
msg
;
/* The handle of the RFC822 parser. */
int
preview
;
/* Do only decryption and pop up no message bozes. */
int
verify_mode
;
/* True if we want to verify a PGP/MIME signature. */
int
nesting_level
;
/* Current MIME nesting level. */
int
in_data
;
/* We are currently in data (body or attachment). */
gpgme_data_t
signed_data
;
/* NULL or the data object used to collect
the signed data. It would bet better to
just hash it but there is no support in
gpgme for this yet. */
gpgme_data_t
sig_data
;
/* NULL or data object to collect the
signature attachment which should be a
signature then. */
gpgme_data_t
body
;
/* NULL or a data object used to collect the
body part we are going to display later. */
int
collect_body
;
/* True if we are collecting the body lines. */
int
collect_attachment
;
/* True if we are collecting an attachment. */
int
collect_signeddata
;
/* True if we are collecting the signed data. */
int
collect_signature
;
/* True if we are collecting a signature. */
int
start_hashing
;
/* Flag used to start collecting signed data. */
int
hashing_level
;
/* MIME level where we started hashing. */
int
is_qp_encoded
;
/* Current part is QP encoded. */
int
is_base64_encoded
;
/* Current part is base 64 encoded. */
int
is_utf8
;
/* Current part has charset utf-8. */
int
part_counter
;
/* Counts the number of processed parts. */
char
*
filename
;
/* Current filename (malloced) or NULL. */
LPSTREAM
outstream
;
/* NULL or a stream to write a part to. */
/* Helper to keep the state of the base64 decoder. */
struct
{
int
idx
;
unsigned
char
val
;
int
stop_seen
;
int
invalid_encoding
;
}
base64
;
int
line_too_long
;
/* Indicates that a received line was too long. */
int
parser_error
;
/* Indicates that we encountered a error from
the parser. */
/* Buffer used to constructed complete files. */
size_t
linebufsize
;
/* The allocated size of the buffer. */
size_t
linebufpos
;
/* The actual write posituion. */
char
linebuf
[
1
];
/* The buffer. */
};
typedef
struct
pgpmime_context
*
pgpmime_context_t
;
/* This function is a wrapper around gpgme_data_write to convert the
data to utf-8 first. We assume Latin-1 here. */
static
int
latin1_data_write
(
gpgme_data_t
data
,
const
char
*
line
,
size_t
len
)
{
const
char
*
s
;
char
*
buffer
,
*
p
;
size_t
i
,
n
;
int
rc
;
for
(
s
=
line
,
i
=
0
,
n
=
0
;
i
<
len
;
s
++
,
i
++
)
{
n
++
;
if
(
*
s
&
0x80
)
n
++
;
}
buffer
=
xmalloc
(
n
+
1
);
for
(
s
=
line
,
i
=
0
,
p
=
buffer
;
i
<
len
;
s
++
,
i
++
)
{
if
(
*
s
&
0x80
)
{
*
p
++
=
0xc0
|
((
*
s
>>
6
)
&
3
);
*
p
++
=
0x80
|
(
*
s
&
0x3f
);
}
else
*
p
++
=
*
s
;
}
assert
(
p
-
buffer
==
n
);
rc
=
gpgme_data_write
(
data
,
buffer
,
n
);
xfree
(
buffer
);
return
rc
;
}
/* Do in-place decoding of quoted-printable data of LENGTH in BUFFER.
Returns the new length of the buffer. */
static
size_t
qp_decode
(
char
*
buffer
,
size_t
length
)
{
char
*
d
,
*
s
;
for
(
s
=
d
=
buffer
;
length
;
length
--
)
if
(
*
s
==
'='
&&
length
>
2
&&
hexdigitp
(
s
+
1
)
&&
hexdigitp
(
s
+
2
))
{
s
++
;
*
(
unsigned
char
*
)
d
++
=
xtoi_2
(
s
);
s
+=
2
;
length
-=
2
;
}
else
*
d
++
=
*
s
++
;
return
d
-
buffer
;
}
/* Do in-place decoding of base-64 data of LENGTH in BUFFER. Returns
the new length of the buffer. CTX is required to return errors and
to maintain state of the decoder. */
static
size_t
base64_decode
(
pgpmime_context_t
ctx
,
char
*
buffer
,
size_t
length
)
{
int
idx
=
ctx
->
base64
.
idx
;
unsigned
char
val
=
ctx
->
base64
.
val
;
int
c
;
char
*
d
,
*
s
;
if
(
ctx
->
base64
.
stop_seen
)
return
0
;
for
(
s
=
d
=
buffer
;
length
;
length
--
,
s
++
)
{
if
(
*
s
==
'\n'
||
*
s
==
' '
||
*
s
==
'\r'
||
*
s
==
'\t'
)
continue
;
if
(
*
s
==
'='
)
{
/* Pad character: stop */
if
(
idx
==
1
)
*
d
++
=
val
;
ctx
->
base64
.
stop_seen
=
1
;
break
;
}
if
((
c
=
asctobin
[
*
(
unsigned
char
*
)
s
])
==
255
)
{
if
(
!
ctx
->
base64
.
invalid_encoding
)
log_debug
(
"%s: invalid base64 character %02X at pos %d skipped
\n
"
,
__func__
,
*
(
unsigned
char
*
)
s
,
(
int
)(
s
-
buffer
));
ctx
->
base64
.
invalid_encoding
=
1
;
continue
;
}
switch
(
idx
)
{
case
0
:
val
=
c
<<
2
;
break
;
case
1
:
val
|=
(
c
>>
4
)
&
3
;
*
d
++
=
val
;
val
=
(
c
<<
4
)
&
0xf0
;
break
;
case
2
:
val
|=
(
c
>>
2
)
&
15
;
*
d
++
=
val
;
val
=
(
c
<<
6
)
&
0xc0
;
break
;
case
3
:
val
|=
c
&
0x3f
;
*
d
++
=
val
;
break
;
}
idx
=
(
idx
+
1
)
%
4
;
}
ctx
->
base64
.
idx
=
idx
;
ctx
->
base64
.
val
=
val
;
return
d
-
buffer
;
}
/* Print the message event EVENT. */
static
void
debug_message_event
(
pgpmime_context_t
ctx
,
rfc822parse_event_t
event
)
{
const
char
*
s
;
switch
(
event
)
{
case
RFC822PARSE_OPEN
:
s
=
"Open"
;
break
;
case
RFC822PARSE_CLOSE
:
s
=
"Close"
;
break
;
case
RFC822PARSE_CANCEL
:
s
=
"Cancel"
;
break
;
case
RFC822PARSE_T2BODY
:
s
=
"T2Body"
;
break
;
case
RFC822PARSE_FINISH
:
s
=
"Finish"
;
break
;
case
RFC822PARSE_RCVD_SEEN
:
s
=
"Rcvd_Seen"
;
break
;
case
RFC822PARSE_LEVEL_DOWN
:
s
=
"Level_Down"
;
break
;
case
RFC822PARSE_LEVEL_UP
:
s
=
"Level_Up"
;
break
;
case
RFC822PARSE_BOUNDARY
:
s
=
"Boundary"
;
break
;
case
RFC822PARSE_LAST_BOUNDARY
:
s
=
"Last_Boundary"
;
break
;
case
RFC822PARSE_BEGIN_HEADER
:
s
=
"Begin_Header"
;
break
;
case
RFC822PARSE_PREAMBLE
:
s
=
"Preamble"
;
break
;
case
RFC822PARSE_EPILOGUE
:
s
=
"Epilogue"
;
break
;
default
:
s
=
"[unknown event]"
;
break
;
}
log_debug
(
"%s: ctx=%p, rfc822 event %s
\n
"
,
SRCNAME
,
ctx
,
s
);
}
/* This routine gets called by the RFC822 parser for all kind of
events. OPAQUE carries in our case a pgpmime context. Should
return 0 on success or -1 and as well as seeting errno on
failure. */
static
int
message_cb
(
void
*
opaque
,
rfc822parse_event_t
event
,
rfc822parse_t
msg
)
{
pgpmime_context_t
ctx
=
opaque
;
HRESULT
hr
;
debug_message_event
(
ctx
,
event
);
if
(
event
==
RFC822PARSE_BEGIN_HEADER
||
event
==
RFC822PARSE_T2BODY
)
{
/* We need to check here whether to start collecting signed data
because attachments might come without header lines and thus
we won't see the BEGIN_HEADER event. */
if
(
ctx
->
start_hashing
==
1
)
{
ctx
->
start_hashing
=
2
;
ctx
->
hashing_level
=
ctx
->
nesting_level
;
ctx
->
collect_signeddata
=
1
;
gpgme_data_new
(
&
ctx
->
signed_data
);
}
}
if
(
event
==
RFC822PARSE_T2BODY
)
{
rfc822parse_field_t
field
;
const
char
*
s1
,
*
s2
,
*
s3
;
size_t
off
;
char
*
p
;
int
is_text
=
0
;
ctx
->
is_utf8
=
0
;
field
=
rfc822parse_parse_field
(
msg
,
"Content-Type"
,
-1
);
if
(
field
)
{
s1
=
rfc822parse_query_media_type
(
field
,
&
s2
);
if
(
s1
)
{
log_debug
(
"%s: ctx=%p, media `%s' `%s'
\n
"
,
SRCNAME
,
ctx
,
s1
,
s2
);
if
(
!
strcmp
(
s1
,
"multipart"
))
{
/* We don't care about the top level multipart layer
but wait until it comes to the actual parts which
then will get stored as attachments.
For now encapsulated signed or encrypted
containers are not processed in a special way as
they should. Except for the simple verify
mode. */
if
(
ctx
->
verify_mode
&&
!
ctx
->
signed_data
&&
!
strcmp
(
s2
,
"signed"
)
&&
(
s3
=
rfc822parse_query_parameter
(
field
,
"protocol"
,
0
))
&&
!
strcmp
(
s3
,
"application/pgp-signature"
))
{
/* Need to start the hashing after the next
boundary. */
ctx
->
start_hashing
=
1
;
}
}
else
if
(
!
strcmp
(
s1
,
"text"
))
{
is_text
=
1
;
}
else
if
(
ctx
->
verify_mode
&&
ctx
->
nesting_level
==
1
&&
!
ctx
->
sig_data
&&
!
strcmp
(
s1
,
"application"
)
&&
!
strcmp
(
s2
,
"pgp-signature"
))
{
/* This is the second part of a PGP/MIME signature.
We only support here full messages thus checking
the nesting level is sufficient. We do this only
for the first signature (i.e. if sig_data has not
been set yet). We do this only while in verify
mode because we don't want to write a full MUA
(although this would be easier than to tame this
Outlook beast). */
if
(
!
ctx
->
preview
&&
!
gpgme_data_new
(
&
ctx
->
sig_data
))
{
ctx
->
collect_signature
=
1
;
}
}
else
/* Other type. */
{
if
(
!
ctx
->
preview
)
ctx
->
collect_attachment
=
1
;
}
}
s1
=
rfc822parse_query_parameter
(
field
,
"charset"
,
0
);
if
(
s1
&&
!
strcmp
(
s1
,
"utf-8"
))
ctx
->
is_utf8
=
1
;
rfc822parse_release_field
(
field
);
}
else
{
/* No content-type at all indicates text/plain. */
is_text
=
1
;
}
ctx
->
in_data
=
1
;
log_debug
(
"%s: this body: nesting=%d part_counter=%d is_text=%d
\n
"
,
SRCNAME
,
ctx
->
nesting_level
,
ctx
->
part_counter
,
is_text
);
/* Need to figure out the encoding. */
ctx
->
is_qp_encoded
=
0
;
ctx
->
is_base64_encoded
=
0
;
p
=
rfc822parse_get_field
(
msg
,
"Content-Transfer-Encoding"
,
-1
,
&
off
);
if
(
p
)
{
if
(
!
stricmp
(
p
+
off
,
"quoted-printable"
))
ctx
->
is_qp_encoded
=
1
;
else
if
(
!
stricmp
(
p
+
off
,
"base64"
))
{
ctx
->
is_base64_encoded
=
1
;
ctx
->
base64
.
idx
=
0
;
ctx
->
base64
.
val
=
0
;
ctx
->
base64
.
stop_seen
=
0
;
ctx
->
base64
.
invalid_encoding
=
0
;
}
free
(
p
);
}
/* If this is a text part, decide whether we treat it as our body. */
if
(
is_text
)
{
/* If this is the first text part at all we will
start to collect it and use it later as the
regular body. An initialized ctx->BODY is an
indication that this is not the first text part -
we treat such a part like any other
attachment. */
if
(
!
ctx
->
body
)
{
if
(
!
gpgme_data_new
(
&
ctx
->
body
))
ctx
->
collect_body
=
1
;
}
else
if
(
!
ctx
->
preview
)
ctx
->
collect_attachment
=
1
;
}
if
(
ctx
->
collect_attachment
)
{
/* Now that if we have an attachment prepare for writing it
out. */
p
=
NULL
;
field
=
rfc822parse_parse_field
(
msg
,
"Content-Disposition"
,
-1
);
if
(
field
)
{
s1
=
rfc822parse_query_parameter
(
field
,
"filename"
,
0
);
if
(
s1
)
p
=
xstrdup
(
s1
);
rfc822parse_release_field
(
field
);
}
if
(
!
p
)
{
p
=
xmalloc
(
50
);
snprintf
(
p
,
49
,
"unnamed-%d.dat"
,
ctx
->
part_counter
);
}
if
(
ctx
->
outstream
)
{
IStream_Release
(
ctx
->
outstream
);
ctx
->
outstream
=
NULL
;
}
tryagain
:
xfree
(
ctx
->
filename
);
ctx
->
filename
=
ctx
->
preview
?
NULL
:
get_save_filename
(
ctx
->
hwnd
,
p
);
if
(
!
ctx
->
filename
)
ctx
->
collect_attachment
=
0
;
/* User does not want to save it. */
else
{
hr
=
OpenStreamOnFile
(
MAPIAllocateBuffer
,
MAPIFreeBuffer
,
(
STGM_CREATE
|
STGM_READWRITE
),
ctx
->
filename
,
NULL
,
&
ctx
->
outstream
);
if
(
FAILED
(
hr
))
{
log_error
(
"%s:%s: can't create file `%s': hr=%#lx
\n
"
,
SRCNAME
,
__func__
,
ctx
->
filename
,
hr
);
MessageBox
(
ctx
->
hwnd
,
_
(
"Error creating file
\n
"
"Please select another one"
),
_
(
"I/O-Error"
),
MB_ICONERROR
|
MB_OK
);
goto
tryagain
;
}
log_debug
(
"%s:%s: writing attachment to `%s'
\n
"
,
SRCNAME
,
__func__
,
ctx
->
filename
);
}
xfree
(
p
);
}
}
else
if
(
event
==
RFC822PARSE_LEVEL_DOWN
)
{
ctx
->
nesting_level
++
;
}
else
if
(
event
==
RFC822PARSE_LEVEL_UP
)
{
if
(
ctx
->
nesting_level
)
ctx
->
nesting_level
--
;
else
{
log_error
(
"%s: ctx=%p, invalid structure: bad nesting level
\n
"
,
SRCNAME
,
ctx
);
ctx
->
parser_error
=
1
;
}
}
else
if
(
event
==
RFC822PARSE_BOUNDARY
||
event
==
RFC822PARSE_LAST_BOUNDARY
)
{
ctx
->
in_data
=
0
;
ctx
->
collect_body
=
0
;
ctx
->
collect_attachment
=
0
;
xfree
(
ctx
->
filename
);
ctx
->
filename
=
NULL
;
if
(
ctx
->
outstream
)
{
IStream_Commit
(
ctx
->
outstream
,
0
);
IStream_Release
(
ctx
->
outstream
);
ctx
->
outstream
=
NULL
;
}
if
(
ctx
->
start_hashing
==
2
&&
ctx
->
hashing_level
==
ctx
->
nesting_level
)
{
ctx
->
start_hashing
=
3
;
/* Avoid triggering it again. */
ctx
->
collect_signeddata
=
0
;
}
}
else
if
(
event
==
RFC822PARSE_BEGIN_HEADER
)
{
ctx
->
part_counter
++
;
}
return
0
;
}
/* This handler is called by GPGME with the decrypted plaintext. */
static
ssize_t
plaintext_handler
(
void
*
handle
,
const
void
*
buffer
,
size_t
size
)
{
pgpmime_context_t
ctx
=
handle
;
const
char
*
s
;
size_t
nleft
,
pos
,
len
;
s
=
buffer
;
pos
=
ctx
->
linebufpos
;
nleft
=
size
;
for
(;
nleft
;
nleft
--
,
s
++
)
{
if
(
pos
>=
ctx
->
linebufsize
)
{
log_error
(
"%s: ctx=%p, rfc822 parser failed: line too long
\n
"
,
SRCNAME
,
ctx
);
ctx
->
line_too_long
=
1
;
return
0
;
/* Error. */
}
if
(
*
s
!=
'\n'
)
ctx
->
linebuf
[
pos
++
]
=
*
s
;
else
{
/* Got a complete line. Remove the last CR */
if
(
pos
&&
ctx
->
linebuf
[
pos
-1
]
==
'\r'
)
pos
--
;
if
(
rfc822parse_insert
(
ctx
->
msg
,
ctx
->
linebuf
,
pos
))
{
log_error
(
"%s: ctx=%p, rfc822 parser failed: %s
\n
"
,
SRCNAME
,
ctx
,
strerror
(
errno
));
ctx
->
parser_error
=
1
;
return
0
;
/* Error. */
}
if
(
ctx
->
collect_signeddata
&&
ctx
->
signed_data
)
{
/* Save the signed data. Note that we need to delay
the CR/LF because the last line ending belongs to the
next boundary. */
if
(
ctx
->
collect_signeddata
==
2
)
gpgme_data_write
(
ctx
->
signed_data
,
"
\r\n
"
,
2
);
gpgme_data_write
(
ctx
->
signed_data
,
ctx
->
linebuf
,
pos
);
ctx
->
collect_signeddata
=
2
;
}
if
(
ctx
->
in_data
&&
ctx
->
collect_body
&&
ctx
->
body
)
{
/* We are inside the body of the message. Save it away
to a gpgme data object. Note that this is only
used for the first text part. */
if
(
ctx
->
collect_body
==
1
)
/* Need to skip the first line. */
ctx
->
collect_body
=
2
;
else
{
if
(
ctx
->
is_qp_encoded
)
len
=
qp_decode
(
ctx
->
linebuf
,
pos
);
else
if
(
ctx
->
is_base64_encoded
)
len
=
base64_decode
(
ctx
,
ctx
->
linebuf
,
pos
);
else
len
=
pos
;
if
(
len
)
{
if
(
ctx
->
is_utf8
)
gpgme_data_write
(
ctx
->
body
,
ctx
->
linebuf
,
len
);
else
latin1_data_write
(
ctx
->
body
,
ctx
->
linebuf
,
len
);
}
if
(
!
ctx
->
is_base64_encoded
)
gpgme_data_write
(
ctx
->
body
,
"
\r\n
"
,
2
);
}
}
else
if
(
ctx
->
in_data
&&
ctx
->
collect_attachment
)
{
/* We are inside of an attachment part. Write it out. */
if
(
ctx
->
collect_attachment
==
1
)
/* Skip the first line. */
ctx
->
collect_attachment
=
2
;
else
if
(
ctx
->
outstream
)
{
HRESULT
hr
=
0
;
if
(
ctx
->
is_qp_encoded
)
len
=
qp_decode
(
ctx
->
linebuf
,
pos
);
else
if
(
ctx
->
is_base64_encoded
)
len
=
base64_decode
(
ctx
,
ctx
->
linebuf
,
pos
);
else
len
=
pos
;
if
(
len
)
hr
=
IStream_Write
(
ctx
->
outstream
,
ctx
->
linebuf
,
len
,
NULL
);
if
(
!
hr
&&
!
ctx
->
is_base64_encoded
)
hr
=
IStream_Write
(
ctx
->
outstream
,
"
\r\n
"
,
2
,
NULL
);
if
(
hr
)
{
log_debug
(
"%s:%s: Write failed: hr=%#lx"
,
SRCNAME
,
__func__
,
hr
);
if
(
!
ctx
->
preview
)
MessageBox
(
ctx
->
hwnd
,
_
(
"Error writing file"
),
_
(
"I/O-Error"
),
MB_ICONERROR
|
MB_OK
);
ctx
->
parser_error
=
1
;
return
0
;
/* Error. */
}
}
}
else
if
(
ctx
->
in_data
&&
ctx
->
collect_signature
)
{
/* We are inside of a signature attachment part. */
if
(
ctx
->
collect_signature
==
1
)
/* Skip the first line. */
ctx
->
collect_signature
=
2
;
else
if
(
ctx
->
sig_data
)
{
if
(
ctx
->
is_qp_encoded
)
len
=
qp_decode
(
ctx
->
linebuf
,
pos
);
else
if
(
ctx
->
is_base64_encoded
)
len
=
base64_decode
(
ctx
,
ctx
->
linebuf
,
pos
);
else
len
=
pos
;
if
(
len
)
gpgme_data_write
(
ctx
->
sig_data
,
ctx
->
linebuf
,
len
);
if
(
!
ctx
->
is_base64_encoded
)
gpgme_data_write
(
ctx
->
sig_data
,
"
\r\n
"
,
2
);
}
}
/* Continue with next line. */
pos
=
0
;
}
}
ctx
->
linebufpos
=
pos
;
return
size
;
}
/* Decrypt the PGP/MIME INSTREAM (i.e the second part of the
multipart/mixed) and allow saving of all attachments. On success a
newly allocated body will be stored at BODY. If ATTESTATION is not
NULL a text with the result of the signature verification will get
printed to it. HWND is the window to be used for message box and
such. In PREVIEW_MODE no verification will be done, no messages
saved and no messages boxes will pop up. */
int
pgpmime_decrypt
(
LPSTREAM
instream
,
int
ttl
,
char
**
body
,
gpgme_data_t
attestation
,
HWND
hwnd
,
int
preview_mode
)
{
gpg_error_t
err
;
struct
gpgme_data_cbs
cbs
;
gpgme_data_t
plaintext
;
pgpmime_context_t
ctx
;
char
*
tmp
;
*
body
=
NULL
;
memset
(
&
cbs
,
0
,
sizeof
cbs
);
cbs
.
write
=
plaintext_handler
;
ctx
=
xcalloc
(
1
,
sizeof
*
ctx
+
LINEBUFSIZE
);
ctx
->
linebufsize
=
LINEBUFSIZE
;
ctx
->
hwnd
=
hwnd
;
ctx
->
preview
=
preview_mode
;
ctx
->
msg
=
rfc822parse_open
(
message_cb
,
ctx
);
if
(
!
ctx
->
msg
)
{
err
=
gpg_error_from_errno
(
errno
);
log_error
(
"failed to open the RFC822 parser: %s"
,
strerror
(
errno
));
goto
leave
;
}
err
=
gpgme_data_new_from_cbs
(
&
plaintext
,
&
cbs
,
ctx
);
if
(
err
)
goto
leave
;
tmp
=
native_to_utf8
(
_
(
"[PGP/MIME message]"
));
err
=
op_decrypt_stream_to_gpgme
(
instream
,
plaintext
,
ttl
,
tmp
,
attestation
,
preview_mode
);
xfree
(
tmp
);
if
(
!
err
&&
(
ctx
->
parser_error
||
ctx
->
line_too_long
))
err
=
gpg_error
(
GPG_ERR_GENERAL
);
if
(
!
err
)
{
if
(
ctx
->
body
)
{
/* Return the buffer but first make sure it is a string. */
if
(
gpgme_data_write
(
ctx
->
body
,
""
,
1
)
==
1
)
{
*
body
=
gpgme_data_release_and_get_mem
(
ctx
->
body
,
NULL
);
ctx
->
body
=
NULL
;
}
}
else
{
*
body
=
native_to_utf8
(
_
(
"[PGP/MIME message "
"without plain text body]"
));
}
}
leave
:
if
(
plaintext
)
gpgme_data_release
(
plaintext
);
if
(
ctx
)
{
if
(
ctx
->
outstream
)
{
IStream_Revert
(
ctx
->
outstream
);
IStream_Release
(
ctx
->
outstream
);
}
rfc822parse_close
(
ctx
->
msg
);
if
(
ctx
->
body
)
gpgme_data_release
(
ctx
->
body
);
xfree
(
ctx
->
filename
);
xfree
(
ctx
);
}
return
err
;
}
int
pgpmime_verify
(
const
char
*
message
,
int
ttl
,
char
**
body
,
gpgme_data_t
attestation
,
HWND
hwnd
,
int
preview_mode
)
{
gpg_error_t
err
=
0
;
pgpmime_context_t
ctx
;
const
char
*
s
;
*
body
=
NULL
;
ctx
=
xcalloc
(
1
,
sizeof
*
ctx
+
LINEBUFSIZE
);
ctx
->
linebufsize
=
LINEBUFSIZE
;
ctx
->
hwnd
=
hwnd
;
ctx
->
preview
=
preview_mode
;
ctx
->
verify_mode
=
1
;
ctx
->
msg
=
rfc822parse_open
(
message_cb
,
ctx
);
if
(
!
ctx
->
msg
)
{
err
=
gpg_error_from_errno
(
errno
);
log_error
(
"failed to open the RFC822 parser: %s"
,
strerror
(
errno
));
goto
leave
;
}
/* Need to pass the data line by line to the handler. */
for
(;(
s
=
strchr
(
message
,
'\n'
));
message
=
s
+
1
)
{
plaintext_handler
(
ctx
,
message
,
(
s
-
message
)
+
1
);
if
(
ctx
->
parser_error
||
ctx
->
line_too_long
)
{
err
=
gpg_error
(
GPG_ERR_GENERAL
);
break
;
}
}
/* Unless there is an error we should return the body. */
if
(
!
err
)
{
if
(
ctx
->
body
)
{
/* Return the buffer but first make sure it is a string. */
if
(
gpgme_data_write
(
ctx
->
body
,
""
,
1
)
==
1
)
{
*
body
=
gpgme_data_release_and_get_mem
(
ctx
->
body
,
NULL
);
ctx
->
body
=
NULL
;
}
}
else
{
*
body
=
native_to_utf8
(
_
(
"[PGP/MIME signed message without a "
"plain text body]"
));
}
}
/* Now actually verify the signature. */
if
(
!
err
&&
ctx
->
signed_data
&&
ctx
->
sig_data
)
{
char
*
tmp
;
gpgme_data_seek
(
ctx
->
signed_data
,
0
,
SEEK_SET
);
gpgme_data_seek
(
ctx
->
sig_data
,
0
,
SEEK_SET
);
tmp
=
native_to_utf8
(
_
(
"[PGP/MIME signature]"
));
err
=
op_verify_detached_sig_gpgme
(
ctx
->
signed_data
,
ctx
->
sig_data
,
tmp
,
attestation
);
xfree
(
tmp
);
}
leave
:
if
(
ctx
)
{
if
(
ctx
->
outstream
)
{
IStream_Revert
(
ctx
->
outstream
);
IStream_Release
(
ctx
->
outstream
);
}
rfc822parse_close
(
ctx
->
msg
);
if
(
ctx
->
body
)
gpgme_data_release
(
ctx
->
body
);
if
(
ctx
->
signed_data
)
gpgme_data_release
(
ctx
->
signed_data
);
if
(
ctx
->
sig_data
)
gpgme_data_release
(
ctx
->
sig_data
);
xfree
(
ctx
->
filename
);
xfree
(
ctx
);
}
return
err
;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Mon, Dec 23, 3:15 PM (20 m, 27 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a5/c4/afe7475adb088f76fcf362d038f3
Attached To
rO GpgOL
Event Timeline
Log In to Comment