2003-04-28 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / conversion.c
1 /* conversion.c - String conversion helper functions.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003 g10 Code GmbH
4  
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    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    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16  
17    You should have received a copy of the GNU General Public License
18    along with GPGME; if not, write to the Free Software Foundation,
19    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <string.h>
26 #include <errno.h>
27 #include <ctype.h>
28 #include <sys/types.h>
29 #include <stdlib.h>
30
31 #include "gpgme.h"
32 #include "util.h"
33
34 \f
35 /* Convert two hexadecimal digits from STR to the value they
36    represent.  Returns -1 if one of the characters is not a
37    hexadecimal digit.  */
38 int
39 _gpgme_hextobyte (const unsigned char *str)
40 {
41   int val = 0;
42   int i;
43
44 #define NROFHEXDIGITS 2
45   for (i = 0; i < NROFHEXDIGITS; i++)
46     {
47       if (*str >= '0' && *str <= '9')
48         val += *str - '0';
49       else if (*str >= 'A' && *str <= 'F')
50         val += 10 + *str - 'A';
51       else if (*str >= 'a' && *str <= 'f')
52         val += 10 + *str - 'a';
53       else
54         return -1;
55       if (i < NROFHEXDIGITS - 1)
56         val *= 16;
57       str++;
58     }
59   return val;
60 }
61
62
63 /* Decode the C formatted string SRC and store the result in the
64    buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
65    large enough buffer is allocated with malloc and *DESTP is set to
66    the result.  Currently, LEN is only used to specify if allocation
67    is desired or not, the caller is expected to make sure that *DESTP
68    is large enough if LEN is not zero.  */
69 GpgmeError
70 _gpgme_decode_c_string (const char *src, char **destp, int len)
71 {
72   char *dest;
73
74   /* Set up the destination buffer.  */
75   if (len)
76     {
77       if (len < strlen (src) + 1)
78         return GPGME_General_Error;
79
80       dest = *destp;
81     }
82   else
83     {
84       /* The converted string will never be larger than the original
85          string.  */
86       dest = malloc (strlen (src) + 1);
87       if (!dest)
88         return GPGME_Out_Of_Core;
89
90       *destp = dest;
91     }
92
93   /* Convert the string.  */
94   while (*src)
95     {
96       if (*src != '\\')
97         {
98           *(dest++) = *(src++);
99           continue;
100         }
101
102       switch (src[1])
103         {
104 #define DECODE_ONE(match,result)        \
105         case match:                     \
106           src += 2;                     \
107           *(dest++) = result;           \
108           break;
109
110           DECODE_ONE ('\'', '\'');
111           DECODE_ONE ('\"', '\"');
112           DECODE_ONE ('\?', '\?');
113           DECODE_ONE ('\\', '\\');
114           DECODE_ONE ('a', '\a');
115           DECODE_ONE ('b', '\b');
116           DECODE_ONE ('f', '\f');
117           DECODE_ONE ('n', '\n');
118           DECODE_ONE ('r', '\r');
119           DECODE_ONE ('t', '\t');
120           DECODE_ONE ('v', '\v');
121
122         case 'x':
123           {
124             int val = _gpgme_hextobyte (&src[2]);
125
126             if (val == -1)
127               {
128                 /* Should not happen.  */
129                 *(dest++) = *(src++);
130                 *(dest++) = *(src++);
131                 if (*src)
132                   *(dest++) = *(src++);
133                 if (*src)
134                   *(dest++) = *(src++);
135               }
136             else
137               {
138                 if (!val)
139                   {
140                     /* A binary zero is not representable in a C
141                        string.  */
142                     *(dest++) = '\\';
143                     *(dest++) = '0'; 
144                   }
145                 else 
146                   *((unsigned char *) dest++) = val;
147                 src += 4;
148               }
149           }
150
151         default:
152           {
153             /* Should not happen.  */
154             *(dest++) = *(src++);
155             *(dest++) = *(src++);
156           }
157         } 
158     }
159   *(dest++) = 0;
160
161   return 0;
162 }
163
164
165 /* Decode the percent escaped string SRC and store the result in the
166    buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
167    large enough buffer is allocated with malloc and *DESTP is set to
168    the result.  Currently, LEN is only used to specify if allocation
169    is desired or not, the caller is expected to make sure that *DESTP
170    is large enough if LEN is not zero.  */
171 GpgmeError
172 _gpgme_decode_percent_string (const char *src, char **destp, int len)
173 {
174   char *dest;
175
176   /* Set up the destination buffer.  */
177   if (len)
178     {
179       if (len < strlen (src) + 1)
180         return GPGME_General_Error;
181
182       dest = *destp;
183     }
184   else
185     {
186       /* The converted string will never be larger than the original
187          string.  */
188       dest = malloc (strlen (src) + 1);
189       if (!dest)
190         return GPGME_Out_Of_Core;
191
192       *destp = dest;
193     }
194
195   /* Convert the string.  */
196   while (*src)
197     {
198       if (*src != '%')
199         {
200           *(dest++) = *(src++);
201           continue;
202         }
203       else
204         {
205           int val = _gpgme_hextobyte (&src[1]);
206           
207           if (val == -1)
208             {
209               /* Should not happen.  */
210               *(dest++) = *(src++);
211               if (*src)
212                 *(dest++) = *(src++);
213               if (*src)
214                 *(dest++) = *(src++);
215             }
216           else
217             {
218               if (!val)
219                 {
220                   /* A binary zero is not representable in a C
221                      string.  */
222                   *(dest++) = '\\';
223                   *(dest++) = '0'; 
224                 }
225               else 
226                 *((unsigned char *) dest++) = val;
227               src += 3;
228             }
229         }
230     }
231   *(dest++) = 0;
232
233   return 0;
234 }
235
236 \f
237 static struct
238 {
239   char *name;
240   GpgmeError err;
241 } gnupg_errors[] =
242   {
243     { "EOF", GPGME_EOF },
244     { "No_Error", GPGME_No_Error },
245     { "General_Error", GPGME_General_Error },
246     { "Out_Of_Core", GPGME_Out_Of_Core },
247     { "Invalid_Value", GPGME_Invalid_Value },
248     { "IO_Error", GPGME_General_Error },
249     { "Resource_Limit", GPGME_General_Error },
250     { "Internal_Error", GPGME_General_Error },
251     { "Bad_Certificate", GPGME_Invalid_Key },
252     { "Bad_Certificate_Chain", GPGME_General_Error },
253     { "Missing_Certificate", GPGME_No_Public_Key },
254     { "No_Data", GPGME_No_Data },
255     { "Bad_Signature", GPGME_Bad_Signature },
256     { "Not_Implemented", GPGME_Not_Implemented },
257     { "Conflict", GPGME_Conflict },
258     { "Bug", GPGME_General_Error },
259     { "Read_Error", GPGME_General_Error },
260     { "Write_Error", GPGME_General_Error },
261     { "Invalid_Line", GPGME_General_Error },
262     { "Incomplete_Line", GPGME_General_Error },
263     { "Invalid_Response", GPGME_General_Error },
264     { "Agent_Error", GPGME_General_Error },
265     { "No_Public_Key", GPGME_No_Public_Key },
266     { "No_Secret_Key", GPGME_No_Secret_Key },
267     { "File_Open_Error", GPGME_General_Error },
268     { "File_Create_Error", GPGME_General_Error },
269     { "File_Error", GPGME_General_Error },
270     { "Not_Supported", GPGME_General_Error },
271     { "Invalid_Data", GPGME_General_Error },
272     { "Assuan_Server_Fault", GPGME_General_Error },
273     { "Assuan_Error", GPGME_General_Error },
274     { "Invalid_Session_Key", GPGME_General_Error },
275     { "Invalid_Sexp", GPGME_General_Error },
276     { "Unsupported_Algorithm", GPGME_Unsupported_Algorithm },
277     { "No_PIN_Entry", GPGME_Invalid_Engine },
278     { "PIN_Entry_Error", GPGME_Invalid_Engine },
279     { "Bad_PIN", GPGME_Bad_Passphrase },
280     { "Bad_Passphrase", GPGME_Bad_Passphrase },
281     { "Invalid_Name", GPGME_General_Error },
282     { "Bad_Public_Key", GPGME_General_Error },
283     { "Bad_Secret_Key", GPGME_General_Error },
284     { "Bad_Data", GPGME_General_Error },
285     { "Invalid_Parameter", GPGME_General_Error },
286     { "Tribute_to_D_A", GPGME_General_Error },
287     { "No_Dirmngr", GPGME_Invalid_Engine },
288     { "Dirmngr_Error", GPGME_General_Error },
289     { "Certificate_Revoked", GPGME_Key_Revoked },
290     { "No_CRL_Known", GPGME_No_CRL_Known },
291     { "CRL_Too_Old", GPGME_CRL_Too_Old },
292     { "Line_Too_Long", GPGME_General_Error },
293     { "Not_Trusted", GPGME_Key_Not_Trusted },
294     { "Canceled", GPGME_Canceled },
295     { "Bad_CA_Certificate", GPGME_General_Error },
296     { "Certificate_Expired", GPGME_Key_Expired },
297     { "Certificate_Too_Young", GPGME_Invalid_Key },
298     { "Unsupported_Certificate", GPGME_General_Error },
299     { "Unknown_Sexp", GPGME_General_Error },
300     { "Unsupported_Protection", GPGME_General_Error },
301     { "Corrupted_Protection", GPGME_General_Error },
302     { "Ambiguous_Name", GPGME_Ambiguous_Specification },
303     { "Card_Error", GPGME_General_Error },
304     { "Card_Reset", GPGME_General_Error },
305     { "Card_Removed", GPGME_General_Error },
306     { "Invalid_Card", GPGME_General_Error },
307     { "Card_Not_Present", GPGME_General_Error },
308     { "No_PKCS15_App", GPGME_General_Error },
309     { "Not_Confirmed", GPGME_General_Error },
310     { "Configuration_Error", GPGME_General_Error },
311     { "No_Policy_Match", GPGME_Policy_Mismatch },
312     { "Invalid_Index", GPGME_General_Error },
313     { "Invalid_Id", GPGME_General_Error },
314     { "No_Scdaemon", GPGME_Invalid_Engine },
315     { "Scdaemon_Error", GPGME_General_Error },
316     { "Unsupported_Protocol", GPGME_General_Error },
317     { "Bad_PIN_Method", GPGME_General_Error },
318     { "Card_Not_Initialized", GPGME_General_Error },
319     { "Unsupported_Operation", GPGME_General_Error },
320     { "Wrong_Key_Usage", GPGME_Wrong_Key_Usage }
321   };
322     
323
324 GpgmeError
325 _gpgme_map_gnupg_error (char *err)
326 {
327   int i;
328
329   for (i = 0; i < DIM (gnupg_errors); i++)
330     if (!strcmp (gnupg_errors[i].name, err))
331       return gnupg_errors[i].err;
332
333   return GPGME_General_Error;
334 }
335
336 \f
337 GpgmeError
338 _gpgme_data_append (GpgmeData dh, const char *buffer, size_t length)
339 {
340   if (!dh || !buffer)
341     return GPGME_Invalid_Value;
342
343   do
344     {
345       ssize_t amt = gpgme_data_write (dh, buffer, length);
346       if (amt == 0 || (amt < 0 && errno != EINTR))
347         return GPGME_File_Error;
348       buffer += amt;
349       length -= amt;
350     }
351   while (length > 0);
352
353   return 0;
354 }
355
356
357 GpgmeError
358 _gpgme_data_append_string (GpgmeData dh, const char *str)
359 {
360   if (!str)
361     return 0;
362
363   return _gpgme_data_append (dh, str, strlen (str));
364 }
365
366
367 GpgmeError
368 _gpgme_data_append_for_xml (GpgmeData dh, const char *buffer, size_t len)
369 {
370   const char *text, *str;
371   size_t count;
372   int err = 0;
373
374   if (!dh || !buffer)
375     return GPGME_Invalid_Value;
376
377   do
378     {
379       text = NULL;
380       str = buffer;
381       for (count = len; count && !text; str++, count--)
382         {
383           if (*str == '<')
384             text = "&lt;";
385           else if (*str == '>')
386             text = "&gt;";  /* Not sure whether this is really needed.  */
387           else if (*str == '&')
388             text = "&amp;";
389           else if (!*str)
390             text = "&#00;";
391         }
392       if (text)
393         {
394           str--;
395           count++;
396         }
397       if (str != buffer)
398         err = _gpgme_data_append (dh, buffer, str - buffer);
399       if (!err && text)
400         {
401           err = _gpgme_data_append_string (dh, text);
402           str++;
403           count--;
404         }
405       buffer = str;
406       len = count;
407     }
408   while (!err && len);
409   return err;
410 }
411
412
413 /* Append a string to DATA and convert it so that the result will be
414    valid XML.  */
415 GpgmeError
416 _gpgme_data_append_string_for_xml (GpgmeData dh, const char *str)
417 {
418   return _gpgme_data_append_for_xml (dh, str, strlen (str));
419 }
420
421
422 /* Append a string with percent style (%XX) escape characters as
423    XML.  */
424 GpgmeError
425 _gpgme_data_append_percentstring_for_xml (GpgmeData dh, const char *str)
426 {
427   const unsigned char *src;
428   unsigned char *buf, *dst;
429   int val;
430   GpgmeError err;
431
432   buf = malloc (strlen (str));
433   dst = buf;
434   for (src = str; *src; src++)
435     {
436       if (*src == '%' && (val = _gpgme_hextobyte (src + 1)) != -1)
437         {
438           *dst++ = val;
439           src += 2;
440         }
441       else
442         *dst++ = *src;
443     }
444
445   err = _gpgme_data_append_for_xml (dh, buf, dst - buf);
446   free (buf);
447   return err;
448 }