7b5d3e8deb5f0bf654c01c027c15f64195fabda6
[privoxy.git] / encode.c
1 const char encode_rcs[] = "$Id: encode.c,v 1.22 2011/11/06 11:44:32 fabiankeil Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/encode.c,v $
5  *
6  * Purpose     :  Functions to encode and decode URLs, and also to
7  *                encode cookies and HTML text.
8  *
9  * Copyright   :  Written by and Copyright (C) 2001 the
10  *                Privoxy team. http://www.privoxy.org/
11  *
12  *                Based on the Internet Junkbuster originally written
13  *                by and Copyright (C) 1997 Anonymous Coders and
14  *                Junkbusters Corporation.  http://www.junkbusters.com
15  *
16  *                This program is free software; you can redistribute it
17  *                and/or modify it under the terms of the GNU General
18  *                Public License as published by the Free Software
19  *                Foundation; either version 2 of the License, or (at
20  *                your option) any later version.
21  *
22  *                This program is distributed in the hope that it will
23  *                be useful, but WITHOUT ANY WARRANTY; without even the
24  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
25  *                PARTICULAR PURPOSE.  See the GNU General Public
26  *                License for more details.
27  *
28  *                The GNU General Public License should be included with
29  *                this file.  If not, you can view it at
30  *                http://www.gnu.org/copyleft/gpl.html
31  *                or write to the Free Software Foundation, Inc., 59
32  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
33  *
34  *********************************************************************/
35
36
37 #include "config.h"
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <assert.h>
43
44 #include "miscutil.h"
45 #include "encode.h"
46
47 const char encode_h_rcs[] = ENCODE_H_VERSION;
48
49 /* Maps special characters in a URL to their equivalent % codes. */
50 static const char * const url_code_map[256] = {
51    NULL, "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09",
52    "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", "%10", "%11", "%12", "%13",
53    "%14", "%15", "%16", "%17", "%18", "%19", "%1A", "%1B", "%1C", "%1D",
54    "%1E", "%1F", "%20", "%21", "%22", "%23", "%24", "%25", "%26", "%27",
55    "%28", "%29", NULL,  "%2B", "%2C", NULL,  NULL,  "%2F", NULL,  NULL,
56    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  "%3A", "%3B",
57    "%3C", "%3D", "%3E", "%3F", NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
58    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
59    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
60    NULL,  "%5B", "%5C", "%5D", "%5E", NULL,  "%60", NULL,  NULL,  NULL,
61    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
62    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,
63    NULL,  NULL,  NULL,  "%7B", "%7C", "%7D", "%7E", "%7F", "%80", "%81",
64    "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8A", "%8B",
65    "%8C", "%8D", "%8E", "%8F", "%90", "%91", "%92", "%93", "%94", "%95",
66    "%96", "%97", "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
67    "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", "%A8", "%A9",
68    "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", "%B0", "%B1", "%B2", "%B3",
69    "%B4", "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB", "%BC", "%BD",
70    "%BE", "%BF", "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
71    "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", "%D0", "%D1",
72    "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB",
73    "%DC", "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3", "%E4", "%E5",
74    "%E6", "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
75    "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8", "%F9",
76    "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
77 };
78
79 /* Maps special characters in HTML to their equivalent entities. */
80 static const char * const html_code_map[256] = {
81    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
82    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
83    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
84    NULL, NULL, NULL, NULL,"&quot;",NULL,NULL,NULL,"&amp;","&#39;",
85    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
86    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
87    "&lt;",NULL,"&gt;",NULL,NULL, NULL, NULL, NULL, NULL, NULL,
88    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
89    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
90    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
91    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
92    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
93    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
94    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
95    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
96    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
97    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
98    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
99    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
100    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
101    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
102    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
103    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
104    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
105    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
106    NULL, NULL, NULL, NULL, NULL, NULL
107 };
108
109
110 /*********************************************************************
111  *
112  * Function    :  html_encode
113  *
114  * Description :  Encodes a string so it's not interpreted as
115  *                containing HTML tags or entities.
116  *                Replaces <, >, &, and " with the appropriate HTML
117  *                entities.
118  *
119  * Parameters  :
120  *          1  :  s = String to encode.  Null-terminated.
121  *
122  * Returns     :  Encoded string, newly allocated on the heap.
123  *                Caller is responsible for freeing it with free().
124  *                If s is NULL, or on out-of memory, returns NULL.
125  *
126  *********************************************************************/
127 char * html_encode(const char *s)
128 {
129    char * buf;
130    size_t buf_size;
131
132    if (s == NULL)
133    {
134       return NULL;
135    }
136
137    /* each input char can expand to at most 6 chars */
138    buf_size = (strlen(s) * 6) + 1;
139    buf = (char *) malloc(buf_size);
140
141    if (buf)
142    {
143       char c;
144       char * p = buf;
145       while ( (c = *s++) != '\0')
146       {
147          const char * replace_with = html_code_map[(unsigned char) c];
148          if(replace_with != NULL)
149          {
150             const size_t bytes_written = (size_t)(p - buf);
151             assert(bytes_written < buf_size);
152             p += strlcpy(p, replace_with, buf_size - bytes_written);
153          }
154          else
155          {
156             *p++ = c;
157          }
158       }
159
160       *p = '\0';
161    }
162
163    assert(strlen(buf) < buf_size);
164    return(buf);
165 }
166
167
168 /*********************************************************************
169  *
170  * Function    :  html_encode_and_free_original
171  *
172  * Description :  Encodes a string so it's not interpreted as
173  *                containing HTML tags or entities.
174  *                Replaces <, >, &, and " with the appropriate HTML
175  *                entities.  Free()s original string.
176  *                If original string is NULL, simply returns NULL.
177  *
178  * Parameters  :
179  *          1  :  s = String to encode.  Null-terminated.
180  *
181  * Returns     :  Encoded string, newly allocated on the heap.
182  *                Caller is responsible for freeing it with free().
183  *                If s is NULL, or on out-of memory, returns NULL.
184  *
185  *********************************************************************/
186 char * html_encode_and_free_original(char *s)
187 {
188    char * result;
189
190    if (s == NULL)
191    {
192       return NULL;
193    }
194
195    result = html_encode(s);
196    free(s);
197
198    return result;
199 }
200
201
202 /*********************************************************************
203  *
204  * Function    :  url_encode
205  *
206  * Description :  Encodes a string so it can be used in a URL
207  *                query string.  Replaces special characters with
208  *                the appropriate %xx codes.
209  *
210  *                XXX: url_query_encode() would be a more fitting
211  *                     name.
212  *
213  * Parameters  :
214  *          1  :  s = String to encode.  Null-terminated.
215  *
216  * Returns     :  Encoded string, newly allocated on the heap.
217  *                Caller is responsible for freeing it with free().
218  *                If s is NULL, or on out-of memory, returns NULL.
219  *
220  *********************************************************************/
221 char * url_encode(const char *s)
222 {
223    char * buf;
224    size_t buf_size;
225
226    if (s == NULL)
227    {
228       return NULL;
229    }
230
231    /* each input char can expand to at most 3 chars */
232    buf_size = (strlen(s) * 3) + 1;
233    buf = (char *) malloc(buf_size);
234
235    if (buf)
236    {
237       char c;
238       char * p = buf;
239       while( (c = *s++) != '\0')
240       {
241          const char * replace_with = url_code_map[(unsigned char) c];
242          if (replace_with != NULL)
243          {
244             const size_t bytes_written = (size_t)(p - buf);
245             assert(bytes_written < buf_size);
246             p += strlcpy(p, replace_with, buf_size - bytes_written);
247          }
248          else
249          {
250             *p++ = c;
251          }
252       }
253
254       *p = '\0';
255
256    }
257
258    assert(strlen(buf) < buf_size);
259    return(buf);
260 }
261
262
263 /*********************************************************************
264  *
265  * Function    :  xdtoi
266  *
267  * Description :  Converts a single hex digit to an integer.
268  *
269  * Parameters  :
270  *          1  :  d = in the range of ['0'..'9', 'A'..'F', 'a'..'f']
271  *
272  * Returns     :  The integer value, or -1 for non-hex characters.
273  *
274  *********************************************************************/
275 static int xdtoi(const int d)
276 {
277    if ((d >= '0') && (d <= '9'))
278    {
279       return(d - '0');
280    }
281    else if ((d >= 'a') && (d <= 'f'))
282    {
283       return(d - 'a' + 10);
284    }
285    else if ((d >= 'A') && (d <= 'F'))
286    {
287       return(d - 'A' + 10);
288    }
289    else
290    {
291       return(-1);
292    }
293 }
294
295
296 /*********************************************************************
297  *
298  * Function    :  xtoi
299  *
300  * Description :  Hex string to integer conversion.
301  *
302  * Parameters  :
303  *          1  :  s = a 2 digit hex string (e.g. "1f").  Only the
304  *                    first two characters will be looked at.
305  *
306  * Returns     :  The integer value, or 0 for non-hex strings.
307  *
308  *********************************************************************/
309 int xtoi(const char *s)
310 {
311    int d1;
312
313    d1 = xdtoi(*s);
314    if(d1 >= 0)
315    {
316       int d2 = xdtoi(*(s+1));
317       if(d2 >= 0)
318       {
319          return (d1 << 4) + d2;
320       }
321    }
322
323    return 0;
324 }
325
326
327 /*********************************************************************
328  *
329  * Function    :  url_decode
330  *
331  * Description :  Decodes a URL query string, replacing %xx codes
332  *                with their decoded form.
333  *
334  * Parameters  :
335  *          1  :  s = String to decode.  Null-terminated.
336  *
337  * Returns     :  Decoded string, newly allocated on the heap.
338  *                Caller is responsible for freeing it with free().
339  *
340  *********************************************************************/
341 char *url_decode(const char * s)
342 {
343    char *buf = malloc(strlen(s) + 1);
344    char *q = buf;
345
346    if (buf)
347    {
348       while (*s)
349       {
350          switch (*s)
351          {
352             case '+':
353                s++;
354                *q++ = ' ';
355                break;
356
357             case '%':
358                if ((*q = (char)xtoi(s + 1)) != '\0')
359                {
360                   s += 3;
361                   q++;
362                }
363                else
364                {
365                   /* malformed, just use it */
366                   *q++ = *s++;
367                }
368                break;
369
370             default:
371                *q++ = *s++;
372                break;
373          }
374       }
375       *q = '\0';
376    }
377
378    return(buf);
379
380 }
381
382
383 /*********************************************************************
384  *
385  * Function    :  percent_encode_url
386  *
387  * Description :  Percent-encodes a string so it no longer contains
388  *                any characters that aren't valid in an URL according
389  *                to RFC 3986.
390  *
391  *                XXX: Do not confuse with encode_url()
392  *
393  * Parameters  :
394  *          1  :  s = String to encode.  Null-terminated.
395  *
396  * Returns     :  Encoded string, newly allocated on the heap.
397  *                Caller is responsible for freeing it with free().
398  *                If s is NULL, or on out-of memory, returns NULL.
399  *
400  *********************************************************************/
401 char *percent_encode_url(const char *s)
402 {
403    static const char allowed_characters[128] = {
404       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
405       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
406       '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
407       '\0', '\0', '\0', '!',  '\0', '#',  '$',  '%',  '&',  '\'',
408       '(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',  '0',  '1',
409       '2',  '3',  '4',  '5',  '6',  '7',  '8',  '9',  ':',  ';',
410       '\0', '=',  '\0', '?',  '@',  'A',  'B',  'C',  'D',  'E',
411       'F',  'G',  'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
412       'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',  'X',  'Y',
413       'Z',  '[',  '\0', ']',  '\0', '_',  '\0', 'a',  'b',  'c',
414       'd',  'e',  'f',  'g',  'h',  'i',  'j',  'k',  'l',  'm',
415       'n',  'o',  'p',  'q',  'r',  's',  't',  'u',  'v',  'w',
416       'x',  'y',  'z',  '\0', '\0', '\0', '~',  '\0'
417    };
418    char *buf;
419    size_t buf_size;
420
421    assert(s != NULL);
422
423    /* Each input char can expand to at most 3 chars. */
424    buf_size = (strlen(s) * 3) + 1;
425    buf = (char *)malloc(buf_size);
426
427    if (buf != NULL)
428    {
429       char c;
430       char *p = buf;
431       while((c = *s++) != '\0')
432       {
433          const unsigned int i = (unsigned char)c;
434          if (i >= sizeof(allowed_characters) || '\0' == allowed_characters[i])
435          {
436             const char *replace_with = url_code_map[i];
437             assert(replace_with != NULL);
438             if (replace_with != NULL)
439             {
440                const size_t bytes_written = (size_t)(p - buf);
441                assert(bytes_written < buf_size);
442                p += strlcpy(p, replace_with, buf_size - bytes_written);
443             }
444          }
445          else
446          {
447             *p++ = c;
448          }
449       }
450       *p = '\0';
451    }
452
453    assert(strlen(buf) < buf_size);
454
455    return(buf);
456
457 }
458
459
460 /*
461   Local Variables:
462   tab-width: 3
463   end:
464 */