2010-05-12 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / src / conversion.c
1 /* conversion.c - String conversion helper functions.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2007 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 Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (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    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 /* Solaris 8 needs sys/types.h before time.h.  */
29 #include <sys/types.h>
30 #include <time.h>
31 #include <errno.h>
32
33 #include "gpgme.h"
34 #include "util.h"
35 #include "debug.h"
36
37 #define atoi_1(p)   (*(p) - '0' )
38 #define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
39 #define atoi_4(p)   ((atoi_2(p) * 100) + atoi_2((p)+2))
40
41
42 \f
43 /* Convert two hexadecimal digits from STR to the value they
44    represent.  Returns -1 if one of the characters is not a
45    hexadecimal digit.  */
46 int
47 _gpgme_hextobyte (const char *str)
48 {
49   int val = 0;
50   int i;
51
52 #define NROFHEXDIGITS 2
53   for (i = 0; i < NROFHEXDIGITS; i++)
54     {
55       if (*str >= '0' && *str <= '9')
56         val += *str - '0';
57       else if (*str >= 'A' && *str <= 'F')
58         val += 10 + *str - 'A';
59       else if (*str >= 'a' && *str <= 'f')
60         val += 10 + *str - 'a';
61       else
62         return -1;
63       if (i < NROFHEXDIGITS - 1)
64         val *= 16;
65       str++;
66     }
67   return val;
68 }
69
70
71 /* Decode the C formatted string SRC and store the result in the
72    buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
73    large enough buffer is allocated with malloc and *DESTP is set to
74    the result.  Currently, LEN is only used to specify if allocation
75    is desired or not, the caller is expected to make sure that *DESTP
76    is large enough if LEN is not zero.  */
77 gpgme_error_t
78 _gpgme_decode_c_string (const char *src, char **destp, size_t len)
79 {
80   char *dest;
81
82   /* Set up the destination buffer.  */
83   if (len)
84     {
85       if (len < strlen (src) + 1)
86         return gpg_error (GPG_ERR_INTERNAL);
87
88       dest = *destp;
89     }
90   else
91     {
92       /* The converted string will never be larger than the original
93          string.  */
94       dest = malloc (strlen (src) + 1);
95       if (!dest)
96         return gpg_error_from_syserror ();
97
98       *destp = dest;
99     }
100
101   /* Convert the string.  */
102   while (*src)
103     {
104       if (*src != '\\')
105         {
106           *(dest++) = *(src++);
107           continue;
108         }
109
110       switch (src[1])
111         {
112 #define DECODE_ONE(match,result)        \
113         case match:                     \
114           src += 2;                     \
115           *(dest++) = result;           \
116           break;
117
118           DECODE_ONE ('\'', '\'');
119           DECODE_ONE ('\"', '\"');
120           DECODE_ONE ('\?', '\?');
121           DECODE_ONE ('\\', '\\');
122           DECODE_ONE ('a', '\a');
123           DECODE_ONE ('b', '\b');
124           DECODE_ONE ('f', '\f');
125           DECODE_ONE ('n', '\n');
126           DECODE_ONE ('r', '\r');
127           DECODE_ONE ('t', '\t');
128           DECODE_ONE ('v', '\v');
129
130         case 'x':
131           {
132             int val = _gpgme_hextobyte (&src[2]);
133
134             if (val == -1)
135               {
136                 /* Should not happen.  */
137                 *(dest++) = *(src++);
138                 *(dest++) = *(src++);
139                 if (*src)
140                   *(dest++) = *(src++);
141                 if (*src)
142                   *(dest++) = *(src++);
143               }
144             else
145               {
146                 if (!val)
147                   {
148                     /* A binary zero is not representable in a C
149                        string.  */
150                     *(dest++) = '\\';
151                     *(dest++) = '0'; 
152                   }
153                 else 
154                   *((unsigned char *) dest++) = val;
155                 src += 4;
156               }
157           }
158           break;
159
160         default:
161           {
162             /* Should not happen.  */
163             *(dest++) = *(src++);
164             *(dest++) = *(src++);
165           }
166         } 
167     }
168   *(dest++) = 0;
169
170   return 0;
171 }
172
173
174 /* Decode the percent escaped string SRC and store the result in the
175    buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
176    large enough buffer is allocated with malloc and *DESTP is set to
177    the result.  Currently, LEN is only used to specify if allocation
178    is desired or not, the caller is expected to make sure that *DESTP
179    is large enough if LEN is not zero.  If BINARY is 1, then '\0'
180    characters are allowed in the output.  */
181 gpgme_error_t
182 _gpgme_decode_percent_string (const char *src, char **destp, size_t len,
183                               int binary)
184 {
185   char *dest;
186
187   /* Set up the destination buffer.  */
188   if (len)
189     {
190       if (len < strlen (src) + 1)
191         return gpg_error (GPG_ERR_INTERNAL);
192
193       dest = *destp;
194     }
195   else
196     {
197       /* The converted string will never be larger than the original
198          string.  */
199       dest = malloc (strlen (src) + 1);
200       if (!dest)
201         return gpg_error_from_syserror ();
202
203       *destp = dest;
204     }
205
206   /* Convert the string.  */
207   while (*src)
208     {
209       if (*src != '%')
210         {
211           *(dest++) = *(src++);
212           continue;
213         }
214       else
215         {
216           int val = _gpgme_hextobyte (&src[1]);
217           
218           if (val == -1)
219             {
220               /* Should not happen.  */
221               *(dest++) = *(src++);
222               if (*src)
223                 *(dest++) = *(src++);
224               if (*src)
225                 *(dest++) = *(src++);
226             }
227           else
228             {
229               if (!val && !binary)
230                 {
231                   /* A binary zero is not representable in a C
232                      string.  */
233                   *(dest++) = '\\';
234                   *(dest++) = '0'; 
235                 }
236               else 
237                 *((unsigned char *) dest++) = val;
238               src += 3;
239             }
240         }
241     }
242   *(dest++) = 0;
243
244   return 0;
245 }
246
247
248 /* Encode the string SRC with percent escaping and store the result in
249    the buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
250    large enough buffer is allocated with malloc and *DESTP is set to
251    the result.  Currently, LEN is only used to specify if allocation
252    is desired or not, the caller is expected to make sure that *DESTP
253    is large enough if LEN is not zero.  If BINARY is 1, then '\0'
254    characters are allowed in the output.  */
255 gpgme_error_t
256 _gpgme_encode_percent_string (const char *src, char **destp, size_t len)
257 {
258   size_t destlen;
259   char *dest;
260   const char *str;
261
262   destlen = 0;
263   str = src;
264   /* We percent-escape the + character because the user might need a
265      "percent plus" escaped string (special gpg format).  But we
266      percent-escape the space character, which works with and without
267      the special plus format.  */
268   while (*str)
269     {
270       if (*str == '+' || *str == '\"' || *str == '%' 
271           || *(const unsigned char *)str <= 0x20)
272         destlen += 3;
273       else
274         destlen++;
275       str++;
276     }
277   /* Terminating nul byte.  */
278   destlen++;
279
280   /* Set up the destination buffer.  */
281   if (len)
282     {
283       if (len < destlen);
284         return gpg_error (GPG_ERR_INTERNAL);
285
286       dest = *destp;
287     }
288   else
289     {
290       /* The converted string will never be larger than the original
291          string.  */
292       dest = malloc (destlen);
293       if (!dest)
294         return gpg_error_from_syserror ();
295
296       *destp = dest;
297     }
298
299   /* Convert the string.  */
300   while (*src)
301     {
302       if (*src == '+' || *src == '\"' || *src == '%' 
303           || *(const unsigned char *)src <= 0x20)
304         {
305           snprintf (dest, 4, "%%%02X", *(unsigned char *)src);
306           dest += 3;
307         }
308       else
309         *(dest++) = *src;
310       src++;
311     }
312   *(dest++) = 0;
313
314   return 0;
315 }
316
317
318 #ifdef HAVE_W32_SYSTEM
319 static time_t
320 _gpgme_timegm (struct tm *tm)
321 {
322   /* This one is thread safe.  */
323   SYSTEMTIME st;
324   FILETIME ft;
325   unsigned long long cnsecs;
326   
327   st.wYear   = tm->tm_year + 1900;
328   st.wMonth  = tm->tm_mon  + 1;
329   st.wDay    = tm->tm_mday;
330   st.wHour   = tm->tm_hour;
331   st.wMinute = tm->tm_min;
332   st.wSecond = tm->tm_sec;
333   st.wMilliseconds = 0; /* Not available.  */
334   st.wDayOfWeek = 0;    /* Ignored.  */
335
336   /* System time is UTC thus the conversion is pretty easy.  */
337   if (!SystemTimeToFileTime (&st, &ft))
338     {
339       gpg_err_set_errno (EINVAL);
340       return (time_t)(-1);
341     }
342   
343   cnsecs = (((unsigned long long)ft.dwHighDateTime << 32)
344             | ft.dwLowDateTime);
345   cnsecs -= 116444736000000000ULL; /* The filetime epoch is 1601-01-01.  */
346   return (time_t)(cnsecs / 10000000ULL);
347 }
348 #endif
349
350
351 /* Parse the string TIMESTAMP into a time_t.  The string may either be
352    seconds since Epoch or in the ISO 8601 format like
353    "20390815T143012".  Returns 0 for an empty string or seconds since
354    Epoch. Leading spaces are skipped. If ENDP is not NULL, it will
355    point to the next non-parsed character in TIMESTRING. */
356 time_t
357 _gpgme_parse_timestamp (const char *timestamp, char **endp)
358 {
359   /* Need to skip leading spaces, because that is what strtoul does
360      but not our ISO 8601 checking code. */
361   while (*timestamp && *timestamp== ' ')
362     timestamp++;
363   if (!*timestamp)
364     return 0;
365
366   if (strlen (timestamp) >= 15 && timestamp[8] == 'T')
367     {
368       struct tm buf;
369       int year;
370
371       year = atoi_4 (timestamp);
372       if (year < 1900)
373         return (time_t)(-1);
374
375       /* Fixme: We would better use a configure test to see whether
376          mktime can handle dates beyond 2038. */
377       if (sizeof (time_t) <= 4 && year >= 2038)
378         return (time_t)2145914603; /* 2037-12-31 23:23:23 */
379
380       memset (&buf, 0, sizeof buf);
381       buf.tm_year = year - 1900;
382       buf.tm_mon = atoi_2 (timestamp+4) - 1; 
383       buf.tm_mday = atoi_2 (timestamp+6);
384       buf.tm_hour = atoi_2 (timestamp+9);
385       buf.tm_min = atoi_2 (timestamp+11);
386       buf.tm_sec = atoi_2 (timestamp+13);
387
388       if (endp)
389         *endp = (char*)(timestamp + 15);
390 #ifdef HAVE_W32_SYSTEM
391       return _gpgme_timegm (&buf);
392 #else
393 #ifdef HAVE_TIMEGM
394       return timegm (&buf);
395 #else
396       {
397         time_t tim;
398         
399         putenv ("TZ=UTC");
400         tim = mktime (&buf);
401 #ifdef __GNUC__
402 #warning fixme: we must somehow reset TZ here.  It is not threadsafe anyway.
403 #endif
404         return tim;
405       }
406 #endif /* !HAVE_TIMEGM */
407 #endif /* !HAVE_W32_SYSTEM */
408     }
409   else
410     return (time_t)strtoul (timestamp, endp, 10);
411 }