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