/[winpt]/trunk/Src/wptClipboard.cpp
ViewVC logotype

Contents of /trunk/Src/wptClipboard.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 328 - (show annotations)
Fri Sep 25 16:07:38 2009 UTC (15 years, 5 months ago) by twoaday
File size: 11920 byte(s)


1 /* wptClipboard.cpp - GPG related clipboard functions
2 * Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Timo Schulz
3 * Copyright (C) 2005 g10 Code GmbH
4 *
5 * This file is part of WinPT.
6 *
7 * WinPT is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * WinPT is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21
22 #include <windows.h>
23 #include <gpgme.h>
24
25 #include "wptW32API.h"
26 #include "wptCrypto.h"
27 #include "wptVersion.h"
28 #include "wptGPG.h"
29 #include "wptTypes.h"
30 #include "wptUTF8.h"
31
32 /* Default buffer size. */
33 #define BUFSIZE 192
34
35 gpgme_error_t gpg_data_wrap_lines (gpgme_data_t *r_dh, size_t wraplen);
36
37 /* XXX: define clipboard errors. */
38
39 /* Check if the clipboard contains text. @r_val contains
40 one text data is available.
41 Return value: 0 on success. */
42 gpgme_error_t
43 gpg_clip_istext_avail (int *r_val)
44 {
45 HANDLE clipmem;
46 char *clipdata;
47
48 if (!r_val)
49 return gpg_error (GPG_ERR_INV_ARG);
50 *r_val = 0;
51 if (OpenClipboard (NULL) == FALSE)
52 return gpg_error (GPG_ERR_INTERNAL);
53
54 clipmem = GetClipboardData (CF_TEXT);
55 if (!clipmem) {
56 CloseClipboard();
57 return gpg_error (GPG_ERR_NO_DATA);
58 }
59
60 clipdata = (char *) GlobalLock (clipmem);
61 if (!clipdata) {
62 CloseClipboard ();
63 return gpg_error (GPG_ERR_INTERNAL);
64 }
65
66 if (strlen (clipdata) > 0)
67 *r_val = 1;
68 GlobalUnlock (clipmem);
69 CloseClipboard ();
70 return 0;
71 }
72
73
74 /* Figure out what kind of PGP data is stored in @data.
75 @r_type contains the ORed types on success.
76 Return value: 0 on success. */
77 gpgme_error_t
78 gpg_clip_parse_pgpid (const char *data, int *r_type)
79 {
80 int type;
81
82 if (!data)
83 return gpg_error (GPG_ERR_INV_ARG);
84 if (strlen (data) < 19) {
85 *r_type = 0;
86 return 0;
87 }
88 type = 0;
89 if (strstr (data, "BEGIN PGP MESSAGE"))
90 type |= PGP_MESSAGE;
91 if (strstr (data, "BEGIN PGP SIGNED MESSAGE"))
92 type |= PGP_CLEARSIG;
93 if (strstr (data, "BEGIN PGP SIGNATURE"))
94 type |= PGP_SIG;
95 if (strstr (data, "BEGIN PGP PUBLIC KEY BLOCK"))
96 type |= PGP_PUBKEY;
97 if (strstr (data, "BEGIN PGP PRIVATE KEY BLOCK") ||
98 strstr (data, "BEGIN PGP SECRET KEY BLOCK"))
99 type |= PGP_SECKEY;
100 // Actually this is no type but rather a flag to
101 // indicate the that the data is 'dash escaped'
102 if (strstr (data, "- -----BEGIN"))
103 type |= PGP_DASH_ESCAPED;
104 *r_type = type;
105 return 0;
106 }
107
108
109 /* Check if the clipboard is already OpenPGP protected.
110 @r_type optional returns the type and @r_val contains
111 1 for yes.
112 Return value: 0 on success. */
113 gpgme_error_t
114 gpg_clip_is_secured (int *r_type, int *r_val)
115 {
116 HANDLE clipmem;
117 char *clipdata;
118
119 if (!r_val)
120 return gpg_error (GPG_ERR_INV_ARG);
121 if (OpenClipboard (NULL) == FALSE)
122 return gpg_error (GPG_ERR_INTERNAL);
123 clipmem = GetClipboardData (CF_TEXT);
124 if (!clipmem) {
125 *r_val = 0;
126 CloseClipboard();
127 return gpg_error (GPG_ERR_NO_DATA);
128 }
129 clipdata = (char *) GlobalLock (clipmem);
130 if (!clipdata) {
131 *r_val = 0;
132 CloseClipboard();
133 return gpg_error (GPG_ERR_INTERNAL);
134 }
135 if (strlen (clipdata) > 32 &&
136 strstr (clipdata, "-----BEGIN PGP") &&
137 strstr (clipdata, "-----END PGP"))
138 *r_val = 1;
139 GlobalUnlock (clipmem);
140 gpg_clip_parse_pgpid (clipdata, r_type);
141 CloseClipboard ();
142 return gpg_error (GPG_ERR_NO_ERROR);
143 }
144
145
146 /* Return the OpenPGP data format of the clipboard in @r_type.
147 Return 0 on success. */
148 gpgme_error_t
149 gpg_clip_get_pgptype (int *r_type)
150 {
151 HANDLE clipmem;
152 char *clipdata;
153
154 if (OpenClipboard (NULL) == FALSE)
155 return gpg_error (GPG_ERR_INTERNAL);
156 clipmem = GetClipboardData (CF_TEXT);
157 if (!clipmem) {
158 CloseClipboard ();
159 return gpg_error (GPG_ERR_NO_DATA);
160 }
161 clipdata = (char *)GlobalLock (clipmem);
162 if (!clipdata) {
163 CloseClipboard ();
164 return gpg_error (GPG_ERR_INTERNAL);
165 }
166 gpg_clip_parse_pgpid (clipdata, r_type);
167 GlobalUnlock (clipmem);
168 CloseClipboard ();
169 return gpg_error (GPG_ERR_NO_ERROR);
170 }
171
172
173 /* Retrieve the text of the clipboard.
174 Return value: the text as a string, NULL otherwise. */
175 static char*
176 get_w32_clip_text (void)
177 {
178 char *private_clip;
179 char *clip;
180 size_t size;
181 HANDLE cb;
182
183 if (!OpenClipboard (NULL))
184 return NULL;
185 cb = GetClipboardData (CF_TEXT);
186 if (!cb) {
187 CloseClipboard ();
188 return NULL;
189 }
190 private_clip = (char *)GlobalLock (cb);
191 if (!private_clip) {
192 CloseClipboard ();
193 return NULL;
194 }
195 size = strlen (private_clip);
196 clip = new char[size+1];
197 if (!clip)
198 BUG (NULL);
199 memset (clip, 0, size+1);
200 memcpy (clip, private_clip, size);
201 GlobalUnlock(cb);
202 CloseClipboard ();
203 return clip;
204 }
205
206
207 /* Set the new clipboard data to @data. */
208 static void
209 set_w32_clip_text (const char *data, int size)
210 {
211 HANDLE cb;
212 char *private_data;
213
214 if (!OpenClipboard(NULL))
215 return;
216 cb = GlobalAlloc (GHND, size+1);
217 if (!cb)
218 BUG (NULL);
219 private_data = (char*)GlobalLock (cb);
220 if (private_data != NULL) {
221 EmptyClipboard ();
222 memset (private_data, 0, size+1);
223 memcpy (private_data, data, size);
224 SetClipboardData (CF_TEXT, cb);
225 GlobalUnlock (cb);
226 }
227
228 CloseClipboard ();
229 GlobalFree (cb);
230 }
231
232
233 /* Search for the armor header 'Version:' in @r_dh and
234 add the WinPT version. On success @r_dh is replaced
235 with the modified data.
236 Return value: 0 on success. */
237 static gpgme_error_t
238 gpg_data_change_version (gpgme_data_t *r_dh)
239 {
240 gpgme_error_t err;
241 gpgme_data_t mdh;
242 const char *s;
243 char line[BUFSIZE+256];
244 int found_version, hash_seen;
245 int n;
246
247 if (!r_dh)
248 return gpg_error (GPG_ERR_INV_ARG);
249
250 err = gpgme_data_new (&mdh);
251 if (err)
252 return err;
253
254 found_version = 0;
255 hash_seen = 0;
256 /* Pattern we search for. */
257 s = "Version: GnuPG";
258 gpgme_data_rewind (*r_dh);
259 while ((n = gpg_data_readline (*r_dh, line, BUFSIZE-1)) > 0) {
260 if (found_version == 0 && !strncmp (line, "Hash:", 5)) {
261 // If we found a cleartext signature, we do not add a
262 // charset header in the signed area since it would broke
263 // the signature
264 hash_seen = 1;
265 }
266
267 if (!strncmp (line, "\r\n", 2) && found_version == 0 &&
268 hash_seen == 0) {
269 // If the user decided she will not tell what client she
270 // used, we will also omit the version and just output
271 // the charset info
272 line[strlen (line)- 2]=0;
273 strcat (line, /*"Version: WinPT "PGM_VERSION"\r\n"*/
274 "Charset: UTF-8\r\n"
275 "\r\n");
276 found_version = 1;
277 }
278 if (strlen (line) > strlen (s) &&
279 !strncmp (line, s, strlen (s)) &&
280 !stristr (line, "WinPT "PGM_VERSION)) {
281 /* Do not assume a \r\n sequence. */
282 if (strstr (line, "\r\n"))
283 line[strlen (line) - 2] = '\0';
284 if (strstr (line, "\n"))
285 line[strlen (line) - 1] = '\0';
286 // We add a charset header to indicate that the application
287 // on the recipients side is able to properly decode it
288 strcat (line, " - WinPT "PGM_VERSION"\r\n"
289 "Charset: UTF-8\r\n");
290 found_version = 1;
291 }
292 gpgme_data_write (mdh, line, strlen (line));
293 }
294
295 gpgme_data_write (mdh, "", 1);
296 gpgme_data_rewind (mdh);
297 gpgme_data_release (*r_dh);
298 *r_dh = mdh;
299 return err;
300 }
301
302
303 /* The problem is, that it might be also possible that we
304 received an ciphertext which is not encoded with UTF-8.
305 Thus, we check explicitly for the 'Charset: UTF-8' header.
306 If it is not found, we assume a different encoding and
307 no decoding is performed. */
308 static int
309 has_utf8_charset (gpgme_data_t dh)
310 {
311 char buf[BUFSIZE+1];
312 int found = 0;
313
314 for (;;) {
315 int n = gpg_data_readline (dh, buf, BUFSIZE);
316 if (n < 1)
317 break;
318 // Read until the first blank line, if the
319 // header has not been found yet, it does not exist
320 if ((n == 1 && buf[0] == '\n') ||
321 (n == 2 && buf[0] == '\r'))
322 break;
323 if (!memcmp (buf, "Charset:", 8) && strstr (buf, "UTF-8")) {
324 found = 1;
325 break;
326 }
327 }
328 gpgme_data_rewind (dh);
329 return found;
330 }
331
332
333 /* Retrieve the clipboard data and create a new gpgme
334 data object @r_dh and return it. If @wraplen is > 0
335 wrap lines of the buffer.
336 Return value: 0 on success. */
337 gpgme_error_t
338 gpg_data_new_from_clipboard (gpgme_data_t *r_dh, int wraplen)
339 {
340 gpgme_error_t err = 0;
341 gpgme_data_t dh;
342 char *clip_text = NULL;
343
344 if (!r_dh)
345 return gpg_error (GPG_ERR_INV_ARG);
346 *r_dh = NULL;
347 clip_text = get_w32_clip_text ();
348 if (!clip_text)
349 return gpg_error (GPG_ERR_NO_DATA);
350 err = gpgme_data_new_from_mem (&dh, clip_text, strlen (clip_text), 1);
351 if (clip_text)
352 free (clip_text);
353 if (wraplen > 0)
354 gpg_data_wrap_lines (&dh, wraplen);
355
356 *r_dh = dh;
357 return err;
358 }
359
360 /* Retrieve the clipboard data and create a new gpgme
361 data object @r_dh and return it. If @wraplen is > 0
362 wrap lines of the buffer.
363 Return value: 0 on success. */
364 gpgme_error_t
365 gpg_data_utf8_new_from_clipboard (gpgme_data_t *r_dh,
366 int wraplen, int *r_is_utf8)
367 {
368 gpgme_error_t err = gpg_error (GPG_ERR_NO_ERROR);
369 gpgme_data_t dh;
370 char *clip_text, *utf8_text;
371
372 if (!r_dh)
373 return gpg_error (GPG_ERR_INV_ARG);
374 *r_dh = NULL;
375 clip_text = get_w32_clip_text ();
376 if (!clip_text)
377 return gpg_error (GPG_ERR_NO_DATA);
378
379
380 /* We automatically encode all clipboard data
381 into the UTF-8 format for a portable transportation. */
382 utf8_text = native_to_utf8 (clip_text);
383 free_if_alloc (clip_text); clip_text = utf8_text;
384
385 err = gpgme_data_new_from_mem (&dh, clip_text, strlen (clip_text), 1);
386 free_if_alloc (clip_text);
387 if (wraplen > 0)
388 gpg_data_wrap_lines (&dh, wraplen);
389
390 // for ARMORED data find out if the Charset header is set to UTF8
391 if (r_is_utf8)
392 *r_is_utf8 = has_utf8_charset (dh);
393
394 *r_dh = dh;
395 return err;
396 }
397
398
399
400 gpgme_error_t
401 gpg_data_release_utf8_to_clipboard (gpgme_data_t dh)
402 {
403 char *clip_text, *utf8_text;
404 size_t n;
405
406 if (!dh)
407 return gpg_error (GPG_ERR_INV_ARG);
408
409 gpgme_data_write (dh, "", 1); // make sure it's a string!
410 utf8_text = gpgme_data_release_and_get_mem (dh, &n);
411 if (!utf8_text)
412 return gpg_error (GPG_ERR_INV_VALUE);
413 if (n < 0) {
414 gpgme_free (utf8_text);
415 return gpg_error (GPG_ERR_NO_DATA);
416 }
417
418 /* We assume that all data from GPG is in UTF-8 and
419 need to be decoded. If the charset is US-ASCII,
420 this wouldn't do any harm, but if the data is ISO-XXX-XX
421 we get scrambled output. */
422 utf8_text[n] = '\0';
423 clip_text = utf8_to_native (utf8_text);
424 wipememory (utf8_text, n);
425 gpgme_free (utf8_text);
426 n = strlen (clip_text);
427 set_w32_clip_text (clip_text, n);
428 wipememory (clip_text, n);
429 free_if_alloc (clip_text);
430 return gpg_error (GPG_ERR_NO_ERROR);
431 }
432
433
434 /* Release a gpgme data object @dh and write the contents of
435 the object to the clipboard. If @chg_ver is set, modify the
436 Version: header and add WinPT information. */
437 gpgme_error_t
438 gpg_data_release_to_clipboard (gpgme_data_t dh, int chg_ver)
439 {
440 gpgme_data_t in;
441 char *clip_text;
442 size_t n;
443
444 if (!dh)
445 return gpg_error (GPG_ERR_INV_ARG);
446
447 in = dh;
448 if (chg_ver)
449 gpg_data_change_version (&in);
450
451 clip_text = gpgme_data_release_and_get_mem (in, &n);
452 if (clip_text && *clip_text) {
453 set_w32_clip_text (clip_text, n);
454 wipememory (clip_text, n);
455 gpgme_free (clip_text);
456 }
457
458 return gpg_error (GPG_ERR_NO_ERROR);
459 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26