doc/
[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 \f
165 GpgmeError
166 _gpgme_data_append (GpgmeData dh, const char *buffer, size_t length)
167 {
168   if (!dh || !buffer)
169     return GPGME_Invalid_Value;
170
171   do
172     {
173       ssize_t amt = gpgme_data_write (dh, buffer, length);
174       if (amt == 0 || (amt < 0 && errno != EINTR))
175         return GPGME_File_Error;
176       buffer += amt;
177       length -= amt;
178     }
179   while (length > 0);
180
181   return 0;
182 }
183
184
185 GpgmeError
186 _gpgme_data_append_string (GpgmeData dh, const char *str)
187 {
188   if (!str)
189     return 0;
190
191   return _gpgme_data_append (dh, str, strlen (str));
192 }
193
194
195 GpgmeError
196 _gpgme_data_append_for_xml (GpgmeData dh, const char *buffer, size_t len)
197 {
198   const char *text, *str;
199   size_t count;
200   int err = 0;
201
202   if (!dh || !buffer)
203     return GPGME_Invalid_Value;
204
205   do
206     {
207       text = NULL;
208       str = buffer;
209       for (count = len; count && !text; str++, count--)
210         {
211           if (*str == '<')
212             text = "&lt;";
213           else if (*str == '>')
214             text = "&gt;";  /* Not sure whether this is really needed.  */
215           else if (*str == '&')
216             text = "&amp;";
217           else if (!*str)
218             text = "&#00;";
219         }
220       if (text)
221         {
222           str--;
223           count++;
224         }
225       if (str != buffer)
226         err = _gpgme_data_append (dh, buffer, str - buffer);
227       if (!err && text)
228         {
229           err = _gpgme_data_append_string (dh, text);
230           str++;
231           count--;
232         }
233       buffer = str;
234       len = count;
235     }
236   while (!err && len);
237   return err;
238 }
239
240
241 /* Append a string to DATA and convert it so that the result will be
242    valid XML.  */
243 GpgmeError
244 _gpgme_data_append_string_for_xml (GpgmeData dh, const char *str)
245 {
246   return _gpgme_data_append_for_xml (dh, str, strlen (str));
247 }
248
249
250 /* Append a string with percent style (%XX) escape characters as
251    XML.  */
252 GpgmeError
253 _gpgme_data_append_percentstring_for_xml (GpgmeData dh, const char *str)
254 {
255   const unsigned char *src;
256   unsigned char *buf, *dst;
257   int val;
258   GpgmeError err;
259
260   buf = malloc (strlen (str));
261   dst = buf;
262   for (src = str; *src; src++)
263     {
264       if (*src == '%' && (val = _gpgme_hextobyte (src + 1)) != -1)
265         {
266           *dst++ = val;
267           src += 2;
268         }
269       else
270         *dst++ = *src;
271     }
272
273   err = _gpgme_data_append_for_xml (dh, buf, dst - buf);
274   free (buf);
275   return err;
276 }