172d7b765f52ee7a2cea0c757b76cab12aa14809
[gnupg.git] / common / percent.c
1 /* percent.c - Percent escaping
2  * Copyright (C) 2008, 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <errno.h>
23 #include <ctype.h>
24 #include <assert.h>
25
26 #include "util.h"
27
28
29 /* Create a newly alloced string from STRING with all spaces and
30    control characters converted to plus signs or %xx sequences.  The
31    function returns the new string or NULL in case of a malloc
32    failure.
33
34    Note that we also escape the quote character to work around a bug
35    in the mingw32 runtime which does not correcty handle command line
36    quoting.  We correctly double the quote mark when calling a program
37    (i.e. gpg-protect-tool), but the pre-main code does not notice the
38    double quote as an escaped quote.  We do this also on POSIX systems
39    for consistency.  */
40 char *
41 percent_plus_escape (const char *string)
42 {
43   char *buffer, *p;
44   const char *s;
45   size_t length;
46
47   for (length=1, s=string; *s; s++)
48     {
49       if (*s == '+' || *s == '\"' || *s == '%'
50           || *(const unsigned char *)s < 0x20)
51         length += 3;
52       else
53         length++;
54     }
55
56   buffer = p = xtrymalloc (length);
57   if (!buffer)
58     return NULL;
59
60   for (s=string; *s; s++)
61     {
62       if (*s == '+' || *s == '\"' || *s == '%'
63           || *(const unsigned char *)s < 0x20)
64         {
65           snprintf (p, 4, "%%%02X", *(unsigned char *)s);
66           p += 3;
67         }
68       else if (*s == ' ')
69         *p++ = '+';
70       else
71         *p++ = *s;
72     }
73   *p = 0;
74
75   return buffer;
76
77 }
78
79
80 /* Do the percent and plus/space unescaping from STRING to BUFFER and
81    return the length of the valid buffer.  Plus unescaping is only
82    done if WITHPLUS is true.  An escaped Nul character will be
83    replaced by NULREPL.  */
84 static size_t
85 do_unescape (unsigned char *buffer, const unsigned char *string,
86              int withplus, int nulrepl)
87 {
88   unsigned char *p = buffer;
89
90   while (*string)
91     {
92       if (*string == '%' && string[1] && string[2])
93         {
94           string++;
95           *p = xtoi_2 (string);
96           if (!*p)
97             *p = nulrepl;
98           string++;
99         }
100       else if (*string == '+' && withplus)
101         *p = ' ';
102       else
103         *p = *string;
104       p++;
105       string++;
106     }
107
108   return (p - buffer);
109 }
110
111
112 /* Count space required after unescaping STRING.  Note that this will
113    never be larger than strlen (STRING).  */
114 static size_t
115 count_unescape (const unsigned char *string)
116 {
117   size_t n = 0;
118
119   while (*string)
120     {
121       if (*string == '%' && string[1] && string[2])
122         {
123           string++;
124           string++;
125         }
126       string++;
127       n++;
128     }
129
130   return n;
131 }
132
133
134 /* Helper.  */
135 static char *
136 do_plus_or_plain_unescape (const char *string, int withplus, int nulrepl)
137 {
138   size_t nbytes, n;
139   char *newstring;
140
141   nbytes = count_unescape (string);
142   newstring = xtrymalloc (nbytes+1);
143   if (newstring)
144     {
145       n = do_unescape (newstring, string, withplus, nulrepl);
146       assert (n == nbytes);
147       newstring[n] = 0;
148     }
149   return newstring;
150 }
151
152
153 /* Create a new allocated string from STRING with all "%xx" sequences
154    decoded and all plus signs replaced by a space.  Embedded Nul
155    characters are replaced by the value of NULREPL.  The function
156    returns the new string or NULL in case of a malloc failure.  */
157 char *
158 percent_plus_unescape (const char *string, int nulrepl)
159 {
160   return do_plus_or_plain_unescape (string, 1, nulrepl);
161 }
162
163
164 /* Create a new allocated string from STRING with all "%xx" sequences
165    decoded.  Embedded Nul characters are replaced by the value of
166    NULREPL.  The function returns the new string or NULL in case of a
167    malloc failure.  */
168 char *
169 percent_unescape (const char *string, int nulrepl)
170 {
171   return do_plus_or_plain_unescape (string, 0, nulrepl);
172 }
173
174
175 static size_t
176 do_unescape_inplace (char *string, int withplus, int nulrepl)
177 {
178   unsigned char *p, *p0;
179
180   p = p0 = string;
181   while (*string)
182     {
183       if (*string == '%' && string[1] && string[2])
184         {
185           string++;
186           *p = xtoi_2 (string);
187           if (!*p)
188             *p = nulrepl;
189           string++;
190         }
191       else if (*string == '+' && withplus)
192         *p = ' ';
193       else
194         *p = *string;
195       p++;
196       string++;
197     }
198
199   return (p - p0);
200 }
201
202
203 /* Perform percent and plus unescaping in STRING and return the new
204    valid length of the string.  Embedded Nul characters are replaced
205    by the value of NULREPL.  A terminating Nul character is not
206    inserted; the caller might want to call this function this way:
207
208       foo[percent_plus_unescape_inplace (foo, 0)] = 0;
209  */
210 size_t
211 percent_plus_unescape_inplace (char *string, int nulrepl)
212 {
213   return do_unescape_inplace (string, 1, nulrepl);
214 }
215
216
217 /* Perform percent unescaping in STRING and return the new valid
218    length of the string.  Embedded Nul characters are replaced by the
219    value of NULREPL.  A terminating Nul character is not inserted; the
220    caller might want to call this function this way:
221
222       foo[percent_unescape_inplace (foo, 0)] = 0;
223  */
224 size_t
225 percent_unescape_inplace (char *string, int nulrepl)
226 {
227   return do_unescape_inplace (string, 0, nulrepl);
228 }