/** * Copyright (C) 2021 Graham Leggett * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* * Common utility functions for all tools. */ #include "redwax_util.h" #include #include #include #include int redwax_certcmp(redwax_certificate_t *cert1, redwax_certificate_t *cert2) { return 0; } apr_size_t redwax_strrcspn(const char *s, const char *charset) { apr_size_t last = 0, cur; cur = strcspn(s, charset); while (s[cur++]) { last = cur; cur += strcspn(s + cur, charset); } return last; } const char *redwax_stroff(const char *s, apr_size_t off) { while(off-- && *s) { s++; } return s; } const char *redwax_pstrntrim(apr_pool_t *p, const char *s, apr_size_t off) { const char *start, *end; apr_size_t remain = off; while (remain) { if (*s == ' ') { s++; remain--; } else { break; } } start = end = s; while (remain) { if (*s != ' ') { end = s + 1; } s++; remain--; } return apr_pstrndup(p, start, end - start); } /* c2x takes an unsigned, and expects the caller has guaranteed that * 0 <= what < 256... which usually means that you have to cast to * unsigned char first, because (unsigned)(char)(x) first goes through * signed extension to an int before the unsigned cast. * * The reason for this assumption is to assist gcc code generation -- * the unsigned char -> unsigned extension is already done earlier in * both uses of this code, so there's no need to waste time doing it * again. */ static const char c2x_table[] = "0123456789abcdef"; static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix, unsigned char *where) { #if APR_CHARSET_EBCDIC what = convert_e2a[(unsigned char)what]; #endif /*APR_CHARSET_EBCDIC*/ *where++ = prefix; *where++ = c2x_table[what >> 4]; *where++ = c2x_table[what & 0xf]; return where; } static APR_INLINE unsigned char *c2xx(unsigned what, unsigned char prefix, unsigned char *where) { #if APR_CHARSET_EBCDIC what = convert_e2a[(unsigned char)what]; #endif /*APR_CHARSET_EBCDIC*/ *where++ = prefix; *where++ = c2x_table[what >> 12 & 0xf]; *where++ = c2x_table[what >> 8 & 0xf]; *where++ = c2x_table[what >> 4 & 0xf]; *where++ = c2x_table[what & 0xf]; return where; } static int test_echo(char c) { if (c && (!apr_isprint(c) || c == '"' || c == '\\' || apr_iscntrl(c))) { return 1; } return 0; } static int test_path(char c) { if (!apr_isalnum(c) && !strchr("$-_.+!*'(),:@&=~", c)) { return 1; } return 0; } static int test_all(char c) { return 1; } static int test_json(char c) { if (c && (c == '"' || c == '\\' || apr_iscntrl(c))) { return 1; } return 0; } apr_status_t redwax_escape_echo(char *escaped, const char *str, apr_ssize_t slen, int quote, apr_size_t *len) { apr_size_t size = 1; int found = 0; const unsigned char *s = (const unsigned char *) str; unsigned char *d = (unsigned char *) escaped; unsigned c; if (s) { if (d) { while ((c = *s) && slen) { if (test_echo(c)) { *d++ = '\\'; size++; switch (c) { case '\a': *d++ = 'a'; size++; found = 1; break; case '\b': *d++ = 'b'; size++; found = 1; break; case '\f': *d++ = 'f'; size++; found = 1; break; case '\n': *d++ = 'n'; size++; found = 1; break; case '\r': *d++ = 'r'; size++; found = 1; break; case '\t': *d++ = 't'; size++; found = 1; break; case '\v': *d++ = 'v'; size++; found = 1; break; case '\\': *d++ = '\\'; size++; found = 1; break; case '"': if (quote) { *d++ = c; size++; found = 1; } else { d[-1] = c; } break; default: c2x(c, 'x', d); d += 3; size += 3; found = 1; break; } } else { *d++ = c; size++; } ++s; slen--; } *d = '\0'; } else { while ((c = *s) && slen) { if (test_echo(c)) { size++; switch (c) { case '\a': case '\b': case '\f': case '\n': case '\r': case '\t': case '\v': case '\\': size++; found = 1; break; case '"': if (quote) { size++; found = 1; } break; default: size += 3; found = 1; break; } } else { size++; } ++s; slen--; } } } if (len) { *len = size; } if (!found) { return APR_NOTFOUND; } return APR_SUCCESS; } static const char *redwax_pescape_echo(apr_pool_t *p, const char *str, int quote) { apr_size_t len; switch (redwax_escape_echo(NULL, str, REDWAX_ESCAPE_STRING, quote, &len)) { case APR_SUCCESS: { char *cmd = apr_palloc(p, len); redwax_escape_echo(cmd, str, REDWAX_ESCAPE_STRING, quote, NULL); return cmd; } case APR_NOTFOUND: { break; } } return str; } const char *redwax_pescape_echo_quoted(apr_pool_t *p, const char *str, redwax_token_quoted_e quoted, int close) { str = redwax_pescape_echo(p, str, (quoted != REDWAX_TOKEN_NOQUOTE)); if (str) { if (quoted != REDWAX_TOKEN_NOQUOTE) { char *d; const char *s; int i; int count = 1 + close; /* count quotes */ for (i = 0; str[i]; i++) { if (str[i] == '\"') { count++; } } /* escape quotes */ s = str; d = apr_palloc(p, strlen(str) + count + 1); str = d; *d++ = '\"'; for (; *s; ++s) { if (*s == '\"') { *d++ = '\\'; } *d++ = *s; } *d = 0; if (close) { *d++ = '\"'; } } else { int i; int count = 0; /* count spaces */ for (i = 0; str[i]; i++) { switch (str[i]) { case ' ': case ';': count++; break; default: break; } } /* escape spaces */ if (count) { char *d; const char *s; s = str; d = apr_palloc(p, strlen(str) + count + 1); str = d; for (; *s; ++s) { switch (*s) { case ' ': case ';': *d++ = '\\'; break; default: break; } *d++ = *s; } *d = 0; } } } return str; } static apr_status_t redwax_urlescape(char *escaped, const char *str, apr_ssize_t slen, apr_size_t *len, int(*test)(char)) { apr_size_t size = 1; int found = 0; const unsigned char *s = (const unsigned char *) str; unsigned char *d = (unsigned char *) escaped; unsigned c; if (s) { if (d) { while ((c = *s) && slen) { if (test(c)) { d = c2x(c, '%', d); size += 2; found = 1; } else { *d++ = c; } ++s; size++; slen--; } *d = '\0'; } else { while ((c = *s) && slen) { if (test(c)) { size += 2; found = 1; } ++s; size++; slen--; } } } if (len) { *len = size; } if (!found) { return APR_NOTFOUND; } return APR_SUCCESS; } apr_status_t redwax_urlescape_path(char *escaped, const char *str, apr_ssize_t slen, apr_size_t *len) { return redwax_urlescape(escaped, str, slen, len, test_path); } const char* redwax_pescape_path(apr_pool_t *p, const char *str) { apr_size_t len; switch (redwax_urlescape_path(NULL, str, REDWAX_ESCAPE_STRING, &len)) { case APR_SUCCESS: { char *cmd = apr_palloc(p, len); redwax_urlescape_path(cmd, str, REDWAX_ESCAPE_STRING, NULL); return cmd; } case APR_NOTFOUND: { break; } } return str; } apr_status_t redwax_urlescape_all(char *escaped, const char *str, apr_ssize_t slen, apr_size_t *len) { return redwax_urlescape(escaped, str, slen, len, test_all); } const char* redwax_purlescape_all(apr_pool_t *p, const char *str) { apr_size_t len; switch (redwax_urlescape_all(NULL, str, REDWAX_ESCAPE_STRING, &len)) { case APR_SUCCESS: { char *cmd = apr_palloc(p, len); redwax_urlescape_all(cmd, str, REDWAX_ESCAPE_STRING, NULL); return cmd; } case APR_NOTFOUND: { break; } } return str; } apr_status_t redwax_escape_entity(char *escaped, const char *str, apr_ssize_t slen, int toasc, apr_size_t *len) { apr_size_t size = 1; int found = 0; const unsigned char *s = (const unsigned char *) str; unsigned char *d = (unsigned char *) escaped; unsigned c; if (s) { if (d) { while ((c = *s) && slen) { if (strchr("<>&\"", c)) { switch (c) { case '>': { memcpy(d, ">", 4); size += 4; d += 4; break; } case '<': { memcpy(d, "<", 4); size += 4; d += 4; break; } case '&': { memcpy(d, "&", 5); size += 5; d += 5; break; } case '\"': { memcpy(d, """, 6); size += 6; d += 6; break; } case '\'': { memcpy(d, "'", 6); size += 6; d += 6; break; } } found = 1; } else if (toasc && !apr_isascii(c)) { int offset = apr_snprintf((char *) d, 6, "&#%3.3d;", c); size += offset; d += offset; found = 1; } else { *d++ = c; size++; } ++s; slen--; } *d = '\0'; } else { while ((c = *s) && slen) { if (strchr("<>&\"", c)) { switch (c) { case '>': { size += 4; break; } case '<': { size += 4; break; } case '&': { size += 5; break; } case '\"': { size += 6; break; } case '\'': { size += 6; break; } } found = 1; } else if (toasc && !apr_isascii(c)) { char buf[8]; size += apr_snprintf(buf, 6, "&#%3.3d;", c); found = 1; } else { size++; } ++s; slen--; } } } if (len) { *len = size; } if (!found) { return APR_NOTFOUND; } return APR_SUCCESS; } const char* apr_pescape_entity(apr_pool_t *p, const char *str, int toasc) { apr_size_t len; switch (redwax_escape_entity(NULL, str, REDWAX_ESCAPE_STRING, toasc, &len)) { case APR_SUCCESS: { char *cmd = apr_palloc(p, len); redwax_escape_entity(cmd, str, REDWAX_ESCAPE_STRING, toasc, NULL); return cmd; } case APR_NOTFOUND: { break; } } return str; } apr_status_t redwax_escape_json(char *escaped, const char *str, apr_ssize_t slen, apr_size_t *len) { apr_size_t size = 1; int found = 0; const unsigned char *s = (const unsigned char *) str; unsigned char *d = (unsigned char *) escaped; unsigned c; if (s) { if (d) { while ((c = *s) && slen) { if (test_json(c)) { *d++ = '\\'; size++; switch (c) { case '\n': *d++ = 'n'; size++; found = 1; break; case '\r': *d++ = 'r'; size++; found = 1; break; case '\t': *d++ = 't'; size++; found = 1; break; case '\b': *d++ = 'b'; size++; found = 1; break; case '\f': *d++ = 'f'; size++; found = 1; break; case '\\': *d++ = '\\'; size++; found = 1; break; case '"': *d++ = c; size++; found = 1; break; default: c2xx(c, 'u', d); d += 5; size += 5; found = 1; break; } } else { *d++ = c; size++; } ++s; slen--; } *d = '\0'; } else { while ((c = *s) && slen) { if (test_json(c)) { size++; switch (c) { case '\n': case '\r': case '\t': case '\b': case '\f': case '\\': case '"': size++; found = 1; break; default: size += 5; found = 1; break; } } else { size++; } ++s; slen--; } } } if (len) { *len = size; } if (!found) { return APR_NOTFOUND; } return APR_SUCCESS; } const char *redwax_pescape_json(apr_pool_t *p, const char *str) { apr_size_t len; switch (redwax_escape_json(NULL, str, REDWAX_ESCAPE_STRING, &len)) { case APR_SUCCESS: { char *cmd = apr_palloc(p, len); redwax_escape_json(cmd, str, REDWAX_ESCAPE_STRING, NULL); return cmd; } case APR_NOTFOUND: { break; } } return str; } static const char base16[] = "0123456789ABCDEF"; static const char base16lower[] = "0123456789abcdef"; apr_status_t redwax_encode_base16_binary(char *dest, const unsigned char *src, apr_ssize_t slen, int flags, apr_size_t * len) { const unsigned char *in = src; apr_ssize_t size; if (!src) { return APR_NOTFOUND; } if (dest) { register char *bufout = dest; const char *base; if ((flags & REDWAX_ENCODE_LOWER)) { base = base16lower; } else { base = base16; } for (size = 0; size < slen; size++) { if ((flags & REDWAX_ENCODE_COLON) && size) { *(bufout++) = ':'; } *(bufout++) = base[in[size] >> 4]; *(bufout++) = base[in[size] & 0xf]; } if (len) { *len = bufout - dest; } *bufout = 0; return APR_SUCCESS; } if (len) { if ((flags & REDWAX_ENCODE_COLON) && slen) { *len = slen * 3; } else { *len = slen * 2 + 1; } } return APR_SUCCESS; } const char *redwax_pencode_base16_binary(apr_pool_t * p, const unsigned char *src, apr_ssize_t slen, int flags, apr_size_t * len) { apr_size_t size; switch (redwax_encode_base16_binary(NULL, src, slen, flags, &size)) { case APR_SUCCESS:{ char *cmd = apr_palloc(p, size); redwax_encode_base16_binary(cmd, src, slen, flags, len); return cmd; } case APR_NOTFOUND:{ break; } } return NULL; } apr_status_t redwax_strip_whitespace(char *escaped, const char *str, apr_size_t slen, apr_size_t *len) { apr_size_t size = 1; int found = 0; const unsigned char *s = (const unsigned char *) str; unsigned char *d = (unsigned char *) escaped; unsigned c; if (slen == REDWAX_ESCAPE_STRING) { slen = strlen(str); } if (s) { if (d) { while ((c = *s) && slen) { if (apr_isspace(c)) { found = 1; } else { *d++ = c; size++; } ++s; slen--; } *d = '\0'; } else { while ((c = *s) && slen) { if (apr_isspace(c)) { found = 1; } else { size++; } ++s; slen--; } } } if (len) { *len = size; } if (!found) { return APR_NOTFOUND; } return APR_SUCCESS; } const char* apr_pstrip_whitespace(apr_pool_t *p, const char *str, apr_size_t slen) { apr_size_t len; switch (redwax_strip_whitespace(NULL, str, slen, &len)) { case APR_SUCCESS: { char *cmd = apr_palloc(p, len); redwax_strip_whitespace(cmd, str, slen, NULL); return cmd; } case APR_NOTFOUND: { break; } } return str; } #define XML_PREAMBLE "\n" static apr_status_t cleanup_metadata(void *dummy) { if (dummy) { redwax_metadata_t *m = dummy; free(m->object_prefix); } return APR_SUCCESS; } static void redwax_metadata_prefix(redwax_metadata_t *m, int diff) { int i = m->prefix_len; m->prefix_len += diff * 2; if (m->prefix_len < 0) { m->prefix_len = 0; } if (m->object_prefix) { m->object_prefix = realloc(m->object_prefix, m->prefix_len + 2); if (m->object_prefix) { for (; i < m->prefix_len; i++) { m->object_prefix[i] = ' '; } m->object_prefix[m->prefix_len] = '-'; m->object_prefix[m->prefix_len + 1] = ' '; m->array_prefix = m->object_prefix + 2; } } } apr_status_t redwax_metadata_push_root(apr_pool_t *pool, const char *k, apr_status_t (*wv)(void *ctx, const struct iovec *vec, apr_size_t nvec), void *ctx, redwax_format_e format, redwax_metadata_t **mm) { redwax_metadata_t *m = apr_pcalloc(pool, sizeof(redwax_metadata_t)); redwax_metadata_level_t *ml; apr_pool_create(&m->pool, pool); m->levels = apr_array_make(m->pool, 16, sizeof(redwax_metadata_level_t)); m->wv = wv; m->ctx = ctx; m->format = format; m->object_prefix = malloc(0); apr_pool_cleanup_register(pool, m, cleanup_metadata, apr_pool_cleanup_null); *mm = m; m->level = ml = apr_array_push(m->levels); m->level->k = (void *)k; // escape? m->level->klen = k ? strlen(k) : 0; m->level->root = 1; m->level->object = 1; switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { const struct iovec vec[] = { {XML_PREAMBLE, strlen(XML_PREAMBLE)}, {"<", 1}, {ml->k, ml->klen}, {">", 1}, }; return m->wv(ctx, vec, 4); } case REDWAX_FORMAT_JSON: { const struct iovec vec[] = { {"{", 1} }; return wv(m->ctx, vec, 1); } case REDWAX_FORMAT_YAML: { const struct iovec vec[] = { {"---\n", 4}, {ml->k, ml->klen}, {": ", 2} }; return wv(m->ctx, vec, 3); } default: break; } return APR_SUCCESS; } apr_status_t redwax_metadata_pop_root(redwax_metadata_t *m) { redwax_metadata_level_t *ml = apr_array_pop(m->levels); if (!ml || !ml->root) { return APR_EGENERAL; } switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"k, ml->klen}, {">\n", 2}, }; return m->wv(m->ctx, vec, 5); } case REDWAX_FORMAT_JSON: { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"}\n", 2} }; return m->wv(m->ctx, vec, 3); } case REDWAX_FORMAT_YAML: { const struct iovec vec[] = { {"\n...\n", 5} }; return m->wv(m->ctx, vec, 1); } default: break; } return APR_SUCCESS; } apr_status_t redwax_metadata_push_array(redwax_metadata_t *m, const char *k, int empty) { redwax_metadata_level_t *ml = m->level; int array = ml->array; int object = ml->object; int next = ml->next; m->level->next = 1; m->level = ml = apr_array_push(m->levels); m->level->k = (void *)k; m->level->klen = k ? strlen(k) : 0; m->level->empty = empty; m->level->array = 1; m->level->object = 0; m->level->next = 0; m->level->root = 0; redwax_metadata_prefix(m, 1); switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { if (empty) { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"<", 1}, {ml->k, ml->klen}, {" />\n", 4}, }; return m->wv(m->ctx, vec, 5); } else { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"<", 1}, {ml->k, ml->klen}, {">", 1}, }; return m->wv(m->ctx, vec, 5); } break; } case REDWAX_FORMAT_JSON: { if (object) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"\"", 1}, {ml->k, ml->klen}, {"\": [", 4} }; return m->wv(m->ctx, vec, 6); } else if (array) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"[", 1} }; return m->wv(m->ctx, vec, 4); } break; } case REDWAX_FORMAT_YAML: { if (object) { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {ml->k, ml->klen}, {": ", 2} }; return m->wv(m->ctx, vec, 4); } else if (array) { const struct iovec vec[] = { {"\n", 1}, {m->array_prefix, m->prefix_len}, {ml->k, ml->klen}, {": ", 2} }; return m->wv(m->ctx, vec, 4); } break; } default: break; } return APR_SUCCESS; } apr_status_t redwax_metadata_pop_array(redwax_metadata_t *m) { redwax_metadata_level_t *ml = apr_array_pop(m->levels); char *prefix = m->object_prefix; int prefix_len = m->prefix_len; int empty = m->level->empty; apr_status_t status = APR_SUCCESS; if (!ml->array) { return APR_EGENERAL; } if (m->levels->nelts) { m->level = &APR_ARRAY_IDX(m->levels, m->levels->nelts - 1, redwax_metadata_level_t); } switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { if (empty) { status = APR_SUCCESS; } else { const struct iovec vec[] = { {"\n", 1}, {prefix, prefix_len}, {"k, ml->klen}, {">", 1}, }; status = m->wv(m->ctx, vec, 5); } break; } case REDWAX_FORMAT_JSON: { if (empty) { const struct iovec vec[] = { {"]", 1} }; status = m->wv(m->ctx, vec, 1); } else { const struct iovec vec[] = { {"\n", 1}, {prefix, prefix_len}, {"]", 1} }; status = m->wv(m->ctx, vec, 3); } break; } default: break; } redwax_metadata_prefix(m, -1); return status; } apr_status_t redwax_metadata_push_object(redwax_metadata_t *m, const char *k, int empty) { redwax_metadata_level_t *ml = m->level; int array = ml->array; int object = ml->object; int next = ml->next; m->level->next = 1; m->level = ml = apr_array_push(m->levels); m->level->k = (void *)k; m->level->klen = k ? strlen(k) : 0; m->level->empty = empty; m->level->array = 0; m->level->object = 1; m->level->next = 0; m->level->root = 0; redwax_metadata_prefix(m, 1); switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { if (object || array ) { if (empty) { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"<", 1}, {ml->k, ml->klen}, {" />\n", 4}, }; return m->wv(m->ctx, vec, 5); } else { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"<", 1}, {ml->k, ml->klen}, {">", 1}, }; return m->wv(m->ctx, vec, 5); } } break; } case REDWAX_FORMAT_JSON: { if (object) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"\"", 1}, {ml->k, ml->klen}, {"\": {", 4} }; return m->wv(m->ctx, vec, 6); } else if (array) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"{", 1} }; return m->wv(m->ctx, vec, 4); } break; } case REDWAX_FORMAT_YAML: { if (object) { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {ml->k, ml->klen}, {": ", 2} }; return m->wv(m->ctx, vec, 4); } else if (array) { const struct iovec vec[] = { {"\n", 1}, {m->array_prefix, m->prefix_len}, {ml->k, ml->klen}, {": ", 2} }; return m->wv(m->ctx, vec, 4); } break; } default: break; } return APR_SUCCESS; } apr_status_t redwax_metadata_pop_object(redwax_metadata_t *m) { redwax_metadata_level_t *ml = apr_array_pop(m->levels); char *prefix = m->object_prefix; int prefix_len = m->prefix_len; int empty = m->level->empty; apr_status_t status = APR_SUCCESS; if (!ml->object) { return APR_EGENERAL; } if (m->levels->nelts) { m->level = &APR_ARRAY_IDX(m->levels, m->levels->nelts - 1, redwax_metadata_level_t); } switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { if (empty) { status = APR_SUCCESS; } else { const struct iovec vec[] = { {"\n", 1}, {prefix, prefix_len}, {"k, ml->klen}, {">", 1}, }; status = m->wv(m->ctx, vec, 5); } break; } case REDWAX_FORMAT_JSON: { if (empty) { const struct iovec vec[] = { {"}", 1} }; status = m->wv(m->ctx, vec, 1); } else { const struct iovec vec[] = { {"\n", 1}, {prefix, prefix_len}, {"}", 1} }; status = m->wv(m->ctx, vec, 3); } break; } default: break; } redwax_metadata_prefix(m, -1); return status; } apr_status_t redwax_metadata_add_string(redwax_metadata_t *m, const char *key, const char *val) { redwax_metadata_level_t *ml = m->level; apr_status_t status = APR_SUCCESS; void *k = (void *)key; int klen = k ? strlen(k) : 0; int array = ml->array; int object = ml->object; int next = ml->next; ml->next = 1; redwax_metadata_prefix(m, 1); switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { void *v = (void *)apr_pescape_entity(m->pool, val, 1); int vlen = v ? strlen(v) : 0; if (!v) { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"<", 1}, {k, klen}, {" />", 3}, }; status = m->wv(m->ctx, vec, 5); } else { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"<", 1}, {k, klen}, {">", 1}, {v, vlen}, {"", 1}, }; status = m->wv(m->ctx, vec, 9); } break; } case REDWAX_FORMAT_JSON: { void *v = (void *)redwax_pescape_json(m->pool, val); int vlen = v ? strlen(v) : 0; if (object) { if (!v) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"\"", 1}, {k, klen}, {"\": null", 7} }; status = m->wv(m->ctx, vec, 6); } else { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"\"", 1}, {k, klen}, {"\": \"", 4}, {v, vlen}, {"\"", 1} }; status = m->wv(m->ctx, vec, 8); } } else if (array) { if (!v) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"null", 4} }; status = m->wv(m->ctx, vec, 4); } else { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"\"", 1}, {v, vlen}, {"\"", 1} }; status = m->wv(m->ctx, vec, 6); } } break; } case REDWAX_FORMAT_YAML: { void *v = (void *)val; if (object) { if (!v) { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {k, klen}, {": ~", 3}, }; status = m->wv(m->ctx, vec, 4); } else { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {k, klen}, {": ", 2} }; status = m->wv(m->ctx, vec, 4); if (APR_SUCCESS == status) { void *l = strchr(v, '\n'); if (l) { const struct iovec vec[] = { {"|\n", 2} }; status = m->wv(m->ctx, vec, 1); while (APR_SUCCESS == status && l) { const struct iovec vec[] = { {m->object_prefix, m->prefix_len}, {" ", 2}, {v, l - v + 1}, }; status = m->wv(m->ctx, vec, 3); l = strchr((v = l + 1), '\n'); }; if (APR_SUCCESS == status) { const struct iovec vec[] = { {m->object_prefix, m->prefix_len}, {" ", 2}, {v, strlen(v)}, }; status = m->wv(m->ctx, vec, 3); } } else if (APR_SUCCESS == status) { const struct iovec vec[] = { {v, strlen(v)}, }; status = m->wv(m->ctx, vec, 1); } } } } else if (array) { if (!v) { const struct iovec vec[] = { {"\n", 1}, {m->array_prefix, m->prefix_len}, {"~", 1}, }; status = m->wv(m->ctx, vec, 3); } else { const struct iovec vec[] = { {"\n", 1}, {m->array_prefix, m->prefix_len} }; status = m->wv(m->ctx, vec, 2); if (APR_SUCCESS == status) { void *l = strchr(v, '\n'); if (l) { const struct iovec vec[] = { {"|\n", 2} }; status = m->wv(m->ctx, vec, 1); while (APR_SUCCESS == status && l) { const struct iovec vec[] = { {m->object_prefix, m->prefix_len}, {" ", 2}, {v, l - v + 1}, }; status = m->wv(m->ctx, vec, 3); l = strchr((v = l + 1), '\n'); }; if (APR_SUCCESS == status) { const struct iovec vec[] = { {m->object_prefix, m->prefix_len}, {" ", 2}, {v, strlen(v)}, }; status = m->wv(m->ctx, vec, 3); } } else if (APR_SUCCESS == status) { const struct iovec vec[] = { {v, strlen(v)}, }; status = m->wv(m->ctx, vec, 1); } } } } break; } default: break; } redwax_metadata_prefix(m, -1); return APR_SUCCESS; } apr_status_t redwax_metadata_add_number(redwax_metadata_t *m, const char *key, void *v, apr_size_t vlen) { redwax_metadata_level_t *ml = m->level; apr_status_t status = APR_SUCCESS; void *k = (void *)key; int klen = k ? strlen(k) : 0; int array = ml->array; int object = ml->object; int next = ml->next; ml->next = 1; redwax_metadata_prefix(m, 1); switch (m->format) { case REDWAX_FORMAT_TEXT: break; case REDWAX_FORMAT_XML: { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {"<", 1}, {k, klen}, {">", 1}, {v, vlen}, {"", 1}, }; status = m->wv(m->ctx, vec, 9); break; } case REDWAX_FORMAT_JSON: { if (object) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {"\"", 1}, {k, klen}, {"\": ", 3}, {v, vlen} }; status = m->wv(m->ctx, vec, 7); } else if (array) { const struct iovec vec[] = { {next ? "," : "", next ? 1 : 0}, {"\n", 1}, {m->object_prefix, m->prefix_len}, {v, vlen} }; status = m->wv(m->ctx, vec, 4); } break; } case REDWAX_FORMAT_YAML: { if (object) { const struct iovec vec[] = { {"\n", 1}, {m->object_prefix, m->prefix_len}, {k, klen}, {": ", 2}, {v, vlen} }; status = m->wv(m->ctx, vec, 5); } else if (array) { const struct iovec vec[] = { {"\n", 1}, {m->array_prefix, m->prefix_len}, {v, vlen} }; status = m->wv(m->ctx, vec, 3); } break; } default: break; } redwax_metadata_prefix(m, -1); return status; } apr_status_t redwax_metadata_add_long(redwax_metadata_t *m, const char *key, long val) { void *v = apr_itoa(m->pool, val); int vlen = v ? strlen(v) : 0; return redwax_metadata_add_number(m, key, v, vlen); } apr_status_t redwax_metadata_add_boolean(redwax_metadata_t *m, const char *key, int val) { void *v = val ? "true" : "false"; int vlen = v ? strlen(v) : 0; return redwax_metadata_add_number(m, key, v, vlen); } apr_status_t redwax_metadata_add_null(redwax_metadata_t *m, const char *key) { return redwax_metadata_add_string(m, key, NULL); }