1/* $NetBSD: hex_quote.c,v 1.2 2020/03/18 19:05:21 christos Exp $ */ 2 3/*++ 4/* NAME 5/* hex_quote 3 6/* SUMMARY 7/* quote/unquote text, HTTP style. 8/* SYNOPSIS 9/* #include <hex_quote.h> 10/* 11/* VSTRING *hex_quote(hex, raw) 12/* VSTRING *hex; 13/* const char *raw; 14/* 15/* VSTRING *hex_unquote(raw, hex) 16/* VSTRING *raw; 17/* const char *hex; 18/* DESCRIPTION 19/* hex_quote() takes a null-terminated string and replaces non-printable 20/* and whitespace characters and the % by %XX, XX being the two-digit 21/* hexadecimal equivalent. 22/* The hexadecimal codes are produced as upper-case characters. The result 23/* value is the hex argument. 24/* 25/* hex_unquote() performs the opposite transformation. This function 26/* understands lowercase, uppercase, and mixed case %XX sequences. The 27/* result value is the raw argument in case of success, a null pointer 28/* otherwise. 29/* BUGS 30/* hex_quote() cannot process null characters in data. 31/* LICENSE 32/* .ad 33/* .fi 34/* The Secure Mailer license must be distributed with this software. 35/* AUTHOR(S) 36/* Wietse Venema 37/* IBM T.J. Watson Research 38/* P.O. Box 704 39/* Yorktown Heights, NY 10598, USA 40/* 41/* Wietse Venema 42/* Google, Inc. 43/* 111 8th Avenue 44/* New York, NY 10011, USA 45/*--*/ 46 47/* System library. */ 48 49#include "sys_defs.h" 50#include <ctype.h> 51 52/* Utility library. */ 53 54#include "msg.h" 55#include "vstring.h" 56#include "hex_quote.h" 57 58/* Application-specific. */ 59 60#define STR(x) vstring_str(x) 61#define LEN(x) VSTRING_LEN(x) 62 63/* hex_quote - raw data to quoted */ 64 65VSTRING *hex_quote(VSTRING *hex, const char *raw) 66{ 67 const char *cp; 68 int ch; 69 70 VSTRING_RESET(hex); 71 for (cp = raw; (ch = *(unsigned const char *) cp) != 0; cp++) { 72 if (ch != '%' && !ISSPACE(ch) && ISPRINT(ch)) { 73 VSTRING_ADDCH(hex, ch); 74 } else { 75 vstring_sprintf_append(hex, "%%%02X", ch); 76 } 77 } 78 VSTRING_TERMINATE(hex); 79 return (hex); 80} 81 82/* hex_unquote - quoted data to raw */ 83 84VSTRING *hex_unquote(VSTRING *raw, const char *hex) 85{ 86 const char *cp; 87 int ch; 88 89 VSTRING_RESET(raw); 90 for (cp = hex; (ch = *cp) != 0; cp++) { 91 if (ch == '%') { 92 if (ISDIGIT(cp[1])) 93 ch = (cp[1] - '0') << 4; 94 else if (cp[1] >= 'a' && cp[1] <= 'f') 95 ch = (cp[1] - 'a' + 10) << 4; 96 else if (cp[1] >= 'A' && cp[1] <= 'F') 97 ch = (cp[1] - 'A' + 10) << 4; 98 else 99 return (0); 100 if (ISDIGIT(cp[2])) 101 ch |= (cp[2] - '0'); 102 else if (cp[2] >= 'a' && cp[2] <= 'f') 103 ch |= (cp[2] - 'a' + 10); 104 else if (cp[2] >= 'A' && cp[2] <= 'F') 105 ch |= (cp[2] - 'A' + 10); 106 else 107 return (0); 108 cp += 2; 109 } 110 VSTRING_ADDCH(raw, ch); 111 } 112 VSTRING_TERMINATE(raw); 113 return (raw); 114} 115 116#ifdef TEST 117 118 /* 119 * Proof-of-concept test program: convert to hex and back. 120 */ 121#include <vstream.h> 122 123#define BUFLEN 1024 124 125static ssize_t read_buf(VSTREAM *fp, VSTRING *buf) 126{ 127 ssize_t len; 128 129 len = vstream_fread_buf(fp, buf, BUFLEN); 130 VSTRING_TERMINATE(buf); 131 return (len); 132} 133 134int main(int unused_argc, char **unused_argv) 135{ 136 VSTRING *raw = vstring_alloc(BUFLEN); 137 VSTRING *hex = vstring_alloc(100); 138 ssize_t len; 139 140 while ((len = read_buf(VSTREAM_IN, raw)) > 0) { 141 hex_quote(hex, STR(raw)); 142 if (hex_unquote(raw, STR(hex)) == 0) 143 msg_fatal("bad input: %.100s", STR(hex)); 144 if (LEN(raw) != len) 145 msg_fatal("len %ld != raw len %ld", (long) len, (long) LEN(raw)); 146 if (vstream_fwrite(VSTREAM_OUT, STR(raw), LEN(raw)) != LEN(raw)) 147 msg_fatal("write error: %m"); 148 } 149 vstream_fflush(VSTREAM_OUT); 150 vstring_free(raw); 151 vstring_free(hex); 152 return (0); 153} 154 155#endif 156