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

Contents of /trunk/Src/wptClipboard.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26