42a02565255228552a9c38fedb608ab17382e9b4
[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)
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, "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)
180 {
181   HRESULT hr;
182   SPropValue prop;
183   //  BOOL dummy_bool;
184   const char *s;
185   
186   /* Decide whether we need to use the Unicode version. */
187   for (s=string; *s && !(*s & 0x80); s++)
188     ;
189   if (*s)
190     {
191       prop.ulPropTag = PR_BODY_W;
192       prop.Value.lpszW = utf8_to_wchar (string);
193       hr = HrSetOneProp (message, &prop);
194       xfree (prop.Value.lpszW);
195     }
196   else /* Only plain ASCII. */
197     {
198       prop.ulPropTag = PR_BODY_A;
199       prop.Value.lpszA = (CHAR*)string;
200       hr = HrSetOneProp (message, &prop);
201     }
202   if (hr != S_OK)
203     {
204       log_debug ("%s:%s: HrSetOneProp failed: hr=%#lx\n",
205                  __FILE__, __func__, hr); 
206       return gpg_error (GPG_ERR_GENERAL);
207     }
208 // When enabling the code below the result is that (under OL2003
209 // standalone) the message is sent with an empty body.  Thus we don't
210 // do it.  Note further that the specs say that when dummy_bool
211 // returns true, SaveChanges must be called on the message.
212 //   hr = RTFSync (message, RTF_SYNC_BODY_CHANGED, &dummy_bool);
213 //   if (hr != S_OK)
214 //     log_debug ("%s:%s: RTFSync failed: hr=%#lx - error ignored",
215 //                __FILE__, __func__, hr); 
216   return 0;
217 }