Added missing commands for engine-assuan.c.
[gpgol.git] / src / message.cpp
1 /* message.cpp - Functions for message handling
2  *      Copyright (C) 2006, 2007 g10 Code GmbH
3  * 
4  * This file is part of GpgOL.
5  * 
6  * GpgOL is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  * 
11  * GpgOL is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  * 
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <windows.h>
26
27 #include "mymapi.h"
28 #include "mymapitags.h"
29 #include "myexchext.h"
30 #include "common.h"
31 #include "mapihelp.h"
32 #include "mimeparser.h"
33 #include "mimemaker.h"
34 #include "display.h"
35 #include "message.h"
36
37 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
38                                      SRCNAME, __func__, __LINE__); \
39                         } while (0)
40
41
42 static void 
43 ul_release (LPVOID punk)
44 {
45   ULONG res;
46   
47   if (!punk)
48     return;
49   res = UlRelease (punk);
50 //   log_debug ("%s UlRelease(%p) had %lu references\n", __func__, punk, res);
51 }
52
53
54 /* A helper function used by OnRead and OnOpen to dispatch the
55    message.  Returns true if the message has been processed.  */
56 bool
57 message_incoming_handler (LPMESSAGE message, msgtype_t msgtype)
58 {
59   bool retval = false;
60
61   switch (msgtype)
62     {
63     case MSGTYPE_UNKNOWN: 
64       break;
65     case MSGTYPE_GPGOL:
66       log_debug ("%s:%s: ignoring unknown message of original SMIME class\n",
67                  SRCNAME, __func__);
68       break;
69     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
70       log_debug ("%s:%s: processing multipart signed message\n", 
71                  SRCNAME, __func__);
72       retval = true;
73       message_verify (message, msgtype, 0);
74       break;
75     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
76       log_debug ("%s:%s: processing multipart encrypted message\n",
77                  SRCNAME, __func__);
78       retval = true;
79       message_decrypt (message, msgtype, 0);
80       break;
81     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
82       log_debug ("%s:%s: processing opaque signed message\n", 
83                  SRCNAME, __func__);
84       retval = true;
85       message_verify (message, msgtype, 0);
86       break;
87     case MSGTYPE_GPGOL_CLEAR_SIGNED:
88       log_debug ("%s:%s: processing clear signed pgp message\n", 
89                  SRCNAME, __func__);
90       retval = true;
91       message_verify (message, msgtype, 0);
92       break;
93     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
94       log_debug ("%s:%s: processing opaque encrypted message\n",
95                  SRCNAME, __func__);
96       retval = true;
97       message_decrypt (message, msgtype, 0);
98       break;
99     case MSGTYPE_GPGOL_PGP_MESSAGE:
100       log_debug ("%s:%s: processing pgp message\n", SRCNAME, __func__);
101       retval = true;
102       message_decrypt (message, msgtype, 0);
103       break;
104     }
105
106   return retval;
107 }
108
109
110 /* Common Code ise by OnReadComplete and OnOpenComplete to display a
111    modified message.   Returns true if the message was encrypted.  */
112 bool
113 message_display_handler (LPEXCHEXTCALLBACK eecb, HWND hwnd)
114 {
115   HRESULT hr;
116   LPMESSAGE message = NULL;
117   LPMDB mdb = NULL;
118   int ishtml, wasprotected = false;
119   char *body;
120   
121   hr = eecb->GetObject (&mdb, (LPMAPIPROP *)&message);
122   if (SUCCEEDED (hr))
123     {
124       /* If the message was protected we don't allow a fallback to the
125          OOM display methods.  */
126       body = mapi_get_gpgol_body_attachment (message, NULL, 
127                                              &ishtml, &wasprotected);
128       if (body)
129         update_display (hwnd, wasprotected? NULL: eecb, ishtml, body);
130       else
131         update_display (hwnd, NULL, 0, 
132                         _("[Crypto operation failed - "
133                           "can't show the body of the message]"));
134       xfree (body);
135   
136       /*  put_outlook_property (eecb, "EncryptedStatus", "MyStatus"); */
137     }
138   else
139     log_debug_w32 (hr, "%s:%s: error getting message", SRCNAME, __func__);
140
141   ul_release (message);
142   ul_release (mdb);
143
144   return !!wasprotected;
145 }
146
147
148 /* Display some information about MESSAGE.  */
149 void
150 message_show_info (LPMESSAGE message, HWND hwnd)
151 {
152   char *msgcls = mapi_get_message_class (message);
153   char *sigstat = mapi_get_sig_status (message);
154   char *mimeinfo = mapi_get_mime_info (message);
155   size_t buflen;
156   char *buffer;
157
158   buflen = strlen (msgcls) + strlen (sigstat) + strlen (mimeinfo) + 200;
159   buffer = (char*)xmalloc (buflen+1);
160   snprintf (buffer, buflen, 
161             _("Message class: %s\n"
162               "Sig Status   : %s\n"
163               "Structure of the message:\n"
164               "%s"), 
165             msgcls,
166             sigstat,
167             mimeinfo);
168   
169   MessageBox (hwnd, buffer, _("GpgOL - Message Information"),
170               MB_ICONINFORMATION|MB_OK);
171   xfree (buffer);
172   xfree (mimeinfo);
173   xfree (sigstat);
174   xfree (msgcls);
175 }
176
177
178
179 \f
180 /* Convert the clear signed message from INPUT into a PS?MIME signed
181    message and return it in a new allocated buffer.  OUTPUTLEN
182    received the valid length of that buffer; the buffer is guarnateed
183    to be Nul terminated.  */
184 static char *
185 pgp_mime_from_clearsigned (LPSTREAM input, size_t *outputlen)
186 {
187   HRESULT hr;
188   STATSTG statinfo;
189   ULONG nread;
190   char *body = NULL;
191   char *p, *p0, *dest, *mark;
192   char boundary[BOUNDARYSIZE+1];
193   char top_header[200 + 2*BOUNDARYSIZE]; 
194   char sig_header[100 + BOUNDARYSIZE]; 
195   char end_header[10 + BOUNDARYSIZE];
196   size_t reserved_space;
197   int state;
198
199   *outputlen = 0;
200
201   /* Note that our parser does not make use of the micalg parameter.  */
202   generate_boundary (boundary);
203   snprintf (top_header, sizeof top_header,
204             "MIME-Version: 1.0\r\n"
205             "Content-Type: multipart/signed; boundary=\"%s\";\r\n"
206             "              protocol=\"application/pgp-signature\"\r\n"
207             "\r\n"
208             "--%s\r\n", boundary, boundary);
209   snprintf (sig_header, sizeof sig_header,
210             "--%s\r\n"
211             "Content-Type: application/pgp-signature\r\n"
212             "\r\n", boundary);
213   snprintf (end_header, sizeof end_header,
214             "\r\n"
215             "--%s--\r\n", boundary);
216   reserved_space = (strlen (top_header) + strlen (sig_header) 
217                     + strlen (end_header)+ 100);
218
219   hr = input->Stat (&statinfo, STATFLAG_NONAME);
220   if (hr)
221     {
222       log_debug ("%s:%s: Stat failed: hr=%#lx", SRCNAME, __func__, hr);
223       return NULL;
224     }
225       
226   body = (char*)xmalloc (reserved_space
227                          + (size_t)statinfo.cbSize.QuadPart + 2);
228   hr = input->Read (body+reserved_space,
229                     (size_t)statinfo.cbSize.QuadPart, &nread);
230   if (hr)
231     {
232       log_debug ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
233       xfree (body);
234       return NULL;
235     }
236   body[reserved_space + nread] = 0;
237   body[reserved_space + nread+1] = 0;  /* Just in case this is
238                                           accidently an wchar_t. */
239   if (nread != statinfo.cbSize.QuadPart)
240     {
241       log_debug ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
242       xfree (body);
243       return NULL;
244     }
245
246   /* We do in place conversion. */
247   state = 0;
248   dest = NULL;
249   for (p=body+reserved_space; p && *p; p = (p=strchr (p+1, '\n'))? (p+1):NULL)
250     {
251       if (!state && !strncmp (p, "-----BEGIN PGP SIGNED MESSAGE-----", 34)
252           && trailing_ws_p (p+34) )
253         {
254           dest = stpcpy (body, top_header);
255           state = 1;
256         }
257       else if (state == 1)
258         {
259           /* Wait for an empty line.  */
260           if (trailing_ws_p (p))
261             state = 2;
262         }
263       else if (state == 2 && strncmp (p, "-----", 5))
264         {
265           /* Copy signed data. */
266           p0 = p;
267           if (*p == '-' && p[1] == ' ')
268             p +=2;  /* Remove escaping.  */
269           mark = NULL;
270           while (*p && *p != '\n')
271             {
272               if (*p == ' ' || *p == '\t' || *p == '\r')
273                 mark = p;
274               else
275                 mark = NULL;
276               *dest++ = *p++;
277             }
278           if (mark)
279             *mark =0; /* Remove trailing white space.  */
280           if (*p == '\n')
281             {
282               if (p[-1] == '\r')
283                 *dest++ = '\r';
284               *dest++ = '\n';
285             }
286           if (p > p0)
287             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
288         }
289       else if (state == 2)
290         {
291           /* Armor line encountered.  */
292           p0 = p;
293           if (strncmp (p, "-----BEGIN PGP SIGNATURE-----", 29)
294               || !trailing_ws_p (p+29) )
295             fprintf (stderr,"Invalid clear signed message\n");
296           state = 3;
297           dest = stpcpy (dest, sig_header);
298         
299           while (*p && *p != '\n')
300             *dest++ = *p++;
301           if (*p == '\n')
302             {
303               if (p[-1] == '\r')
304                 *dest++ = '\r';
305               *dest++ = '\n';
306             }
307           if (p > p0)
308             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
309         }
310       else if (state == 3 && strncmp (p, "-----", 5))
311         {
312           /* Copy signature.  */
313           p0 = p;
314           while (*p && *p != '\n')
315             *dest++ = *p++;
316           if (*p == '\n')
317             {
318               if (p[-1] == '\r')
319                 *dest++ = '\r';
320               *dest++ = '\n';
321             }
322           if (p > p0)
323             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
324         }
325       else if (state == 3)
326         {
327           /* Armor line encountered.  */
328           p0 = p;
329           if (strncmp (p, "-----END PGP SIGNATURE-----", 27)
330               || !trailing_ws_p (p+27) )
331             fprintf (stderr,"Invalid clear signed message (no end)\n");
332           while (*p && *p != '\n')
333             *dest++ = *p++;
334           if (*p == '\n')
335             {
336               if (p[-1] == '\r')
337                 *dest++ = '\r';
338               *dest++ = '\n';
339             }
340           dest = stpcpy (dest, end_header);
341           if (p > p0)
342             p--; /* Adjust so that the strchr (p+1, '\n') can work. */
343           break; /* Ah well, we can stop here.  */
344         }
345     }
346   if (!dest)
347     {
348       xfree (body);
349       return NULL;
350     }
351   *dest = 0;
352   *outputlen = strlen (body);
353
354   return body;
355 }
356
357
358
359 /* Verify MESSAGE and update the attachments as required.  MSGTYPE
360    should be the type of the message so that the fucntion can decide
361    what to do.  With FORCE set the verification is done regardlessless
362    of a cached signature result. */
363 int
364 message_verify (LPMESSAGE message, msgtype_t msgtype, int force)
365 {
366   mapi_attach_item_t *table = NULL;
367   int moss_idx = -1;
368   int i;
369   char *inbuf;
370   size_t inbuflen;
371   protocol_t protocol = PROTOCOL_UNKNOWN;
372   int err;
373
374   switch (msgtype)
375     {
376     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
377       break;
378     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
379       log_debug ("Opaque signed message are not yet supported!");
380       return 0;
381     case MSGTYPE_GPGOL_CLEAR_SIGNED:
382       break;
383     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
384     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
385     case MSGTYPE_GPGOL_PGP_MESSAGE:
386       return -1; /* Should not be called for such a message.  */
387     case MSGTYPE_UNKNOWN:
388     case MSGTYPE_GPGOL:
389       return 0; /* Nothing to do.  */
390     }
391   
392   /* If a verification is forced, we set the cached signature status
393      first to "?" to mark that no verification has yet happened. */
394   if (force)
395     mapi_set_sig_status (message, "?");
396   else if (mapi_test_sig_status (message))
397     return 0; /* Already checked that message.  */
398
399   if (msgtype == MSGTYPE_GPGOL_CLEAR_SIGNED)
400     {
401       /* PGP's clear signed messages are special: All is contained in
402          the body and thus there is no requirement for an
403          attachment.  */
404       LPSTREAM rawstream;
405       
406       rawstream = mapi_get_body_as_stream (message);
407       if (!rawstream)
408         return -1;
409       
410       inbuf = pgp_mime_from_clearsigned (rawstream, &inbuflen);
411       rawstream->Release ();
412       if (!inbuf)
413         return -1;
414       protocol = PROTOCOL_OPENPGP;
415     }
416   else
417     {
418       /* PGP/MIME or S/MIME stuff.  */
419       table = mapi_create_attach_table (message, 0);
420       if (!table)
421         return -1; /* No attachment - this should not happen.  */
422
423       for (i=0; !table[i].end_of_table; i++)
424         if (table[i].attach_type == ATTACHTYPE_MOSS)
425           {
426             moss_idx = i;
427             break;
428           }
429       if (moss_idx == -1 && !table[0].end_of_table && table[1].end_of_table)
430         {
431           /* No MOSS flag found in the table but there is only one
432              attachment.  Due to the message type we know that this is
433              the original MOSS message.  We mark this attachment as
434              hidden, so that it won't get displayed.  We further mark
435              it as our original MOSS attachment so that after parsing
436              we have a mean to find it again (see above).  */ 
437           moss_idx = 0;
438           mapi_mark_moss_attach (message, table+0);
439         }
440       
441       if (moss_idx == -1)
442         {
443           mapi_release_attach_table (table);
444           return -1; /* No original attachment - this should not happen.  */
445         }
446
447       inbuf = mapi_get_attach (message, table+0, &inbuflen);
448       if (!inbuf)
449         {
450           mapi_release_attach_table (table);
451           return -1; /* Problem getting the attachment.  */
452         }
453     }
454
455   err = mime_verify (protocol, inbuf, inbuflen, message, 0, 0);
456   log_debug ("mime_verify returned %d", err);
457   if (err)
458     {
459       char buf[200];
460       
461       snprintf (buf, sizeof buf, "Verify failed (%s)", gpg_strerror (err));
462       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
463     }
464   xfree (inbuf);
465                     
466   if (err)
467     {
468       char buf[200];
469       snprintf (buf, sizeof buf, "- %s", gpg_strerror (err));
470       mapi_set_sig_status (message, gpg_strerror (err));
471     }
472   else
473     mapi_set_sig_status (message, "! Good signature");
474
475   mapi_release_attach_table (table);
476   return 0;
477 }
478
479 /* Decrypt MESSAGE, check signature and update the attachments as
480    required.  MSGTYPE should be the type of the message so that the
481    function can decide what to do.  With FORCE set the verification is
482    done regardlessless of a cached signature result - hmmm, should we
483    such a thing for an encrypted message? */
484 int
485 message_decrypt (LPMESSAGE message, msgtype_t msgtype, int force)
486 {
487   mapi_attach_item_t *table = NULL;
488   int part2_idx;
489   int tblidx;
490   int retval = -1;
491   LPSTREAM cipherstream;
492   gpg_error_t err;
493   int is_opaque = 0;
494   protocol_t protocol;
495
496   switch (msgtype)
497     {
498     case MSGTYPE_UNKNOWN:
499     case MSGTYPE_GPGOL:
500     case MSGTYPE_GPGOL_OPAQUE_SIGNED:
501     case MSGTYPE_GPGOL_MULTIPART_SIGNED:
502     case MSGTYPE_GPGOL_CLEAR_SIGNED:
503       return -1; /* Should not have been called for this.  */
504     case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
505       break;
506     case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
507       is_opaque = 1;
508       break;
509     case MSGTYPE_GPGOL_PGP_MESSAGE:
510       break;
511     }
512   
513   if (msgtype == MSGTYPE_GPGOL_PGP_MESSAGE)
514     {
515       /* PGP messages are special:  All is contained in the body and thus
516          there is no requirement for an attachment.  */
517       cipherstream = mapi_get_body_as_stream (message);
518       if (!cipherstream)
519         goto leave;
520       protocol = PROTOCOL_OPENPGP;
521     }
522   else
523     {
524   
525       /* PGP/MIME or S/MIME stuff.  */
526       table = mapi_create_attach_table (message, 0);
527       if (!table)
528         goto leave; /* No attachment - this should not happen.  */
529
530       if (is_opaque)
531         {
532           /* S/MIME opaque encrypted message: We expect one
533              attachment.  As we don't know ether we are called the
534              first time, we first try to find this attachment by
535              looking at all attachments.  Only if this fails we
536              identify it by its order.  */
537           part2_idx = -1;
538           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
539             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
540               {
541                 if (part2_idx == -1 && table[tblidx].content_type 
542                     && (!strcmp (table[tblidx].content_type,
543                                  "application/pkcs7-mime")
544                         || !strcmp (table[tblidx].content_type,
545                                     "application/x-pkcs7-mime")))
546                   part2_idx = tblidx;
547               }
548           if (part2_idx == -1 && tblidx >= 1)
549             {
550               /* We have attachments but none are marked.  Thus we
551                  assume that this is the first time we see this
552                  message and we will set the mark now if we see
553                  appropriate content types. */
554               if (table[0].content_type               
555                   && (!strcmp (table[0].content_type, "application/pkcs7-mime")
556                       || !strcmp (table[0].content_type,
557                                   "application/x-pkcs7-mime")))
558                 part2_idx = 0;
559               if (part2_idx != -1)
560                 mapi_mark_moss_attach (message, table+part2_idx);
561             }
562           if (part2_idx == -1)
563             {
564               log_debug ("%s:%s: this is not an S/MIME encrypted message",
565                          SRCNAME, __func__);
566               goto leave;
567             }
568           protocol = PROTOCOL_SMIME;
569         }
570       else 
571         {
572           /* Multipart/encrypted message: We expect 2 attachments.
573              The first one with the version number and the second one
574              with the ciphertext.  As we don't know ether we are
575              called the first time, we first try to find these
576              attachments by looking at all attachments.  Only if this
577              fails we identify them by their order (i.e. the first 2
578              attachments) and mark them as part1 and part2.  */
579           int part1_idx;
580           
581           part1_idx = part2_idx = -1;
582           for (tblidx=0; !table[tblidx].end_of_table; tblidx++)
583             if (table[tblidx].attach_type == ATTACHTYPE_MOSS)
584               {
585                 if (part1_idx == -1 && table[tblidx].content_type 
586                     && !strcmp (table[tblidx].content_type,
587                                 "application/pgp-encrypted"))
588                   part1_idx = tblidx;
589                 else if (part2_idx == -1 && table[tblidx].content_type 
590                          && !strcmp (table[tblidx].content_type,
591                                      "application/octet-stream"))
592                   part2_idx = tblidx;
593               }
594           if (part1_idx == -1 && part2_idx == -1 && tblidx >= 2)
595             {
596               /* At least 2 attachments but none are marked.  Thus we
597                  assume that this is the first time we see this
598                  message and we will set the mark now if we see
599                  appropriate content types. */
600               if (table[0].content_type               
601                   && !strcmp (table[0].content_type,
602                               "application/pgp-encrypted"))
603                 part1_idx = 0;
604               if (table[1].content_type             
605                   && !strcmp (table[1].content_type, 
606                               "application/octet-stream"))
607                 part2_idx = 1;
608               if (part1_idx != -1 && part2_idx != -1)
609                 {
610                   mapi_mark_moss_attach (message, table+part1_idx);
611                   mapi_mark_moss_attach (message, table+part2_idx);
612                 }
613             }
614           if (part1_idx == -1 || part2_idx == -1)
615             {
616               log_debug ("%s:%s: this is not a PGP/MIME encrypted message",
617                          SRCNAME, __func__);
618               goto leave;
619             }
620           protocol = PROTOCOL_OPENPGP;
621         }
622       
623       cipherstream = mapi_get_attach_as_stream (message, table+part2_idx);
624       if (!cipherstream)
625         goto leave; /* Problem getting the attachment.  */
626     }
627
628   err = mime_decrypt (protocol, cipherstream, message, 0, 0);
629   log_debug ("mime_decrypt returned %d (%s)", err, gpg_strerror (err));
630   if (err)
631     {
632       char buf[200];
633       
634       snprintf (buf, sizeof buf, "Decryption failed (%s)", gpg_strerror (err));
635       MessageBox (NULL, buf, "GpgOL", MB_ICONINFORMATION|MB_OK);
636     }
637   cipherstream->Release ();
638   retval = 0;
639
640  leave:
641   mapi_release_attach_table (table);
642   return retval;
643 }
644
645
646 \f
647
648 /* Return an array of strings with the recipients of the message. On
649    success a malloced array is returned containing allocated strings
650    for each recipient.  The end of the array is marked by NULL.
651    Caller is responsible for releasing the array.  On failure NULL is
652    returned.  */
653 static char **
654 get_recipients (LPMESSAGE message)
655 {
656   static SizedSPropTagArray (1L, PropRecipientNum) = {1L, {PR_EMAIL_ADDRESS}};
657   HRESULT hr;
658   LPMAPITABLE lpRecipientTable = NULL;
659   LPSRowSet lpRecipientRows = NULL;
660   unsigned int rowidx;
661   LPSPropValue row;
662   char **rset;
663   int rsetidx;
664
665
666   if (!message)
667     return NULL;
668
669   hr = message->GetRecipientTable (0, &lpRecipientTable);
670   if (FAILED (hr)) 
671     {
672       log_debug_w32 (-1, "%s:%s: GetRecipientTable failed", SRCNAME, __func__);
673       return NULL;
674     }
675
676   hr = HrQueryAllRows (lpRecipientTable, (LPSPropTagArray) &PropRecipientNum,
677                        NULL, NULL, 0L, &lpRecipientRows);
678   if (FAILED (hr)) 
679     {
680       log_debug_w32 (-1, "%s:%s: HrQueryAllRows failed", SRCNAME, __func__);
681       if (lpRecipientTable)
682         lpRecipientTable->Release();
683       return NULL;
684     }
685
686   rset = (char**)xcalloc (lpRecipientRows->cRows+1, sizeof *rset);
687
688   for (rowidx=0, rsetidx=0; rowidx < lpRecipientRows->cRows; rowidx++)
689     {
690       if (!lpRecipientRows->aRow[rowidx].cValues)
691         continue;
692       row = lpRecipientRows->aRow[rowidx].lpProps;
693
694       switch (PROP_TYPE (row->ulPropTag))
695         {
696         case PT_UNICODE:
697           if ((rset[rsetidx] = wchar_to_utf8 (row->Value.lpszW)))
698             rsetidx++;
699           else
700             log_debug ("%s:%s: error converting recipient to utf8\n",
701                        SRCNAME, __func__);
702           break;
703       
704         case PT_STRING8: /* Assume ASCII. */
705           rset[rsetidx++] = xstrdup (row->Value.lpszA);
706           break;
707           
708         default:
709           log_debug ("%s:%s: proptag=0x%08lx not supported\n",
710                      SRCNAME, __func__, row->ulPropTag);
711           break;
712         }
713     }
714
715   if (lpRecipientTable)
716     lpRecipientTable->Release();
717   if (lpRecipientRows)
718     FreeProws(lpRecipientRows); 
719   
720   log_debug ("%s:%s: got %d recipients:\n", SRCNAME, __func__, rsetidx);
721   for (rsetidx=0; rset[rsetidx]; rsetidx++)
722     log_debug ("%s:%s: \t`%s'\n", SRCNAME, __func__, rset[rsetidx]);
723
724   return rset;
725 }
726
727
728 static void
729 release_recipient_array (char **recipients)
730 {
731   int idx;
732
733   if (recipients)
734     {
735       for (idx=0; recipients[idx]; idx++)
736         xfree (recipients[idx]);
737       xfree (recipients);
738     }
739 }
740
741
742
743 static int
744 sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd, int signflag)
745 {
746   gpg_error_t err;
747   char **recipients;
748
749   recipients = get_recipients (message);
750   if (!recipients || !recipients[0])
751     {
752       MessageBox (hwnd, _("No recipients to encrypt to are given"),
753                   "GpgOL", MB_ICONERROR|MB_OK);
754
755       err = gpg_error (GPG_ERR_GENERAL);
756     }
757   else
758     {
759       if (signflag)
760         err = mime_sign_encrypt (message, protocol, recipients);
761       else
762         err = mime_encrypt (message, protocol, recipients);
763       if (err)
764         {
765           char buf[200];
766           
767           snprintf (buf, sizeof buf,
768                     _("Encryption failed (%s)"), gpg_strerror (err));
769           MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
770         }
771     }
772   release_recipient_array (recipients);
773   return err;
774 }
775
776
777 /* Sign the MESSAGE.  */
778 int 
779 message_sign (LPMESSAGE message, protocol_t protocol, HWND hwnd)
780 {
781   gpg_error_t err;
782
783   err = mime_sign (message, protocol);
784   if (err)
785     {
786       char buf[200];
787       
788       snprintf (buf, sizeof buf,
789                 _("Signing failed (%s)"), gpg_strerror (err));
790       MessageBox (hwnd, buf, "GpgOL", MB_ICONERROR|MB_OK);
791     }
792   return err;
793 }
794
795
796
797 /* Encrypt the MESSAGE.  */
798 int 
799 message_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
800 {
801   return sign_encrypt (message, protocol, hwnd, 0);
802 }
803
804
805 /* Sign+Encrypt the MESSAGE.  */
806 int 
807 message_sign_encrypt (LPMESSAGE message, protocol_t protocol, HWND hwnd)
808 {
809   return sign_encrypt (message, protocol, hwnd, 1);
810 }
811
812
813