aef59930f6aa012547cfd8b1561d2bedc632506a
[gpgol.git] / src / display.cpp
1 /* display.cpp - Helper functions to display messages.
2  *      Copyright (C) 2005 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 #include <config.h>
23
24 #include <time.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <windows.h>
28
29 #include "mymapi.h"
30 #include "mymapitags.h"
31 #include "intern.h"
32 #include "display.h"
33
34
35 /* Check wether the string BODY is HTML formatted. */
36 int 
37 is_html_body (const char *body)
38 {
39   char *p1, *p2;
40   
41   /* XXX: it is possible but unlikely that the message text
42      contains the used keywords. */
43   p1 = strstr (body, "<HTML>");
44   p2 = strstr (body, "</HTML>");
45   if (p1 && p2)
46     return 1;
47   p1 = strstr (body, "<html>");
48   p2 = strstr (body, "</html>");
49   if (p1 && p2)
50         return 1;
51   /* XXX: use case insentensive strstr version. */
52   return 0;
53 }
54
55
56 /* Create a new body from body with suitable line endings. Caller must
57    release the result. */
58 char *
59 add_html_line_endings (const char *body)
60 {
61   size_t count;
62   const char *s;
63   char *p, *result;
64
65   for (count=0, s = body; *s; s++)
66     if (*s == '\n')
67       count++;
68   
69   result = (char*)xmalloc ((s - body) + count*10 + 1);
70   
71   for (s=body, p = result; *s; s++ )
72     if (*s == '\n')
73       p = stpcpy (p, "&nbsp;<br>\n");
74     else
75       *p++ = *s;
76   *p = 0;
77   
78   return result;
79   
80 }
81
82
83 /* We need this to find the mailer window because we directly change
84    the text of the window instead of the MAPI object itself.  To do
85    this we walk all windows to find a PGP signature.  */
86 static HWND
87 find_message_window (HWND parent)
88 {
89   HWND child;
90
91   if (!parent)
92     return NULL;
93
94   child = GetWindow (parent, GW_CHILD);
95   while (child)
96     {
97       char buf[1024+1];
98       HWND w;
99       size_t len;
100       const char *s;
101
102       /* OL 2003 SP1 German uses this class name for the main
103          inspector window.  We hope that no other windows uses this
104          class name.  As a fallback we keep on testing for PGP
105          strings, but this does not work for PGP/MIME or already
106          decrypted messages. */
107       len = GetClassName (child, buf, sizeof buf - 1);
108       if (len && !strcmp (buf, "RichEdit20W"))
109         {
110           log_debug ("found class RichEdit20W");
111           return child;
112         }
113       
114       memset (buf, 0, sizeof (buf));
115       GetWindowText (child, buf, sizeof (buf)-1);
116       len = strlen (buf);
117       if (len > 22
118           && (s = strstr (buf, "-----BEGIN PGP "))
119           &&  (!strncmp (s+15, "MESSAGE-----", 12)
120                || !strncmp (s+15, "SIGNED MESSAGE-----", 19)))
121         return child;
122       w = find_message_window (child);
123       if (w)
124         return w;
125       child = GetNextWindow (child, GW_HWNDNEXT);       
126     }
127
128   return NULL;
129 }
130
131
132 /* Update the display using the message MSG.  Return 0 on success. */
133 int
134 update_display (HWND hwnd, GpgMsg *msg, void *exchange_cb, bool is_html)
135 {
136   HWND window;
137
138   window = find_message_window (hwnd);
139   if (window)
140     {
141       const char *string, *s;
142
143       log_debug ("%s:%s: window handle %p\n", __FILE__, __func__, window);
144       string = msg->getDisplayText ();
145       
146       /* Decide whether we need to use the Unicode version. */
147       for (s=string; *s && !(*s & 0x80); s++)
148         ;
149       if (*s)
150         {
151           wchar_t *tmp = utf8_to_wchar (string);
152           SetWindowTextW (window, tmp);
153           xfree (tmp);
154         }
155       else
156         SetWindowTextA (window, string);
157       log_debug ("%s:%s: window text is now `%s'",
158                  __FILE__, __func__, string);
159       return 0;
160     }
161   else if (exchange_cb && !opt.compat.no_oom_write)
162     {
163       log_debug ("updating display using OOM");
164       return put_outlook_property (exchange_cb, is_html? "HTMLBody":"Body",
165                                    msg->getDisplayText ());
166     }
167   else
168     {
169       log_debug ("%s: window handle not found for parent %p\n",
170                  __func__, hwnd);
171       return -1;
172     }
173 }
174
175
176 /* Set the body of MESSAGE to STRING.  Returns 0 on success or an
177    error code otherwise. */
178 int
179 set_message_body (LPMESSAGE message, const char *string, bool is_html)
180 {
181   HRESULT hr;
182   SPropValue prop;
183   SPropTagArray proparray;
184   const char *s;
185   
186   assert (message);
187   
188   /* Decide whether we need to use the Unicode version. */
189   for (s=string; *s && !(*s & 0x80); s++)
190     ;
191   if (*s)
192     {
193       prop.ulPropTag = is_html? PR_BODY_HTML_W : PR_BODY_W;
194       prop.Value.lpszW = utf8_to_wchar (string);
195       hr = HrSetOneProp (message, &prop);
196       xfree (prop.Value.lpszW);
197     }
198   else /* Only plain ASCII. */
199     {
200       prop.ulPropTag = is_html? PR_BODY_HTML_A : PR_BODY_A;
201       prop.Value.lpszA = (CHAR*)string;
202       hr = HrSetOneProp (message, &prop);
203     }
204   if (hr != S_OK)
205     {
206       log_debug ("%s:%s: HrSetOneProp failed: hr=%#lx\n",
207                  __FILE__, __func__, hr); 
208       return gpg_error (GPG_ERR_GENERAL);
209     }
210
211   /* Instead of using RTF Sync, we simply delete any RTF property. */
212   proparray.cValues = 1;
213   proparray.aulPropTag[0] = PR_RTF_COMPRESSED;
214   hr = message->DeleteProps (&proparray, NULL);
215   if (hr != S_OK)
216     log_debug ("%s:%s: DeleteProps failed: hr=%#lx\n", __FILE__, __func__, hr);
217   
218     
219   return 0;
220 }