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

Annotation of /trunk/Src/wptClipboard.cpp

Parent Directory Parent Directory | Revision Log Revision Log


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


1 twoaday 273 /* wptClipboard.cpp - GPG related clipboard functions
2 twoaday 328 * Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Timo Schulz
3 twoaday 121 * 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 twoaday 271 #include "wptTypes.h"
30 twoaday 328 #include "wptUTF8.h"
31 twoaday 121
32 twoaday 271 /* Default buffer size. */
33     #define BUFSIZE 192
34    
35 twoaday 121 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 twoaday 328 *r_val = 0;
51 twoaday 121 if (OpenClipboard (NULL) == FALSE)
52 twoaday 328 return gpg_error (GPG_ERR_INTERNAL);
53 twoaday 121
54     clipmem = GetClipboardData (CF_TEXT);
55 twoaday 328 if (!clipmem) {
56     CloseClipboard();
57     return gpg_error (GPG_ERR_NO_DATA);
58     }
59 twoaday 121
60     clipdata = (char *) GlobalLock (clipmem);
61 twoaday 328 if (!clipdata) {
62     CloseClipboard ();
63     return gpg_error (GPG_ERR_INTERNAL);
64     }
65    
66 twoaday 121 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 twoaday 328 if (strstr (data, "BEGIN PGP MESSAGE"))
90 twoaday 121 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 twoaday 226 if (strstr (data, "BEGIN PGP PRIVATE KEY BLOCK") ||
98     strstr (data, "BEGIN PGP SECRET KEY BLOCK"))
99 twoaday 121 type |= PGP_SECKEY;
100 twoaday 328 // Actually this is no type but rather a flag to
101     // indicate the that the data is 'dash escaped'
102 twoaday 121 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 twoaday 271 if (OpenClipboard (NULL) == FALSE)
122 twoaday 121 return gpg_error (GPG_ERR_INTERNAL);
123 twoaday 271 clipmem = GetClipboardData (CF_TEXT);
124     if (!clipmem) {
125 twoaday 121 *r_val = 0;
126 twoaday 328 CloseClipboard();
127     return gpg_error (GPG_ERR_NO_DATA);
128 twoaday 121 }
129 twoaday 271 clipdata = (char *) GlobalLock (clipmem);
130 twoaday 121 if (!clipdata) {
131 twoaday 328 *r_val = 0;
132     CloseClipboard();
133     return gpg_error (GPG_ERR_INTERNAL);
134 twoaday 121 }
135 twoaday 328 if (strlen (clipdata) > 32 &&
136     strstr (clipdata, "-----BEGIN PGP") &&
137     strstr (clipdata, "-----END PGP"))
138 twoaday 121 *r_val = 1;
139     GlobalUnlock (clipmem);
140     gpg_clip_parse_pgpid (clipdata, r_type);
141     CloseClipboard ();
142 twoaday 328 return gpg_error (GPG_ERR_NO_ERROR);
143 twoaday 121 }
144    
145    
146 twoaday 271 /* Return the OpenPGP data format of the clipboard in @r_type.
147     Return 0 on success. */
148 twoaday 121 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 twoaday 328 CloseClipboard ();
159     return gpg_error (GPG_ERR_NO_DATA);
160 twoaday 121 }
161 twoaday 328 clipdata = (char *)GlobalLock (clipmem);
162 twoaday 121 if (!clipdata) {
163 twoaday 328 CloseClipboard ();
164     return gpg_error (GPG_ERR_INTERNAL);
165 twoaday 121 }
166     gpg_clip_parse_pgpid (clipdata, r_type);
167     GlobalUnlock (clipmem);
168     CloseClipboard ();
169 twoaday 328 return gpg_error (GPG_ERR_NO_ERROR);
170 twoaday 121 }
171    
172    
173 twoaday 319 /* Retrieve the text of the clipboard.
174 twoaday 121 Return value: the text as a string, NULL otherwise. */
175     static char*
176     get_w32_clip_text (void)
177     {
178 twoaday 328 char *private_clip;
179     char *clip;
180 twoaday 121 size_t size;
181     HANDLE cb;
182    
183     if (!OpenClipboard (NULL))
184     return NULL;
185     cb = GetClipboardData (CF_TEXT);
186 twoaday 328 if (!cb) {
187     CloseClipboard ();
188     return NULL;
189     }
190 twoaday 121 private_clip = (char *)GlobalLock (cb);
191 twoaday 328 if (!private_clip) {
192     CloseClipboard ();
193     return NULL;
194     }
195 twoaday 121 size = strlen (private_clip);
196 twoaday 328 clip = new char[size+1];
197     if (!clip)
198     BUG (NULL);
199     memset (clip, 0, size+1);
200 twoaday 121 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 twoaday 328 return;
216 twoaday 121 cb = GlobalAlloc (GHND, size+1);
217     if (!cb)
218 twoaday 328 BUG (NULL);
219 twoaday 121 private_data = (char*)GlobalLock (cb);
220 twoaday 328 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 twoaday 121
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 twoaday 271 gpgme_error_t err;
241 twoaday 121 gpgme_data_t mdh;
242 twoaday 271 const char *s;
243 twoaday 328 char line[BUFSIZE+256];
244     int found_version, hash_seen;
245 twoaday 121 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 twoaday 328
254     found_version = 0;
255     hash_seen = 0;
256 twoaday 271 /* Pattern we search for. */
257     s = "Version: GnuPG";
258 twoaday 121 gpgme_data_rewind (*r_dh);
259 twoaday 328 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 twoaday 271 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 twoaday 328 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 twoaday 121 }
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 twoaday 328
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 twoaday 121 /* 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 twoaday 328 gpgme_error_t
438     gpg_data_release_to_clipboard (gpgme_data_t dh, int chg_ver)
439 twoaday 121 {
440     gpgme_data_t in;
441     char *clip_text;
442     size_t n;
443    
444     if (!dh)
445 twoaday 328 return gpg_error (GPG_ERR_INV_ARG);
446    
447 twoaday 121 in = dh;
448     if (chg_ver)
449     gpg_data_change_version (&in);
450 twoaday 328
451 twoaday 121 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 twoaday 271 wipememory (clip_text, n);
455 twoaday 121 gpgme_free (clip_text);
456     }
457 twoaday 328
458     return gpg_error (GPG_ERR_NO_ERROR);
459 twoaday 121 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26