Use strlcpy() instead of strcpy().
[privoxy.git] / encode.c
1 const char encode_rcs[] = "$Id: encode.c,v 1.11 2006/12/28 18:25:53 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 SourceForge
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  * Revisions   :
35  *    $Log: encode.c,v $
36  *    Revision 1.11  2006/12/28 18:25:53  fabiankeil
37  *    Fixed gcc43 compiler warning.
38  *
39  *    Revision 1.10  2006/07/18 14:48:45  david__schmidt
40  *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
41  *    with what was really the latest development (the v_3_0_branch branch)
42  *
43  *    Revision 1.8  2002/03/26 22:29:54  swa
44  *    we have a new homepage!
45  *
46  *    Revision 1.7  2002/03/24 13:25:43  swa
47  *    name change related issues
48  *
49  *    Revision 1.6  2002/03/13 00:27:04  jongfoster
50  *    Killing warnings
51  *
52  *    Revision 1.5  2002/03/07 03:46:53  oes
53  *    Fixed compiler warnings etc
54  *
55  *    Revision 1.4  2002/01/22 23:28:07  jongfoster
56  *    Adding convenience function html_encode_and_free_original()
57  *    Making all functions accept NULL paramaters - in this case, they
58  *    simply return NULL.  This allows error-checking to be deferred.
59  *
60  *    Revision 1.3  2001/11/13 00:16:40  jongfoster
61  *    Replacing references to malloc.h with the standard stdlib.h
62  *    (See ANSI or K&R 2nd Ed)
63  *
64  *    Revision 1.2  2001/05/17 22:52:35  oes
65  *     - Cleaned CRLF's from the sources and related files
66  *
67  *    Revision 1.1.1.1  2001/05/15 13:58:51  oes
68  *    Initial import of version 2.9.3 source tree
69  *
70  *
71  *********************************************************************/
72 \f
73
74 #include "config.h"
75
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #include <assert.h>
80
81 #include "miscutil.h"
82 #include "encode.h"
83
84 const char encode_h_rcs[] = ENCODE_H_VERSION;
85
86 /* Maps special characters in a URL to their equivalent % codes. */
87 static const char * const url_code_map[256] = {
88    NULL, "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09",
89    "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", "%10", "%11", "%12", "%13",
90    "%14", "%15", "%16", "%17", "%18", "%19", "%1A", "%1B", "%1C", "%1D",
91    "%1E", "%1F", "+",   "%21", "%22", "%23", "%24", "%25", "%26", "%27",
92    "%28", "%29", NULL,  "%2B", "%2C", NULL,  NULL,  "%2F", NULL,  NULL,
93    NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  NULL,  "%3A", "%3B",
94    "%3C", "%3D", "%3E", "%3F", 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,  "%5B", "%5C", "%5D", "%5E", NULL,  "%60", 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,  "%7B", "%7C", "%7D", "%7E", "%7F", "%80", "%81",
101    "%82", "%83", "%84", "%85", "%86", "%87", "%88", "%89", "%8A", "%8B",
102    "%8C", "%8D", "%8E", "%8F", "%90", "%91", "%92", "%93", "%94", "%95",
103    "%96", "%97", "%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F",
104    "%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", "%A8", "%A9",
105    "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", "%B0", "%B1", "%B2", "%B3",
106    "%B4", "%B5", "%B6", "%B7", "%B8", "%B9", "%BA", "%BB", "%BC", "%BD",
107    "%BE", "%BF", "%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7",
108    "%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", "%D0", "%D1",
109    "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", "%D8", "%D9", "%DA", "%DB",
110    "%DC", "%DD", "%DE", "%DF", "%E0", "%E1", "%E2", "%E3", "%E4", "%E5",
111    "%E6", "%E7", "%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF",
112    "%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", "%F8", "%F9",
113    "%FA", "%FB", "%FC", "%FD", "%FE", "%FF"
114 };
115
116 /* Maps special characters in HTML to their equivalent entites. */
117 static const char * const html_code_map[256] = {
118    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
119    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
120    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
121    NULL, NULL, NULL, NULL,"&quot;",NULL,NULL,NULL,"&amp;",NULL,
122    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
123    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
124    "&lt;",NULL,"&gt;",NULL,NULL, NULL, NULL, NULL, NULL, NULL,
125    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
126    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
127    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
128    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
129    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
130    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
131    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
132    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
133    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
134    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
135    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
136    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
137    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
138    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
139    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
140    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
141    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
142    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
143    NULL, NULL, NULL, NULL, NULL, NULL
144 };
145
146 /* Maps special characters in a cookie to their equivalent % codes. */
147 static const char * const cookie_code_map[256] = {
148    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
149    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
150    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
151    NULL, NULL, "+",  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
152    NULL, NULL, NULL, NULL, "%2C",NULL, NULL, NULL, NULL, NULL,
153    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "%3B",
154    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
155    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
156    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
157    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
158    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
159    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
160    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
161    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
162    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
163    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
164    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
165    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
166    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
167    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
168    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
169    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
170    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
171    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
172    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
173    NULL, NULL, NULL, NULL, NULL, NULL
174 };
175
176
177 /*********************************************************************
178  *
179  * Function    :  html_encode
180  *
181  * Description :  Encodes a string so it's not interpreted as
182  *                containing HTML tags or entities.
183  *                Replaces <, >, &, and " with the appropriate HTML
184  *                entities.
185  *
186  * Parameters  :
187  *          1  :  s = String to encode.  Null-terminated.
188  *
189  * Returns     :  Encoded string, newly allocated on the heap. 
190  *                Caller is responsible for freeing it with free().
191  *                If s is NULL, or on out-of memory, returns NULL.
192  *
193  *********************************************************************/
194 char * html_encode(const char *s)
195 {
196    char * buf;
197    size_t buf_size;
198    
199    if (s == NULL)
200    {
201       return NULL;
202    }
203
204    /* each input char can expand to at most 6 chars */
205    buf_size = (strlen(s) * 6) + 1;
206    buf = (char *) malloc(buf_size);
207
208    if (buf)
209    {
210       char c;
211       char * p = buf;
212       while ( (c = *s++) != '\0')
213       {
214          const char * replace_with = html_code_map[(unsigned char) c];
215          if(replace_with != NULL)
216          {
217             const size_t bytes_written = (size_t)(p - buf);
218             assert(bytes_written < buf_size);
219             p += strlcpy(p, replace_with, buf_size - bytes_written);
220          }
221          else
222          {
223             *p++ = c;
224          }
225       }
226
227       *p = '\0';
228    }
229
230    assert(strlen(buf) < buf_size);
231    return(buf);
232 }
233
234
235 /*********************************************************************
236  *
237  * Function    :  html_encode_and_free_original
238  *
239  * Description :  Encodes a string so it's not interpreted as
240  *                containing HTML tags or entities.
241  *                Replaces <, >, &, and " with the appropriate HTML
242  *                entities.  Free()s original string.
243  *                If original string is NULL, simply returns NULL.
244  *
245  * Parameters  :
246  *          1  :  s = String to encode.  Null-terminated.
247  *
248  * Returns     :  Encoded string, newly allocated on the heap. 
249  *                Caller is responsible for freeing it with free().
250  *                If s is NULL, or on out-of memory, returns NULL.
251  *
252  *********************************************************************/
253 char * html_encode_and_free_original(char *s)
254 {
255    char * result;
256    
257    if (s == NULL)
258    {
259       return NULL;
260    }
261
262    result = html_encode(s);
263    free(s);
264
265    return result;
266 }
267
268
269 /*********************************************************************
270  *
271  * Function    :  cookie_encode
272  *
273  * Description :  Encodes a string so it can be used in a cookie.
274  *                Replaces " ", ",", and ";" with the appropriate
275  *                codes.
276  *
277  * Parameters  :
278  *          1  :  s = String to encode.  Null-terminated.
279  *
280  * Returns     :  Encoded string, newly allocated on the heap. 
281  *                Caller is responsible for freeing it with free().
282  *                If s is NULL, or on out-of memory, returns NULL.
283  *
284  *********************************************************************/
285 char * cookie_encode(const char *s)
286 {
287    char * buf;
288    size_t buf_size;
289
290    if (s == NULL)
291    {
292       return NULL;
293    }
294
295    /* each input char can expand to at most 3 chars */
296    buf_size = (strlen(s) * 3) + 1;
297    buf = (char *) malloc(buf_size);
298
299    if (buf)
300    {
301       char c;
302       char * p = buf;
303       while ( (c = *s++) != '\0')
304       {
305          const char * replace_with = cookie_code_map[(unsigned char) c];
306          if (replace_with != NULL)
307          {
308             const size_t bytes_written = (size_t)(p - buf);
309             assert(bytes_written < buf_size);
310             p += strlcpy(p, replace_with, buf_size - bytes_written);
311          }
312          else
313          {
314             *p++ = c;
315          }
316       }
317
318       *p = '\0';
319    }
320
321    assert(strlen(buf) < buf_size);
322    return(buf);
323 }
324
325 /*********************************************************************
326  *
327  * Function    :  url_encode
328  *
329  * Description :  Encodes a string so it can be used in a URL
330  *                query string.  Replaces special characters with
331  *                the appropriate %xx codes.
332  *
333  * Parameters  :
334  *          1  :  s = String to encode.  Null-terminated.
335  *
336  * Returns     :  Encoded string, newly allocated on the heap. 
337  *                Caller is responsible for freeing it with free().
338  *                If s is NULL, or on out-of memory, returns NULL.
339  *
340  *********************************************************************/
341 char * url_encode(const char *s)
342 {
343    char * buf;
344    size_t buf_size;
345
346    if (s == NULL)
347    {
348       return NULL;
349    }
350
351    /* each input char can expand to at most 3 chars */
352    buf_size = (strlen(s) * 3) + 1;
353    buf = (char *) malloc(buf_size);
354
355    if (buf)
356    {
357       char c;
358       char * p = buf;
359       while( (c = *s++) != '\0')
360       {
361          const char * replace_with = url_code_map[(unsigned char) c];
362          if (replace_with != NULL)
363          {
364             const size_t bytes_written = (size_t)(p - buf);
365             assert(bytes_written < buf_size);
366             p += strlcpy(p, replace_with, buf_size - bytes_written);
367          }
368          else
369          {
370             *p++ = c;
371          }
372       }
373
374       *p = '\0';
375
376    }
377
378    assert(strlen(buf) < buf_size);
379    return(buf);
380 }
381
382
383 /*********************************************************************
384  *
385  * Function    :  xdtoi
386  *
387  * Description :  Converts a single hex digit to an integer.
388  *
389  * Parameters  :
390  *          1  :  d = in the range of ['0'..'9', 'A'..'F', 'a'..'f']
391  *
392  * Returns     :  The integer value, or -1 for non-hex characters.
393  *
394  *********************************************************************/
395 static int xdtoi(const int d)
396 {
397    if ((d >= '0') && (d <= '9'))
398    {
399       return(d - '0');
400    }
401    else if ((d >= 'a') && (d <= 'f')) 
402    {
403       return(d - 'a' + 10);
404    }
405    else if ((d >= 'A') && (d <= 'F'))
406    {
407       return(d - 'A' + 10);
408    }
409    else
410    {
411       return(-1);
412    }
413 }
414
415
416 /*********************************************************************
417  *
418  * Function    :  xtoi
419  *
420  * Description :  Hex string to integer conversion.
421  *
422  * Parameters  :
423  *          1  :  s = a 2 digit hex string (e.g. "1f").  Only the
424  *                    first two characters will be looked at.
425  *
426  * Returns     :  The integer value, or 0 for non-hex strings.
427  *
428  *********************************************************************/
429 static int xtoi(const char *s)
430 {
431    int d1, d2;
432
433    d1 = xdtoi(*s);
434    if(d1 >= 0)
435    {
436       d2 = xdtoi(*(s+1));
437       if(d2 >= 0)
438       {
439          return (d1 << 4) + d2;
440       }
441    }
442
443    return 0;
444 }
445
446
447 /*********************************************************************
448  *
449  * Function    :  url_decode
450  *
451  * Description :  Decodes a URL query string, replacing %xx codes
452  *                with their decoded form.
453  *
454  * Parameters  :
455  *          1  :  s = String to decode.  Null-terminated.
456  *
457  * Returns     :  Decoded string, newly allocated on the heap. 
458  *                Caller is responsible for freeing it with free().
459  *
460  *********************************************************************/
461 char *url_decode(const char * s)
462 {
463    char *buf = malloc(strlen(s) + 1);
464    char *q = buf;
465
466    if (buf)
467    {
468       while (*s)
469       {
470          switch (*s)
471          {
472             case '+':
473                s++;
474                *q++ = ' ';
475                break;
476
477             case '%':
478                if ((*q = (char)xtoi(s + 1)) != '\0')
479                {
480                   s += 3;
481                   q++;
482                }
483                else
484                {
485                   /* malformed, just use it */
486                   *q++ = *s++;
487                }
488                break;
489
490             default:
491                *q++ = *s++;
492                break;
493          }
494       }
495       *q = '\0';
496    }
497
498    return(buf);
499
500 }
501
502
503 /*
504   Local Variables:
505   tab-width: 3
506   end:
507 */