escape.c (6299B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 /* Escape and unescape URL encoding in strings. The functions return a new 26 * allocated string or NULL if an error occurred. */ 27 28 #include "curl_setup.h" 29 30 #include <curl/curl.h> 31 32 struct Curl_easy; 33 34 #include "urldata.h" 35 #include "curlx/warnless.h" 36 #include "escape.h" 37 #include "strdup.h" 38 #include "curlx/strparse.h" 39 40 /* The last 3 #include files should be in this order */ 41 #include "curl_printf.h" 42 #include "curl_memory.h" 43 #include "memdebug.h" 44 45 /* for ABI-compatibility with previous versions */ 46 char *curl_escape(const char *string, int inlength) 47 { 48 return curl_easy_escape(NULL, string, inlength); 49 } 50 51 /* for ABI-compatibility with previous versions */ 52 char *curl_unescape(const char *string, int length) 53 { 54 return curl_easy_unescape(NULL, string, length, NULL); 55 } 56 57 /* Escapes for URL the given unescaped string of given length. 58 * 'data' is ignored since 7.82.0. 59 */ 60 char *curl_easy_escape(CURL *data, const char *string, 61 int inlength) 62 { 63 size_t length; 64 struct dynbuf d; 65 (void)data; 66 67 if(!string || (inlength < 0)) 68 return NULL; 69 70 length = (inlength ? (size_t)inlength : strlen(string)); 71 if(!length) 72 return strdup(""); 73 74 curlx_dyn_init(&d, length * 3 + 1); 75 76 while(length--) { 77 /* treat the characters unsigned */ 78 unsigned char in = (unsigned char)*string++; 79 80 if(ISUNRESERVED(in)) { 81 /* append this */ 82 if(curlx_dyn_addn(&d, &in, 1)) 83 return NULL; 84 } 85 else { 86 /* encode it */ 87 unsigned char out[3]={'%'}; 88 Curl_hexbyte(&out[1], in); 89 if(curlx_dyn_addn(&d, out, 3)) 90 return NULL; 91 } 92 } 93 94 return curlx_dyn_ptr(&d); 95 } 96 97 /* 98 * Curl_urldecode() URL decodes the given string. 99 * 100 * Returns a pointer to a malloced string in *ostring with length given in 101 * *olen. If length == 0, the length is assumed to be strlen(string). 102 * 103 * ctrl options: 104 * - REJECT_NADA: accept everything 105 * - REJECT_CTRL: rejects control characters (byte codes lower than 32) in 106 * the data 107 * - REJECT_ZERO: rejects decoded zero bytes 108 * 109 * The values for the enum starts at 2, to make the assert detect legacy 110 * invokes that used TRUE/FALSE (0 and 1). 111 */ 112 113 CURLcode Curl_urldecode(const char *string, size_t length, 114 char **ostring, size_t *olen, 115 enum urlreject ctrl) 116 { 117 size_t alloc; 118 char *ns; 119 120 DEBUGASSERT(string); 121 DEBUGASSERT(ctrl >= REJECT_NADA); /* crash on TRUE/FALSE */ 122 123 alloc = (length ? length : strlen(string)); 124 ns = malloc(alloc + 1); 125 126 if(!ns) 127 return CURLE_OUT_OF_MEMORY; 128 129 /* store output string */ 130 *ostring = ns; 131 132 while(alloc) { 133 unsigned char in = (unsigned char)*string; 134 if(('%' == in) && (alloc > 2) && 135 ISXDIGIT(string[1]) && ISXDIGIT(string[2])) { 136 /* this is two hexadecimal digits following a '%' */ 137 in = (unsigned char)((Curl_hexval(string[1]) << 4) | 138 Curl_hexval(string[2])); 139 string += 3; 140 alloc -= 3; 141 } 142 else { 143 string++; 144 alloc--; 145 } 146 147 if(((ctrl == REJECT_CTRL) && (in < 0x20)) || 148 ((ctrl == REJECT_ZERO) && (in == 0))) { 149 Curl_safefree(*ostring); 150 return CURLE_URL_MALFORMAT; 151 } 152 153 *ns++ = (char)in; 154 } 155 *ns = 0; /* terminate it */ 156 157 if(olen) 158 /* store output size */ 159 *olen = ns - *ostring; 160 161 return CURLE_OK; 162 } 163 164 /* 165 * Unescapes the given URL escaped string of given length. Returns a 166 * pointer to a malloced string with length given in *olen. 167 * If length == 0, the length is assumed to be strlen(string). 168 * If olen == NULL, no output length is stored. 169 * 'data' is ignored since 7.82.0. 170 */ 171 char *curl_easy_unescape(CURL *data, const char *string, 172 int length, int *olen) 173 { 174 char *str = NULL; 175 (void)data; 176 if(string && (length >= 0)) { 177 size_t inputlen = (size_t)length; 178 size_t outputlen; 179 CURLcode res = Curl_urldecode(string, inputlen, &str, &outputlen, 180 REJECT_NADA); 181 if(res) 182 return NULL; 183 184 if(olen) { 185 if(outputlen <= (size_t) INT_MAX) 186 *olen = curlx_uztosi(outputlen); 187 else 188 /* too large to return in an int, fail! */ 189 Curl_safefree(str); 190 } 191 } 192 return str; 193 } 194 195 /* For operating systems/environments that use different malloc/free 196 systems for the app and for this library, we provide a free that uses 197 the library's memory system */ 198 void curl_free(void *p) 199 { 200 free(p); 201 } 202 203 /* 204 * Curl_hexencode() 205 * 206 * Converts binary input to lowercase hex-encoded ASCII output. 207 * Null-terminated. 208 */ 209 void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ 210 unsigned char *out, size_t olen) /* output buffer size */ 211 { 212 DEBUGASSERT(src && len && (olen >= 3)); 213 if(src && len && (olen >= 3)) { 214 while(len-- && (olen >= 3)) { 215 out[0] = Curl_ldigits[*src >> 4]; 216 out[1] = Curl_ldigits[*src & 0x0F]; 217 ++src; 218 out += 2; 219 olen -= 2; 220 } 221 *out = 0; 222 } 223 else if(olen) 224 *out = 0; 225 } 226 227 /* Curl_hexbyte 228 * 229 * Output a single unsigned char as a two-digit UPPERCASE hex number. 230 */ 231 void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */ 232 unsigned char val) 233 { 234 dest[0] = Curl_udigits[val >> 4]; 235 dest[1] = Curl_udigits[val & 0x0F]; 236 }