[svn] GpgOL - r175 - trunk/src
svn author wk
cvs at cvs.gnupg.org
Sat Sep 8 13:19:07 CEST 2007
Author: wk
Date: 2007-09-08 13:18:38 +0200 (Sat, 08 Sep 2007)
New Revision: 175
Modified:
trunk/src/mimemaker.c
trunk/src/util.h
Log:
Added framework for PGP/MIME encryption.
Modified: trunk/src/mimemaker.c
===================================================================
--- trunk/src/mimemaker.c 2007-09-07 15:19:08 UTC (rev 174)
+++ trunk/src/mimemaker.c 2007-09-08 11:18:38 UTC (rev 175)
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdarg.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
@@ -53,10 +54,51 @@
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/");
-/* The maximum length of a line we are able to process. RFC822 allows
- only for 1000 bytes; thus 2000 seems to be a reasonable value. */
-#define LINEBUFSIZE 2000
+/* The object we use instead of IStream. It allows us to have a
+ callback method for output and thus for processing stuff
+ recursively. */
+struct sink_s;
+typedef struct sink_s *sink_t;
+struct sink_s
+{
+ void *cb_data;
+ int (*writefnc)(sink_t sink, const void *data, size_t datalen);
+};
+
+/*** local prototypes ***/
+static int write_multistring (sink_t sink, const char *text1,
+ ...) GPGOL_GCC_A_SENTINEL(0);
+
+
+
+
+
+/* Standard write method used with a sink_t object. */
+static int
+sink_std_write (sink_t sink, const void *data, size_t datalen)
+{
+ HRESULT hr;
+ LPSTREAM stream = sink->cb_data;
+
+ if (!stream)
+ {
+ log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
+ return -1;
+ }
+ if (!data)
+ return 0; /* Flush - nothing to do here. */
+
+ hr = IStream_Write (stream, data, datalen, NULL);
+ if (hr)
+ {
+ log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
+ return -1;
+ }
+ return 0;
+}
+
+
/* Make sure that PROTOCOL is usable or return a suitable protocol.
On error PROTOCOL_UNKNOWN is returned. */
static protocol_t
@@ -84,7 +126,7 @@
retruned. The caller needs to call SaveChanges. Returns NULL on
failure in which case STREAM will be set to NULL. */
static LPATTACH
-create_mapi_attachment (LPMESSAGE message, LPSTREAM *stream)
+create_mapi_attachment (LPMESSAGE message, sink_t sink)
{
HRESULT hr;
ULONG pos;
@@ -92,7 +134,8 @@
LPATTACH att = NULL;
LPUNKNOWN punk;
- *stream = NULL;
+ sink->cb_data = NULL;
+ sink->writefnc = NULL;
hr = IMessage_CreateAttach (message, NULL, 0, &pos, &att);
if (hr)
{
@@ -165,7 +208,8 @@
SRCNAME, __func__, hr);
goto failure;
}
- *stream = (LPSTREAM)punk;
+ sink->cb_data = (LPSTREAM)punk;
+ sink->writefnc = sink_std_write;
return att;
failure:
@@ -174,49 +218,69 @@
}
-/* Wrapper around IStream::Write to print an error message. */
+/* Write data to a sink_t. */
static int
-write_buffer (LPSTREAM stream, const void *data, size_t datalen)
+write_buffer (sink_t sink, const void *data, size_t datalen)
{
- HRESULT hr;
-
- hr = IStream_Write (stream, data, datalen, NULL);
- if (hr)
+ if (!sink || !sink->writefnc)
{
- log_error ("%s:%s: Write failed: hr=%#lx", SRCNAME, __func__, hr);
+ log_error ("%s:%s: sink not properliy setup", SRCNAME, __func__);
return -1;
}
- return 0;
+ return sink->writefnc (sink, data, datalen);
}
+
/* Write the string TEXT to the IStream STREAM. Returns 0 on sucsess,
prints an error message and returns -1 on error. */
static int
-write_string (LPSTREAM stream, const char *text)
+write_string (sink_t sink, const char *text)
{
- return write_buffer (stream, text, strlen (text));
+ return write_buffer (sink, text, strlen (text));
}
-/* Helper to write a boundary to the output stream. The leading LF
+/* Write the string TEXT1 and all folloing arguments of type (const
+ char*) to the SINK. The list of argumens needs to be terminated
+ with a NULL. Returns 0 on sucsess, prints an error message and
+ returns -1 on error. */
+static int
+write_multistring (sink_t sink, const char *text1, ...)
+{
+ va_list arg_ptr;
+ int rc;
+ const char *s;
+
+ va_start (arg_ptr, text1);
+ s = text1;
+ do
+ rc = write_string (sink, s);
+ while (!rc && (s=va_arg (arg_ptr, const char *)));
+ va_end (arg_ptr);
+ return rc;
+}
+
+
+
+/* Helper to write a boundary to the output sink. The leading LF
will be written as well. */
static int
-write_boundary (LPSTREAM stream, const char *boundary, int lastone)
+write_boundary (sink_t sink, const char *boundary, int lastone)
{
- int rc = write_string (stream, "\r\n--");
+ int rc = write_string (sink, "\r\n--");
if (!rc)
- rc = write_string (stream, boundary);
+ rc = write_string (sink, boundary);
if (!rc)
- rc = write_string (stream, lastone? "--\r\n":"\r\n");
+ rc = write_string (sink, lastone? "--\r\n":"\r\n");
return rc;
}
-/* Write DATALEN bytes of DATA to STREAM in base64 encoding. This
+/* Write DATALEN bytes of DATA to SINK in base64 encoding. This
creates a complete Base64 chunk including the trailing fillers. */
static int
-write_b64 (LPSTREAM stream, const void *data, size_t datalen)
+write_b64 (sink_t sink, const void *data, size_t datalen)
{
int rc;
const unsigned char *p;
@@ -224,6 +288,7 @@
int idx, quads;
char outbuf[4];
+ log_debug (" writing base64 of length %d\n", (int)datalen);
idx = quads = 0;
for (p = data; datalen; p++, datalen--)
{
@@ -234,13 +299,13 @@
outbuf[1] = bintoasc[(((*inbuf<<4)&060)|((inbuf[1] >> 4)&017))&077];
outbuf[2] = bintoasc[(((inbuf[1]<<2)&074)|((inbuf[2]>>6)&03))&077];
outbuf[3] = bintoasc[inbuf[2]&077];
- if ((rc = write_buffer (stream, outbuf, 4)))
+ if ((rc = write_buffer (sink, outbuf, 4)))
return rc;
idx = 0;
if (++quads >= (64/4))
{
quads = 0;
- if ((rc = write_buffer (stream, "\r\n", 2)))
+ if ((rc = write_buffer (sink, "\r\n", 2)))
return rc;
}
}
@@ -261,21 +326,21 @@
outbuf[2] = bintoasc[((inbuf[1]<<2)&074)&077];
outbuf[3] = '=';
}
- if ((rc = write_buffer (stream, outbuf, 4)))
+ if ((rc = write_buffer (sink, outbuf, 4)))
return rc;
++quads;
}
if (quads)
- if ((rc = write_buffer (stream, "\r\n", 2)))
+ if ((rc = write_buffer (sink, "\r\n", 2)))
return rc;
return 0;
}
-/* Write DATALEN bytes of DATA to STREAM in quoted-prinable encoding. */
+/* Write DATALEN bytes of DATA to SINK in quoted-prinable encoding. */
static int
-write_qp (LPSTREAM stream, const void *data, size_t datalen)
+write_qp (sink_t sink, const void *data, size_t datalen)
{
int rc;
const unsigned char *p;
@@ -297,12 +362,13 @@
outbuf[outidx++] = '='; \
outbuf[outidx++] = '\r'; \
outbuf[outidx++] = '\n'; \
- if ((rc = write_buffer (stream, outbuf, outidx))) \
+ if ((rc = write_buffer (sink, outbuf, outidx))) \
return rc; \
outidx = 0; \
} \
} while (0)
+ log_debug (" writing qp of length %d\n", (int)datalen);
outidx = 0;
for (p = data; datalen; p++, datalen--)
{
@@ -311,7 +377,7 @@
/* Line break. */
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
- if ((rc = write_buffer (stream, outbuf, outidx)))
+ if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
outidx = 0;
if (*p == '\r')
@@ -363,7 +429,7 @@
{
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
- if ((rc = write_buffer (stream, outbuf, outidx)))
+ if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
}
@@ -373,15 +439,16 @@
}
-/* Write DATALEN bytes of DATA to STREAM in plain ascii encoding. */
+/* Write DATALEN bytes of DATA to SINK in plain ascii encoding. */
static int
-write_plain (LPSTREAM stream, const void *data, size_t datalen)
+write_plain (sink_t sink, const void *data, size_t datalen)
{
int rc;
const unsigned char *p;
char outbuf[100];
int outidx;
+ log_debug (" writing ascii of length %d\n", (int)datalen);
outidx = 0;
for (p = data; datalen; p++, datalen--)
{
@@ -389,7 +456,7 @@
{
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
- if ((rc = write_buffer (stream, outbuf, outidx)))
+ if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
outidx = 0;
if (*p == '\r')
@@ -424,7 +491,7 @@
{
outbuf[outidx++] = '\r';
outbuf[outidx++] = '\n';
- if ((rc = write_buffer (stream, outbuf, outidx)))
+ if ((rc = write_buffer (sink, outbuf, outidx)))
return rc;
}
@@ -660,18 +727,43 @@
-/* Write a MIME part to STREAM. The BOUNDARY is written first the
- DATA is analyzed and appropriate headers are written. If FILENAME
- is given it will be added to the part's header. IS_MAPIBODY should
- be true if teh data has been retrieved from the body property. */
+/* Write a MIME part to SINK. First the BOUNDARY is written (unless
+ it is NULL) then the DATA is analyzed and appropriate headers are
+ written. If FILENAME is given it will be added to the part's
+ header. IS_MAPIBODY should be passed as true if the data has been
+ retrieved from the body property. */
static int
-write_part (LPSTREAM stream, const char *data, size_t datalen,
+write_part (sink_t sink, const char *data, size_t datalen,
const char *boundary, const char *filename, int is_mapibody)
{
int rc;
const char *ct;
int use_b64, use_qp, is_text;
+ if (filename)
+ {
+ /* If there is a filename strip the directory part. Take care
+ that there might be slashes of backslashes. */
+ const char *s1 = strrchr (filename, '/');
+ const char *s2 = strrchr (filename, '\\');
+
+ if (!s1)
+ s1 = s2;
+ else if (s1 && s2 && s2 > s1)
+ s1 = s2;
+
+ if (s1)
+ filename = s1;
+ if (*filename && filename[1] == ':')
+ filename += 2;
+ if (!*filename)
+ filename = NULL;
+ }
+
+ log_debug ("Writing part of length %d%s filename=`%s'\n",
+ (int)datalen, is_mapibody? " (body)":"",
+ filename?filename:"[none]");
+
ct = infer_content_type (data, datalen, filename, is_mapibody, &use_b64);
use_qp = 0;
if (!use_b64)
@@ -685,42 +777,60 @@
}
is_text = !strncmp (ct, "text/", 5);
- if ((rc = write_boundary (stream, boundary, 0)))
+ if (boundary)
+ if ((rc = write_boundary (sink, boundary, 0)))
+ return rc;
+ if ((rc=write_multistring (sink,
+ "Content-Type: ", ct,
+ (is_text || filename? ";\r\n" :"\r\n"),
+ NULL)))
return rc;
- if (!(rc = write_string (stream, "Content-Type: ")))
- if (!(rc = write_string (stream, ct)))
- rc = write_string (stream, is_text? ";\r\n":"\r\n");
- if (rc)
- return rc;
/* OL inserts a charset parameter in many cases, so we do it right
away for all text parts. We can assume us-ascii if no special
encoding is required. */
if (is_text)
- if ((rc = write_string (stream, (!use_qp && !use_b64)?
- "\tcharset=\"us-ascii\"\r\n":
- "\tcharset=\"utf-8\"\r\n")))
+ if ((rc=write_multistring (sink,
+ "\tcharset=\"",
+ (!use_qp && !use_b64? "us-ascii" : "utf-8"),
+ filename ? "\";\r\n" : "\"\r\n",
+ NULL)))
return rc;
-
+
+ if (filename)
+ if ((rc=write_multistring (sink,
+ "\tname=\"", filename, "\"\r\n",
+ NULL)))
+ return rc;
+
/* Note that we need to output even 7bit because OL inserts that
anyway. */
- if (!(rc = write_string (stream, "Content-Transfer-Encoding: ")))
- rc = write_string (stream, (use_b64? "base64\r\n":
- use_qp? "quoted-printable\r\n":"7bit\r\n"));
- if (rc)
+ if ((rc = write_multistring (sink,
+ "Content-Transfer-Encoding: ",
+ (use_b64? "base64\r\n":
+ use_qp? "quoted-printable\r\n":"7bit\r\n"),
+ NULL)))
return rc;
+ if (filename)
+ if ((rc=write_multistring (sink,
+ "Content-Disposition: attachment;\r\n"
+ "\tfilename=\"", filename, "\"\r\n",
+ NULL)))
+ return rc;
+
+
/* Write delimiter. */
- if ((rc = write_string (stream, "\r\n")))
+ if ((rc = write_string (sink, "\r\n")))
return rc;
/* Write the content. */
if (use_b64)
- rc = write_b64 (stream, data, datalen);
+ rc = write_b64 (sink, data, datalen);
else if (use_qp)
- rc = write_qp (stream, data, datalen);
+ rc = write_qp (sink, data, datalen);
else
- rc = write_plain (stream, data, datalen);
+ rc = write_plain (sink, data, datalen);
return rc;
}
@@ -741,11 +851,10 @@
return count;
}
-/* Write old all attachments from TABLE separated by BOUNDARY to
- STREAM. This function needs to be syncronized with
- count_usable_attachments. */
+/* Write old all attachments from TABLE separated by BOUNDARY to SINK.
+ This function needs to be syncronized with count_usable_attachments. */
static int
-write_attachments (LPSTREAM stream,
+write_attachments (sink_t sink,
LPMESSAGE message, mapi_attach_item_t *table,
const char *boundary)
{
@@ -765,7 +874,7 @@
log_debug ("Attachment at index %d: length=%d\n", idx, (int)buflen);
if (!buffer)
return -1;
- rc = write_part (stream, buffer, buflen, boundary,
+ rc = write_part (sink, buffer, buflen, boundary,
table[idx].filename, 0);
xfree (buffer);
}
@@ -798,6 +907,118 @@
+/* Commit changes to the attachment ATTACH and release the object.
+ SINK needs to be passed as well and will also be closed. Note that
+ the address of ATTACH is expected so that the fucntion can set it
+ to NULL. */
+static int
+close_mapi_attachment (LPATTACH *attach, sink_t sink)
+{
+ HRESULT hr;
+ LPSTREAM stream = sink? sink->cb_data : NULL;
+
+ if (!stream)
+ {
+ log_error ("%s:%s: sink not setup", SRCNAME, __func__);
+ return -1;
+ }
+ hr = IStream_Commit (stream, 0);
+ if (hr)
+ {
+ log_error ("%s:%s: Commiting output stream failed: hr=%#lx",
+ SRCNAME, __func__, hr);
+ return -1;
+ }
+ IStream_Release (stream);
+ sink->cb_data = NULL;
+ hr = IAttach_SaveChanges (*attach, 0);
+ if (hr)
+ {
+ log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ return -1;
+ }
+ IAttach_Release (*attach);
+ *attach = NULL;
+ return 0;
+}
+
+
+/* Cancel changes to the attachment ATTACH and release the object.
+ SINK needs to be passed as well and will also be closed. Note that
+ the address of ATTACH is expected so that the fucntion can set it
+ to NULL. */
+static void
+cancel_mapi_attachment (LPATTACH *attach, sink_t sink)
+{
+ LPSTREAM stream = sink? sink->cb_data : NULL;
+
+ if (stream)
+ {
+ IStream_Revert (stream);
+ IStream_Release (stream);
+ sink->cb_data = NULL;
+ }
+ if (*attach)
+ {
+ /* Fixme: Should we try to delete it or is there a Revert method? */
+ IAttach_Release (*attach);
+ *attach = NULL;
+ }
+}
+
+
+
+/* Do the final processing for a message. */
+static int
+finalize_message (LPMESSAGE message, mapi_attach_item_t *att_table)
+{
+ HRESULT hr;
+ SPropValue prop;
+
+ /* Set the message class. */
+ prop.ulPropTag = PR_MESSAGE_CLASS_A;
+ prop.Value.lpszA = "IPM.Note.SMIME.MultipartSigned";
+ hr = IMessage_SetProps (message, 1, &prop, NULL);
+ if (hr)
+ {
+ log_error ("%s:%s: error setting the message class: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ return -1;
+ }
+
+ /* Now delete all parts of the MAPI message except for the one
+ attachment we just created. */
+ if (delete_all_attachments (message, att_table))
+ return -1;
+ {
+ /* Delete the body parts. We don't return any error because there
+ might be no body part at all. To avoid aliasing problems when
+ using static initialized array (SizedSPropTagArray macro) we
+ call it two times in a row. */
+ SPropTagArray proparray;
+
+ proparray.cValues = 1;
+ proparray.aulPropTag[0] = PR_BODY;
+ IMessage_DeleteProps (message, &proparray, NULL);
+ proparray.cValues = 1;
+ proparray.aulPropTag[0] = PR_BODY_HTML;
+ IMessage_DeleteProps (message, &proparray, NULL);
+ }
+
+ /* Save the Changes. */
+ hr = IMessage_SaveChanges (message, KEEP_OPEN_READWRITE|FORCE_SAVE);
+ if (hr)
+ {
+ log_error ("%s:%s: SaveChanges to the message failed: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ return -1;
+ }
+
+ return 0;
+}
+
+
/* Sign the MESSAGE using PROTOCOL. If PROTOCOL is PROTOCOL_UNKNOWN
the engine decides what protocol to use. On return MESSAGE is
modified so that sending it will result in a properly MOSS (that is
@@ -807,13 +1028,13 @@
int
mime_sign (LPMESSAGE message, protocol_t protocol)
{
+ int result = -1;
int rc;
- HRESULT hr;
- LPATTACH outattach;
- LPSTREAM outstream;
+ LPATTACH attach;
+ struct sink_s sinkmem;
+ sink_t sink = &sinkmem;
char boundary[BOUNDARYSIZE+1];
char inner_boundary[BOUNDARYSIZE+1];
- SPropValue prop;
mapi_attach_item_t *att_table = NULL;
char *body = NULL;
int n_att_usable;
@@ -822,8 +1043,8 @@
if (protocol == PROTOCOL_UNKNOWN)
return -1;
- outattach = create_mapi_attachment (message, &outstream);
- if (!outattach)
+ attach = create_mapi_attachment (message, sink);
+ if (!attach)
return -1;
/* Get the attachment info and the body. */
@@ -843,75 +1064,71 @@
/* Write the top header. */
generate_boundary (boundary);
- rc = write_string (outstream, ("MIME-Version: 1.0\r\n"
- "Content-Type: multipart/signed;\r\n"
- "\tprotocol=\"application/"));
- if (!rc)
- rc = write_string (outstream,
- (protocol == PROTOCOL_OPENPGP
- ? "pgp-signature"
- : "pkcs7-signature"));
- if (!rc)
- rc = write_string (outstream, ("\";\r\n\tboundary=\""));
- if (!rc)
- rc = write_string (outstream, boundary);
- if (!rc)
- rc = write_string (outstream, "\"\r\n");
+ rc = write_multistring (sink,
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/signed;\r\n"
+ "\tprotocol=\"application/",
+ (protocol == PROTOCOL_OPENPGP
+ ? "pgp-signature"
+ : "pkcs7-signature"),
+ "\";\r\n\tboundary=\"",
+ boundary,
+ "\"\r\n",
+ NULL);
if (rc)
goto failure;
-
+
if ((body && n_att_usable) || n_att_usable > 1)
{
/* A body and at least one attachment or more than one attachment */
generate_boundary (inner_boundary);
- rc = write_boundary (outstream, boundary, 0);
- if (!rc)
- rc = write_string (outstream, ("Content-Type: multipart/mixed;\r\n"
- "\tboundary=\""));
- if (!rc)
- rc = write_string (outstream, inner_boundary);
- if (!rc)
- rc = write_string (outstream, "\"\r\n");
- if (rc)
+ if ((rc = write_boundary (sink, boundary, 0)))
goto failure;
+ if ((rc=write_multistring (sink,
+ "Content-Type: multipart/mixed;\r\n",
+ "\tboundary=\"", inner_boundary, "\"\r\n",
+ NULL)))
+ goto failure;
}
- else
- {
- /* Only one part. */
- *inner_boundary = 0;
- }
+ else /* Only one part. */
+ *inner_boundary = 0;
+
if (body)
- rc = write_part (outstream, body, strlen (body),
+ rc = write_part (sink, body, strlen (body),
*inner_boundary? inner_boundary : boundary, NULL, 1);
if (!rc && n_att_usable)
- rc = write_attachments (outstream, message, att_table,
+ rc = write_attachments (sink, message, att_table,
*inner_boundary? inner_boundary : boundary);
if (rc)
goto failure;
+
+ xfree (body);
+ body = NULL;
+
/* Finish the possible multipart/mixed. */
if (*inner_boundary)
{
- rc = write_boundary (outstream, inner_boundary, 1);
+ rc = write_boundary (sink, inner_boundary, 1);
if (rc)
goto failure;
}
- /* Write signature attachment. We don't write it directly but a
- palceholder there. This spaceholder starts with the prefix of a
- boundary so that it won't accidently occur in the actual content. */
- if ((rc = write_boundary (outstream, boundary, 0)))
+
+ /* Write signature attachment. We don't write it directly but use a
+ placeholder. */
+ if ((rc = write_boundary (sink, boundary, 0)))
goto failure;
- if ((rc = write_string (outstream,
- (protocol == PROTOCOL_OPENPGP)?
- "Content-Type: application/pgp-signature\r\n":
- "Content-Type: application/pkcs7-signature\r\n")))
+ if ((rc=write_string (sink,
+ (protocol == PROTOCOL_OPENPGP
+ ? "Content-Type: application/pgp-signature\r\n"
+ : "Content-Type: application/pkcs7-signature\r\n"))))
goto failure;
/* If we would add "Content-Transfer-Encoding: 7bit\r\n" to this
- atatchment, Outlooks does not processed with sending and even
+ attachment, Outlooks does not processed with sending and even
does not return any error. A wild guess is that while OL adds
this header itself, it detects that it already exists and somehow
gets into a problem. It is not a problem with the other parts,
@@ -919,104 +1136,193 @@
Any way, it is not required that we add it as we won't hash
it. */
-
- if ((rc = write_string (outstream, "\r\n")))
+ if ((rc = write_string (sink, "\r\n")))
goto failure;
- if ((rc = write_string (outstream, "--=-=@SIGNATURE@\r\n\r\n")))
+ /* Let the placeholder start with the prefix of a boundary so that
+ it won't accidently occur in the actual content. */
+ if ((rc = write_string (sink, "--=-=@SIGNATURE@\r\n\r\n")))
goto failure;
/* Write the final boundary and finish the attachment. */
- if ((rc = write_boundary (outstream, boundary, 1)))
+ if ((rc = write_boundary (sink, boundary, 1)))
goto failure;
- hr = IStream_Commit (outstream, 0);
- if (hr)
+ if (close_mapi_attachment (&attach, sink))
+ goto failure;
+
+ if (finalize_message (message, att_table))
+ goto failure;
+
+ result = 0; /* Everything is fine, fall through the cleanup now. */
+
+ failure:
+ cancel_mapi_attachment (&attach, sink);
+ xfree (body);
+ mapi_release_attach_table (att_table);
+ return result;
+}
+
+
+
+/* Sink write method used by mime_encrypt. */
+static int
+sink_encryption_write (sink_t encsink, const void *data, size_t datalen)
+{
+ sink_t outsink = encsink->cb_data;
+
+ if (!outsink)
{
- log_error ("%s:%s: Commiting output stream failed: hr=%#lx",
- SRCNAME, __func__, hr);
- goto failure;
+ log_error ("%s:%s: sink not setup for writing", SRCNAME, __func__);
+ return -1;
}
- IStream_Release (outstream);
- outstream = NULL;
- hr = IAttach_SaveChanges (outattach, KEEP_OPEN_READWRITE);
- if (hr)
+ if (!data)
{
- log_error ("%s:%s: SaveChanges of the attachment failed: hr=%#lx\n",
- SRCNAME, __func__, hr);
- goto failure;
+ /* Flush */
+ return 0;
}
- IAttach_Release (outattach);
- outattach = NULL;
- /* Set the message class. */
- prop.ulPropTag = PR_MESSAGE_CLASS_A;
- prop.Value.lpszA = "IPM.Note.SMIME.MultipartSigned";
- hr = IMessage_SetProps (message, 1, &prop, NULL);
- if (hr)
+ return write_buffer (outsink, data, datalen);
+}
+
+
+/* Encrypt the MESSAGE. */
+int
+mime_encrypt (LPMESSAGE message, protocol_t protocol)
+{
+ int result = -1;
+ int rc;
+ LPATTACH attach;
+ struct sink_s sinkmem;
+ sink_t sink = &sinkmem;
+ struct sink_s encsinkmem;
+ sink_t encsink = &encsinkmem;
+ char boundary[BOUNDARYSIZE+1];
+ char inner_boundary[BOUNDARYSIZE+1];
+ mapi_attach_item_t *att_table = NULL;
+ char *body = NULL;
+ int n_att_usable;
+
+ protocol = check_protocol (protocol);
+ if (protocol == PROTOCOL_UNKNOWN)
+ return -1;
+
+ /* FIXME For now only PGP/MIME is supported. */
+
+ attach = create_mapi_attachment (message, sink);
+ if (!attach)
+ return -1;
+
+ /* Get the attachment info and the body. */
+ body = mapi_get_body (message, NULL);
+ if (body && !*body)
{
- log_error ("%s:%s: error setting the message class: hr=%#lx\n",
- SRCNAME, __func__, hr);
+ xfree (body);
+ body = NULL;
+ }
+ att_table = mapi_create_attach_table (message, 0);
+ n_att_usable = count_usable_attachments (att_table);
+ if (!n_att_usable && !body)
+ {
+ log_debug ("%s:%s: can't encrypt an empty message\n", SRCNAME, __func__);
goto failure;
}
- /* Now delete all parts of the MAPI message except for the one
- attachment we just created. */
- if ((rc = delete_all_attachments (message, att_table)))
+ /* Write the top header. */
+ generate_boundary (boundary);
+ if ((rc=write_multistring (sink,
+ "MIME-Version: 1.0\r\n"
+ "Content-Type: multipart/encrypted;\r\n"
+ "\tprotocol=\"application/pgp-encrypted\";\r\n",
+ "\tboundary=\"", boundary, "\"\r\n",
+ NULL)))
goto failure;
- {
- /* Delete the body parts. We don't return any error because there
- might be no body part at all. To avoid aliasing problems when
- using static initialized array (SizedSPropTagArray macro) we
- call it two times in a row. */
- SPropTagArray proparray;
- proparray.cValues = 1;
- proparray.aulPropTag[0] = PR_BODY;
- IMessage_DeleteProps (message, &proparray, NULL);
- proparray.cValues = 1;
- proparray.aulPropTag[0] = PR_BODY_HTML;
- IMessage_DeleteProps (message, &proparray, NULL);
- }
+ /* Write the PGP/MIME encrypted part. */
+ if ((rc = write_boundary (sink, boundary, 0)))
+ goto failure;
+ if ((rc=write_multistring (sink,
+ "Content-Type: application/pgp-encrypted\r\n"
+ "\r\n"
+ "Version: 1\r\n",
+ NULL)))
+ goto failure;
+
+ /* And start the second part. */
+ if ((rc = write_boundary (sink, boundary, 0)))
+ goto failure;
+ if ((rc=write_multistring (sink,
+ "Content-Type: application/octet-stream\r\n"
+ "\r\n", NULL)))
+ goto failure;
+
+ /* Create a new sink for encrypting the following stuff. */
+ encsink->cb_data = sink;
+ encsink->writefnc = sink_encryption_write;
+
- /* Save the Changes. */
- hr = IMessage_SaveChanges (message, KEEP_OPEN_READWRITE|FORCE_SAVE);
- if (hr)
+ if ((body && n_att_usable) || n_att_usable > 1)
{
- log_error ("%s:%s: SaveChanges to the message failed: hr=%#lx\n",
- SRCNAME, __func__, hr);
- goto failure;
+ /* A body and at least one attachment or more than one attachment */
+ generate_boundary (inner_boundary);
+ if ((rc=write_multistring (encsink,
+ "Content-Type: multipart/mixed;\r\n",
+ "\tboundary=\"", inner_boundary, "\"\r\n",
+ NULL)))
+ goto failure;
}
+ else /* Only one part. */
+ *inner_boundary = 0;
+
+ if (body)
+ rc = write_part (encsink, body, strlen (body),
+ *inner_boundary? inner_boundary : NULL, NULL, 1);
+ if (!rc && n_att_usable)
+ rc = write_attachments (encsink, message, att_table,
+ *inner_boundary? inner_boundary : NULL);
+ if (rc)
+ goto failure;
+
xfree (body);
- mapi_release_attach_table (att_table);
- return 0;
+ body = NULL;
+ /* Finish the possible multipart/mixed. */
+ if (*inner_boundary && (rc = write_boundary (encsink, inner_boundary, 1)))
+ goto failure;
+
+ /* Flush the encryption sink and thus wait for the encryption to
+ get ready. */
+ if ((rc = write_buffer (encsink, NULL, 0)))
+ goto failure;
+ /* FIXME: Release the encryption context etc. Using the flush above
+ is not a clean way of doing it. */
+ encsink->cb_data = NULL; /* Not needed anymore. */
+
+
+ /* Write the final boundary and finish the attachment. */
+ if ((rc = write_boundary (sink, boundary, 1)))
+ goto failure;
+
+ if (close_mapi_attachment (&attach, sink))
+ goto failure;
+
+ if (finalize_message (message, att_table))
+ goto failure;
+
+ result = 0; /* Everything is fine, fall through the cleanup now. */
+
failure:
- if (outstream)
- {
- IStream_Revert (outstream);
- IStream_Release (outstream);
- }
- if (outattach)
- {
- /* Fixme: Should we try to delete it or is tehre a Revert method? */
- IAttach_Release (outattach);
- }
+ if (encsink->cb_data)
+ ;/*FIXME: Cancel the encryption. */
+ cancel_mapi_attachment (&attach, sink);
xfree (body);
mapi_release_attach_table (att_table);
- return -1;
+ return result;
}
int
-mime_encrypt (LPMESSAGE message, protocol_t protocol)
-{
- return -1;
-}
-
-
-int
mime_sign_encrypt (LPMESSAGE message, protocol_t protocol)
{
return -1;
Modified: trunk/src/util.h
===================================================================
--- trunk/src/util.h 2007-09-07 15:19:08 UTC (rev 174)
+++ trunk/src/util.h 2007-09-08 11:18:38 UTC (rev 175)
@@ -29,6 +29,13 @@
#endif
#endif
+#if __GNUC__ >= 4
+# define GPGOL_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a)))
+#else
+# define GPGOL_GCC_A_SENTINEL(a)
+#endif
+
+
/* To avoid that a compiler optimizes certain memset calls away, these
macros may be used instead. */
#define wipememory2(_ptr,_set,_len) do { \
More information about the Gnupg-commits
mailing list