2004-05-21 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 <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <errno.h>
29
30 #include "gpgme.h"
31 #include "util.h"
32
33
34 #define atoi_1(p)   (*(p) - '0' )
35 #define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
36 #define atoi_4(p)   ((atoi_2(p) * 100) + atoi_2((p)+2))
37
38
39 \f
40 /* Convert two hexadecimal digits from STR to the value they
41    represent.  Returns -1 if one of the characters is not a
42    hexadecimal digit.  */
43 int
44 _gpgme_hextobyte (const char *str)
45 {
46   int val = 0;
47   int i;
48
49 #define NROFHEXDIGITS 2
50   for (i = 0; i < NROFHEXDIGITS; i++)
51     {
52       if (*str >= '0' && *str <= '9')
53         val += *str - '0';
54       else if (*str >= 'A' && *str <= 'F')
55         val += 10 + *str - 'A';
56       else if (*str >= 'a' && *str <= 'f')
57         val += 10 + *str - 'a';
58       else
59         return -1;
60       if (i < NROFHEXDIGITS - 1)
61         val *= 16;
62       str++;
63     }
64   return val;
65 }
66
67
68 /* Decode the C formatted string SRC and store the result in the
69    buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
70    large enough buffer is allocated with malloc and *DESTP is set to
71    the result.  Currently, LEN is only used to specify if allocation
72    is desired or not, the caller is expected to make sure that *DESTP
73    is large enough if LEN is not zero.  */
74 gpgme_error_t
75 _gpgme_decode_c_string (const char *src, char **destp, size_t len)
76 {
77   char *dest;
78
79   /* Set up the destination buffer.  */
80   if (len)
81     {
82       if (len < strlen (src) + 1)
83         return gpg_error (GPG_ERR_INTERNAL);
84
85       dest = *destp;
86     }
87   else
88     {
89       /* The converted string will never be larger than the original
90          string.  */
91       dest = malloc (strlen (src) + 1);
92       if (!dest)
93         return gpg_error_from_errno (errno);
94
95       *destp = dest;
96     }
97
98   /* Convert the string.  */
99   while (*src)
100     {
101       if (*src != '\\')
102         {
103           *(dest++) = *(src++);
104           continue;
105         }
106
107       switch (src[1])
108         {
109 #define DECODE_ONE(match,result)        \
110         case match:                     \
111           src += 2;                     \
112           *(dest++) = result;           \
113           break;
114
115           DECODE_ONE ('\'', '\'');
116           DECODE_ONE ('\"', '\"');
117           DECODE_ONE ('\?', '\?');
118           DECODE_ONE ('\\', '\\');
119           DECODE_ONE ('a', '\a');
120           DECODE_ONE ('b', '\b');
121           DECODE_ONE ('f', '\f');
122           DECODE_ONE ('n', '\n');
123           DECODE_ONE ('r', '\r');
124           DECODE_ONE ('t', '\t');
125           DECODE_ONE ('v', '\v');
126
127         case 'x':
128           {
129             int val = _gpgme_hextobyte (&src[2]);
130
131             if (val == -1)
132               {
133                 /* Should not happen.  */
134                 *(dest++) = *(src++);
135                 *(dest++) = *(src++);
136                 if (*src)
137                   *(dest++) = *(src++);
138                 if (*src)
139                   *(dest++) = *(src++);
140               }
141             else
142               {
143                 if (!val)
144                   {
145                     /* A binary zero is not representable in a C
146                        string.  */
147                     *(dest++) = '\\';
148                     *(dest++) = '0'; 
149                   }
150                 else 
151                   *((unsigned char *) dest++) = val;
152                 src += 4;
153               }
154           }
155           break;
156
157         default:
158           {
159             /* Should not happen.  */
160             *(dest++) = *(src++);
161             *(dest++) = *(src++);
162           }
163         } 
164     }
165   *(dest++) = 0;
166
167   return 0;
168 }
169
170
171 /* Decode the percent escaped string SRC and store the result in the
172    buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
173    large enough buffer is allocated with malloc and *DESTP is set to
174    the result.  Currently, LEN is only used to specify if allocation
175    is desired or not, the caller is expected to make sure that *DESTP
176    is large enough if LEN is not zero.  */
177 gpgme_error_t
178 _gpgme_decode_percent_string (const char *src, char **destp, size_t len)
179 {
180   char *dest;
181
182   /* Set up the destination buffer.  */
183   if (len)
184     {
185       if (len < strlen (src) + 1)
186         return gpg_error (GPG_ERR_INTERNAL);
187
188       dest = *destp;
189     }
190   else
191     {
192       /* The converted string will never be larger than the original
193          string.  */
194       dest = malloc (strlen (src) + 1);
195       if (!dest)
196         return gpg_error_from_errno (errno);
197
198       *destp = dest;
199     }
200
201   /* Convert the string.  */
202   while (*src)
203     {
204       if (*src != '%')
205         {
206           *(dest++) = *(src++);
207           continue;
208         }
209       else
210         {
211           int val = _gpgme_hextobyte (&src[1]);
212           
213           if (val == -1)
214             {
215               /* Should not happen.  */
216               *(dest++) = *(src++);
217               if (*src)
218                 *(dest++) = *(src++);
219               if (*src)
220                 *(dest++) = *(src++);
221             }
222           else
223             {
224               if (!val)
225                 {
226                   /* A binary zero is not representable in a C
227                      string.  */
228                   *(dest++) = '\\';
229                   *(dest++) = '0'; 
230                 }
231               else 
232                 *((unsigned char *) dest++) = val;
233               src += 3;
234             }
235         }
236     }
237   *(dest++) = 0;
238
239   return 0;
240 }
241
242
243 /* Parse the string TIMESTAMP into a time_t.  The string may either be
244    seconds since Epoch or in the ISO 8601 format like
245    "20390815T143012".  Returns 0 for an empty string or seconds since
246    Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
247    point to the next non-parsed character in TIMESTRING. */
248 time_t
249 _gpgme_parse_timestamp (const char *timestamp, char **endp)
250 {
251   /* Need to skip leading spaces, because that is what strtoul does
252      but not our ISO 8601 checking code. */
253   while (*timestamp && *timestamp== ' ')
254     timestamp++;
255   if (!*timestamp)
256     return 0;
257
258   if (strlen (timestamp) >= 15 && timestamp[8] == 'T')
259     {
260       struct tm buf;
261       int year;
262
263       year = atoi_4 (timestamp);
264       if (year < 1900)
265         return (time_t)(-1);
266
267       /* Fixme: We would better use a configure test to see whether
268          mktime can handle dates beyond 2038. */
269       if (sizeof (time_t) <= 4 && year >= 2038)
270         return (time_t)2145914603; /* 2037-12-31 23:23:23 */
271
272       memset (&buf, 0, sizeof buf);
273       buf.tm_year = year - 1900;
274       buf.tm_mon = atoi_2 (timestamp+4) - 1; 
275       buf.tm_mday = atoi_2 (timestamp+6);
276       buf.tm_hour = atoi_2 (timestamp+9);
277       buf.tm_min = atoi_2 (timestamp+11);
278       buf.tm_sec = atoi_2 (timestamp+13);
279
280       if (endp)
281         *endp = (char*)(timestamp + 15);
282 #ifdef HAVE_TIMEGM
283       return timegm (&buf);
284 #else
285       {
286         time_t tim;
287         
288         putenv ("TZ=UTC");
289         tim = mktime (&buf);
290 #ifdef __GNUC__
291 #warning fixme: we must somehow reset TZ here.  It is not threadsafe anyway.
292 #endif
293         return tim;
294       }
295 #endif /* !HAVE_TIMEGM */
296     }
297   else
298     return (time_t)strtoul (timestamp, endp, 10);
299 }
300
301
302
303 \f
304 static struct
305 {
306   char *name;
307   gpgme_error_t err;
308 } gnupg_errors[] =
309   {
310     { "EOF", GPG_ERR_EOF },
311     { "No_Error", GPG_ERR_NO_ERROR },
312     { "General_Error", GPG_ERR_GENERAL },
313     { "Out_Of_Core", GPG_ERR_ENOMEM },
314     { "Invalid_Value", GPG_ERR_INV_VALUE },
315     { "IO_Error", GPG_ERR_GENERAL },
316     { "Resource_Limit", GPG_ERR_RESOURCE_LIMIT },
317     { "Internal_Error", GPG_ERR_INTERNAL },
318     { "Bad_Certificate", GPG_ERR_BAD_CERT },
319     { "Bad_Certificate_Chain", GPG_ERR_BAD_CERT_CHAIN},
320     { "Missing_Certificate", GPG_ERR_MISSING_CERT },
321     { "No_Data", GPG_ERR_NO_DATA },
322     { "Bad_Signature", GPG_ERR_BAD_SIGNATURE },
323     { "Not_Implemented", GPG_ERR_NOT_IMPLEMENTED },
324     { "Conflict", GPG_ERR_CONFLICT },
325     { "Bug", GPG_ERR_BUG },
326     { "Read_Error", GPG_ERR_GENERAL },
327     { "Write_Error", GPG_ERR_GENERAL },
328     { "Invalid_Line", GPG_ERR_GENERAL },
329     { "Incomplete_Line", GPG_ERR_INCOMPLETE_LINE },
330     { "Invalid_Response", GPG_ERR_INV_RESPONSE },
331     { "Agent_Error", GPG_ERR_AGENT },
332     { "No_Public_Key", GPG_ERR_NO_PUBKEY },
333     { "No_Secret_Key", GPG_ERR_NO_SECKEY },
334     { "File_Open_Error", GPG_ERR_GENERAL },
335     { "File_Create_Error", GPG_ERR_GENERAL },
336     { "File_Error", GPG_ERR_GENERAL },
337     { "Not_Supported", GPG_ERR_NOT_SUPPORTED },
338     { "Invalid_Data", GPG_ERR_INV_DATA },
339     { "Assuan_Server_Fault", GPG_ERR_ASSUAN_SERVER_FAULT },
340     { "Assuan_Error", GPG_ERR_ASSUAN },
341     { "Invalid_Session_Key", GPG_ERR_INV_SESSION_KEY },
342     { "Invalid_Sexp", GPG_ERR_INV_SEXP },
343     { "Unsupported_Algorithm", GPG_ERR_UNSUPPORTED_ALGORITHM },
344     { "No_PIN_Entry", GPG_ERR_NO_PIN_ENTRY },
345     { "PIN_Entry_Error", GPG_ERR_NO_PIN_ENTRY },
346     { "Bad_PIN", GPG_ERR_BAD_PIN },
347     { "Bad_Passphrase", GPG_ERR_BAD_PASSPHRASE },
348     { "Invalid_Name", GPG_ERR_INV_NAME },
349     { "Bad_Public_Key", GPG_ERR_BAD_PUBKEY },
350     { "Bad_Secret_Key", GPG_ERR_BAD_SECKEY },
351     { "Bad_Data", GPG_ERR_BAD_DATA },
352     { "Invalid_Parameter", GPG_ERR_INV_PARAMETER },
353     { "Tribute_to_D_A", GPG_ERR_TRIBUTE_TO_D_A },
354     { "No_Dirmngr", GPG_ERR_NO_DIRMNGR },
355     { "Dirmngr_Error", GPG_ERR_DIRMNGR },
356     { "Certificate_Revoked", GPG_ERR_CERT_REVOKED },
357     { "No_CRL_Known", GPG_ERR_NO_CRL_KNOWN },
358     { "CRL_Too_Old", GPG_ERR_CRL_TOO_OLD },
359     { "Line_Too_Long", GPG_ERR_LINE_TOO_LONG },
360     { "Not_Trusted", GPG_ERR_NOT_TRUSTED },
361     { "Canceled", GPG_ERR_CANCELED },
362     { "Bad_CA_Certificate", GPG_ERR_BAD_CA_CERT },
363     { "Certificate_Expired", GPG_ERR_CERT_EXPIRED },
364     { "Certificate_Too_Young", GPG_ERR_CERT_TOO_YOUNG },
365     { "Unsupported_Certificate", GPG_ERR_UNSUPPORTED_CERT },
366     { "Unknown_Sexp", GPG_ERR_UNKNOWN_SEXP },
367     { "Unsupported_Protection", GPG_ERR_UNSUPPORTED_PROTECTION },
368     { "Corrupted_Protection", GPG_ERR_CORRUPTED_PROTECTION },
369     { "Ambiguous_Name", GPG_ERR_AMBIGUOUS_NAME },
370     { "Card_Error", GPG_ERR_CARD },
371     { "Card_Reset", GPG_ERR_CARD_RESET },
372     { "Card_Removed", GPG_ERR_CARD_REMOVED },
373     { "Invalid_Card", GPG_ERR_INV_CARD },
374     { "Card_Not_Present", GPG_ERR_CARD_NOT_PRESENT },
375     { "No_PKCS15_App", GPG_ERR_NO_PKCS15_APP },
376     { "Not_Confirmed", GPG_ERR_NOT_CONFIRMED },
377     { "Configuration_Error", GPG_ERR_CONFIGURATION },
378     { "No_Policy_Match", GPG_ERR_NO_POLICY_MATCH },
379     { "Invalid_Index", GPG_ERR_INV_INDEX },
380     { "Invalid_Id", GPG_ERR_INV_ID },
381     { "No_Scdaemon", GPG_ERR_NO_SCDAEMON },
382     { "Scdaemon_Error", GPG_ERR_SCDAEMON },
383     { "Unsupported_Protocol", GPG_ERR_UNSUPPORTED_PROTOCOL },
384     { "Bad_PIN_Method", GPG_ERR_BAD_PIN_METHOD },
385     { "Card_Not_Initialized", GPG_ERR_CARD_NOT_INITIALIZED },
386     { "Unsupported_Operation", GPG_ERR_UNSUPPORTED_OPERATION },
387     { "Wrong_Key_Usage", GPG_ERR_WRONG_KEY_USAGE }
388   };
389     
390
391 gpgme_error_t
392 _gpgme_map_gnupg_error (char *err)
393 {
394   unsigned int i;
395
396   /* Future version of GnuPG might return the error code directly, so
397      we first test for a a numerical value and use that verbatim.
398      Note that this numerical value might be followed by an
399      underschore and the textual representation of the error code. */
400   if (*err >= '0' && *err <= '9')
401     return strtoul (err, NULL, 10);
402
403   /* Well, this is a token, use the mapping table to get the error.
404      The drawback is that we won't receive an error source and have to
405      use GPG as source. */
406   for (i = 0; i < DIM (gnupg_errors); i++)
407     if (!strcmp (gnupg_errors[i].name, err))
408       return gpg_err_make (GPG_ERR_SOURCE_GPG, gnupg_errors[i].err);
409
410   return gpg_err_make (GPG_ERR_SOURCE_GPG, GPG_ERR_GENERAL);
411 }