qLibc
qencode.c
Go to the documentation of this file.
1/******************************************************************************
2 * qLibc
3 *
4 * Copyright (c) 2010-2026 Seungyoung Kim.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *****************************************************************************/
28
29/**
30 * @file qencode.c Encoding/decoding APIs.
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <stdbool.h>
36#include <string.h>
37#include "qinternal.h"
38#include "utilities/qstring.h"
39#include "utilities/qencode.h"
40
41/**
42 * Parse a URL-encoded query string.
43 *
44 * @param tbl qlisttbl_t pointer. If NULL, a new table is created.
45 * @param query URL-encoded string
46 * @param equalchar separator between key and value
47 * @param sepchar separator between entries
48 * @param count number of parsed entries is stored here if not NULL
49 *
50 * @return qlisttbl_t pointer on success, or NULL on failure.
51 *
52 * @code
53 * cont char query = "category=love&str=%C5%A5%B5%F0%C4%DA%B4%F5&sort=asc";
54 * qlisttbl_t *tbl = qparse_queries(NULL, req->pszQueryString, '=', '&', NULL);
55 * printf("category = %s\n", tbl->get_str(tbl, "category", false));
56 * printf("str = %s\n", tbl->get_str(tbl, "str", false));
57 * printf("sort = %s\n", tbl->get_str(tbl, "sort", false));
58 * tbl->free(tbl);
59 * @endcode
60 */
61qlisttbl_t *qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar,
62 char sepchar, int *count) {
63 if (tbl == NULL && (tbl = qlisttbl(0)) == NULL) {
64 return NULL;
65 }
66
67 if (query == NULL) {
68 return tbl;
69 }
70
71 int cnt = 0;
72 char *newquery = strdup(query);
73 while (newquery && *newquery) {
74 char *value = _q_makeword(newquery, sepchar);
75 char *name = qstrtrim(_q_makeword(value, equalchar));
76 qurl_decode(name);
77 qurl_decode(value);
78
79 if (tbl->putstr(tbl, name, value) == true) {
80 cnt++;
81 }
82
83 free(name);
84 free(value);
85 }
86
87 if (count != NULL) {
88 *count = cnt;
89 }
90 free(newquery);
91
92 return tbl;
93}
94
95/**
96 * Encode data using URL encoding (percent encoding).
97 *
98 * @param bin input data
99 * @param size length of the input data
100 *
101 * @return allocated URL-encoded string on success, or NULL on failure.
102 *
103 * @code
104 * const char *text = "hello 'qLibc' world";
105 *
106 * char *encstr = qurl_encode(text, strlen(text));
107 * if(encstr == NULL) return -1;
108 *
109 * printf("Original: %s\n", text);
110 * printf("Encoded : %s\n", encstr);
111 *
112 * size_t decsize = qurl_decode(encstr);
113 *
114 * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
115 * free(encstr);
116 *
117 * --[output]--
118 * Original: hello 'qLibc' world
119 * Encoded: hello%20%27qLibc%27%20world
120 * Decoded: hello 'qLibc' world (19 bytes)
121 * @endcode
122 */
123char *qurl_encode(const void *bin, size_t size) {
124 const char URLCHARTBL[16*16] = {
125 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 00-0F
126 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 10-1F
127 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,'-','.','/', // 20-2F
128 '0','1','2','3','4','5','6','7','8','9',':', 0 , 0 , 0 , 0 , 0 , // 30-3F
129 '@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O', // 40-4F
130 'P','Q','R','S','T','U','V','W','X','Y','Z', 0 ,'\\',0 , 0 ,'_', // 50-5F
131 00 ,'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o', // 60-6f
132 'p','q','r','s','t','u','v','w','x','y','z', 0 , 0 , 0 , 0 , 0 , // 70-7F
133 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 80-8F
134 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // 90-9F
135 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // A0-AF
136 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // B0-BF
137 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // C0-CF
138 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // D0-DF
139 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , // E0-EF
140 00 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 // F0-FF
141 }; // 0 means must be encoded.
142
143 if (bin == NULL)
144 return NULL;
145 if (size == 0)
146 return strdup("");
147
148 // malloc buffer
149 char *pszEncStr = (char *) malloc((size * 3) + 1);
150 if (pszEncStr == NULL)
151 return NULL;
152
153 char *pszEncPt = pszEncStr;
154 char *pBinPt = (char *) bin;
155 const char *pBinEnd = (bin + size - 1);
156 for (; pBinPt <= pBinEnd; pBinPt++) {
157 unsigned char c = *pBinPt;
158 if (URLCHARTBL[c] != 0) {
159 *pszEncPt++ = *pBinPt;
160 } else {
161 unsigned char cUpper4 = (c >> 4);
162 unsigned char cLower4 = (c & 0x0F);
163
164 *pszEncPt++ = '%';
165 *pszEncPt++ =
166 (cUpper4 < 0x0A) ?
167 (cUpper4 + '0') : ((cUpper4 - 0x0A) + 'a');
168 *pszEncPt++ =
169 (cLower4 < 0x0A) ?
170 (cLower4 + '0') : ((cLower4 - 0x0A) + 'a');
171 }
172 }
173 *pszEncPt = '\0';
174
175 return pszEncStr;
176}
177
178/**
179 * Decode a URL-encoded string.
180 *
181 * @param str URL-encoded string
182 *
183 * @return number of bytes stored in `str`.
184 *
185 * @note
186 * This function modifies `str` in place. The result is always null-terminated.
187 */
188size_t qurl_decode(char *str) {
189 if (str == NULL) {
190 return 0;
191 }
192
193 char *pEncPt, *pBinPt = str;
194 for (pEncPt = str; *pEncPt != '\0'; pEncPt++) {
195 switch (*pEncPt) {
196 case '+': {
197 *pBinPt++ = ' ';
198 break;
199 }
200 case '%': {
201 *pBinPt++ = _q_x2c(*(pEncPt + 1), *(pEncPt + 2));
202 pEncPt += 2;
203 break;
204 }
205 default: {
206 *pBinPt++ = *pEncPt;
207 break;
208 }
209 }
210 }
211 *pBinPt = '\0';
212
213 return (pBinPt - str);
214}
215
216/**
217 * Encode data using BASE64.
218 *
219 * @param bin input data
220 * @param size length of the input data
221 *
222 * @return allocated BASE64 string on success, or NULL on failure.
223 *
224 * @code
225 * const char *text = "hello world";
226 *
227 * char *encstr = qbase64_encode(text, strlen(text));
228 * if(encstr == NULL) return -1;
229 *
230 * printf("Original: %s\n", text);
231 * printf("Encoded : %s\n", encstr);
232 *
233 * size_t decsize = qbase64_decode(encstr);
234 *
235 * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
236 * free(encstr);
237 *
238 * --[output]--
239 * Original: hello world
240 * Encoded: aGVsbG8gd29ybGQ=
241 * Decoded: hello world (11 bytes)
242 * @endcode
243 */
244char *qbase64_encode(const void *bin, size_t size) {
245 const char B64CHARTBL[64] = {
246 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', // 00-0F
247 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', // 10-1F
248 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v', // 20-2F
249 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/' // 30-3F
250 };
251
252 if (size == 0) {
253 return strdup("");
254 }
255
256 // malloc for encoded string
257 char *pszB64 = (char *) malloc(
258 4 * ((size / 3) + ((size % 3 == 0) ? 0 : 1)) + 1);
259 if (pszB64 == NULL) {
260 return NULL;
261 }
262
263 char *pszB64Pt = pszB64;
264 unsigned char *pBinPt, *pBinEnd = (unsigned char *) (bin + size - 1);
265 unsigned char szIn[3] = { 0, 0, 0 };
266 int nOffset;
267 for (pBinPt = (unsigned char *) bin, nOffset = 0; pBinPt <= pBinEnd;
268 pBinPt++, nOffset++) {
269 int nIdxOfThree = nOffset % 3;
270 szIn[nIdxOfThree] = *pBinPt;
271 if (nIdxOfThree < 2 && pBinPt < pBinEnd)
272 continue;
273
274 *pszB64Pt++ = B64CHARTBL[((szIn[0] & 0xFC) >> 2)];
275 *pszB64Pt++ = B64CHARTBL[(((szIn[0] & 0x03) << 4)
276 | ((szIn[1] & 0xF0) >> 4))];
277 *pszB64Pt++ =
278 (nIdxOfThree >= 1) ?
279 B64CHARTBL[(((szIn[1] & 0x0F) << 2)
280 | ((szIn[2] & 0xC0) >> 6))] :
281 '=';
282 *pszB64Pt++ = (nIdxOfThree >= 2) ? B64CHARTBL[(szIn[2] & 0x3F)] : '=';
283
284 memset((void *) szIn, 0, sizeof(szIn));
285 }
286 *pszB64Pt = '\0';
287
288 return pszB64;
289}
290
291/**
292 * Decode a BASE64 string.
293 *
294 * @param str BASE64-encoded string
295 *
296 * @return number of bytes stored in `str`.
297 *
298 * @note
299 * This function modifies `str` in place. The result is always null-terminated.
300 */
301size_t qbase64_decode(char *str) {
302 const char B64MAPTBL[16 * 16] = {
303 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 00-0F
304 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 10-1F
305 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, // 20-2F
306 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, // 30-3F
307 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 40-4F
308 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, // 50-5F
309 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6F
310 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, // 70-7F
311 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 80-8F
312 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // 90-9F
313 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // A0-AF
314 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // B0-BF
315 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // C0-CF
316 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // D0-DF
317 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, // E0-EF
318 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 // F0-FF
319 };
320
321 char *pEncPt, *pBinPt = str;
322 int nIdxOfFour = 0;
323 char cLastByte = 0;
324 for (pEncPt = str; *pEncPt != '\0'; pEncPt++) {
325 char cByte = B64MAPTBL[(unsigned char) (*pEncPt)];
326 if (cByte == 64)
327 continue;
328
329 if (nIdxOfFour == 0) {
330 nIdxOfFour++;
331 } else if (nIdxOfFour == 1) {
332 // 00876543 0021????
333 //*pBinPt++ = ( ((cLastByte << 2) & 0xFC) | ((cByte >> 4) & 0x03) );
334 *pBinPt++ = ((cLastByte << 2) | (cByte >> 4));
335 nIdxOfFour++;
336 } else if (nIdxOfFour == 2) {
337 // 00??8765 004321??
338 //*pBinPt++ = ( ((cLastByte << 4) & 0xF0) | ((cByte >> 2) & 0x0F) );
339 *pBinPt++ = ((cLastByte << 4) | (cByte >> 2));
340 nIdxOfFour++;
341 } else {
342 // 00????87 00654321
343 //*pBinPt++ = ( ((cLastByte << 6) & 0xC0) | (cByte & 0x3F) );
344 *pBinPt++ = ((cLastByte << 6) | cByte);
345 nIdxOfFour = 0;
346 }
347
348 cLastByte = cByte;
349 }
350 *pBinPt = '\0';
351
352 return (pBinPt - str);
353}
354
355/**
356 * Encode data as hexadecimal digits.
357 *
358 * @param bin input data
359 * @param size length of the input data
360 *
361 * @return allocated hexadecimal string on success, or NULL on failure.
362 *
363 * @code
364 * const char *text = "hello world";
365 *
366 * char *encstr = qhex_encode(text, strlen(text));
367 * if(encstr == NULL) return -1;
368 *
369 * printf("Original: %s\n", text);
370 * printf("Encoded : %s\n", encstr);
371 *
372 * size_t decsize = qhex_decode(encstr);
373 *
374 * printf("Decoded : %s (%zu bytes)\n", encstr, decsize);
375 * free(encstr);
376 *
377 * return 0;
378 *
379 * --[output]--
380 * Original: hello world
381 * Encoded : 68656c6c6f20776f726c64
382 * Decoded : hello world (11 bytes)
383 * @endcode
384 */
385char *qhex_encode(const void *bin, size_t size) {
386 const char HEXCHARTBL[16] = {
387 '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
388 };
389
390 char *pHexStr = (char *) malloc(sizeof(char) * ((size * 2) + 1));
391 if (pHexStr == NULL)
392 return NULL;
393
394 unsigned char *pSrc = (unsigned char *) bin;
395 char *pHexPt = pHexStr;
396 int i;
397 for (i = 0; i < size; i++) {
398 *pHexPt++ = HEXCHARTBL[(pSrc[i] >> 4)];
399 *pHexPt++ = HEXCHARTBL[(pSrc[i] & 0x0F)];
400 }
401 *pHexPt = '\0';
402
403 return pHexStr;
404}
405
406/**
407 * Decode hexadecimal data.
408 *
409 * @param str hexadecimal-encoded string
410 *
411 * @return number of bytes stored in `str`.
412 *
413 * @note
414 * This function modifies `str` in place. The result is always null-terminated.
415 */
416size_t qhex_decode(char *str) {
417 const char HEXMAPTBL[16*16] = {
418 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00-0F
419 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10-1F
420 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20-2F
421 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 30-3F
422 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40-4F
423 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 50-5F
424 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60-6f
425 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 70-7F
426 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80-8F
427 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 90-9F
428 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A0-AF
429 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B0-BF
430 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C0-CF
431 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D0-DF
432 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E0-EF
433 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F0-FF
434 };
435
436 char *pEncPt, *pBinPt = str;
437 for (pEncPt = str; *pEncPt != '\0'; pEncPt += 2) {
438 *pBinPt++ = (HEXMAPTBL[(unsigned char) (*pEncPt)] << 4)
439 + HEXMAPTBL[(unsigned char) (*(pEncPt + 1))];
440 }
441 *pBinPt = '\0';
442
443 return (pBinPt - str);
444}
size_t qbase64_decode(char *str)
Decode a BASE64 string.
Definition qencode.c:301
char * qurl_encode(const void *bin, size_t size)
Encode data using URL encoding (percent encoding).
Definition qencode.c:123
char * qbase64_encode(const void *bin, size_t size)
Encode data using BASE64.
Definition qencode.c:244
qlisttbl_t * qparse_queries(qlisttbl_t *tbl, const char *query, char equalchar, char sepchar, int *count)
Parse a URL-encoded query string.
Definition qencode.c:61
size_t qhex_decode(char *str)
Decode hexadecimal data.
Definition qencode.c:416
size_t qurl_decode(char *str)
Decode a URL-encoded string.
Definition qencode.c:188
char * qhex_encode(const void *bin, size_t size)
Encode data as hexadecimal digits.
Definition qencode.c:385
qlisttbl_t * qlisttbl(int options)
Create a new Q_LIST linked-list container.
Definition qlisttbl.c:149
char * qstrtrim(char *str)
Remove whitespace, including CR and LF, from both ends of a string.
Definition qstring.c:55