mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-11 17:04:33 +07:00
CL: Add `mg_url_encode_opt()` - a parametrized version of `mg_url_encode()` PUBLISHED_FROM=17fa57a7a5325b51b6e3aef3855eac4e82c35782
345 lines
9.3 KiB
C
345 lines
9.3 KiB
C
/*
|
|
* Copyright (c) 2014 Cesanta Software Limited
|
|
* All rights reserved
|
|
*/
|
|
|
|
#include "common/cs_base64.h"
|
|
#include "mg_internal.h"
|
|
#include "mg_util.h"
|
|
|
|
/* For platforms with limited libc */
|
|
#ifndef MAX
|
|
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
#endif
|
|
|
|
const char *mg_skip(const char *s, const char *end, const char *delims,
|
|
struct mg_str *v) {
|
|
v->p = s;
|
|
while (s < end && strchr(delims, *(unsigned char *) s) == NULL) s++;
|
|
v->len = s - v->p;
|
|
while (s < end && strchr(delims, *(unsigned char *) s) != NULL) s++;
|
|
return s;
|
|
}
|
|
|
|
#if MG_ENABLE_FILESYSTEM && !defined(MG_USER_FILE_FUNCTIONS)
|
|
int mg_stat(const char *path, cs_stat_t *st) {
|
|
#ifdef _WIN32
|
|
wchar_t wpath[MG_MAX_PATH];
|
|
to_wchar(path, wpath, ARRAY_SIZE(wpath));
|
|
DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st)));
|
|
return _wstati64(wpath, st);
|
|
#else
|
|
return stat(path, st);
|
|
#endif
|
|
}
|
|
|
|
FILE *mg_fopen(const char *path, const char *mode) {
|
|
#ifdef _WIN32
|
|
wchar_t wpath[MG_MAX_PATH], wmode[10];
|
|
to_wchar(path, wpath, ARRAY_SIZE(wpath));
|
|
to_wchar(mode, wmode, ARRAY_SIZE(wmode));
|
|
return _wfopen(wpath, wmode);
|
|
#else
|
|
return fopen(path, mode);
|
|
#endif
|
|
}
|
|
|
|
int mg_open(const char *path, int flag, int mode) { /* LCOV_EXCL_LINE */
|
|
#if defined(_WIN32) && !defined(WINCE)
|
|
wchar_t wpath[MG_MAX_PATH];
|
|
to_wchar(path, wpath, ARRAY_SIZE(wpath));
|
|
return _wopen(wpath, flag, mode);
|
|
#else
|
|
return open(path, flag, mode); /* LCOV_EXCL_LINE */
|
|
#endif
|
|
}
|
|
|
|
size_t mg_fread(void *ptr, size_t size, size_t count, FILE *f) {
|
|
return fread(ptr, size, count, f);
|
|
}
|
|
|
|
size_t mg_fwrite(const void *ptr, size_t size, size_t count, FILE *f) {
|
|
return fwrite(ptr, size, count, f);
|
|
}
|
|
#endif
|
|
|
|
void mg_base64_encode(const unsigned char *src, int src_len, char *dst) {
|
|
cs_base64_encode(src, src_len, dst);
|
|
}
|
|
|
|
int mg_base64_decode(const unsigned char *s, int len, char *dst) {
|
|
return cs_base64_decode(s, len, dst, NULL);
|
|
}
|
|
|
|
#if MG_ENABLE_THREADS
|
|
void *mg_start_thread(void *(*f)(void *), void *p) {
|
|
#ifdef WINCE
|
|
return (void *) CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) f, p, 0, NULL);
|
|
#elif defined(_WIN32)
|
|
return (void *) _beginthread((void(__cdecl *) (void *) ) f, 0, p);
|
|
#else
|
|
pthread_t thread_id = (pthread_t) 0;
|
|
pthread_attr_t attr;
|
|
|
|
(void) pthread_attr_init(&attr);
|
|
(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
|
|
#if defined(MG_STACK_SIZE) && MG_STACK_SIZE > 1
|
|
(void) pthread_attr_setstacksize(&attr, MG_STACK_SIZE);
|
|
#endif
|
|
|
|
pthread_create(&thread_id, &attr, f, p);
|
|
pthread_attr_destroy(&attr);
|
|
|
|
return (void *) thread_id;
|
|
#endif
|
|
}
|
|
#endif /* MG_ENABLE_THREADS */
|
|
|
|
/* Set close-on-exec bit for a given socket. */
|
|
void mg_set_close_on_exec(sock_t sock) {
|
|
#if defined(_WIN32) && !defined(WINCE)
|
|
(void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
|
|
#elif defined(__unix__)
|
|
fcntl(sock, F_SETFD, FD_CLOEXEC);
|
|
#else
|
|
(void) sock;
|
|
#endif
|
|
}
|
|
|
|
int mg_sock_addr_to_str(const union socket_address *sa, char *buf, size_t len,
|
|
int flags) {
|
|
int is_v6;
|
|
if (buf == NULL || len <= 0) return 0;
|
|
memset(buf, 0, len);
|
|
#if MG_ENABLE_IPV6
|
|
is_v6 = sa->sa.sa_family == AF_INET6;
|
|
#else
|
|
is_v6 = 0;
|
|
#endif
|
|
if (flags & MG_SOCK_STRINGIFY_IP) {
|
|
#if MG_ENABLE_IPV6
|
|
const void *addr = NULL;
|
|
char *start = buf;
|
|
socklen_t capacity = len;
|
|
if (!is_v6) {
|
|
addr = &sa->sin.sin_addr;
|
|
} else {
|
|
addr = (void *) &sa->sin6.sin6_addr;
|
|
if (flags & MG_SOCK_STRINGIFY_PORT) {
|
|
*buf = '[';
|
|
start++;
|
|
capacity--;
|
|
}
|
|
}
|
|
if (inet_ntop(sa->sa.sa_family, addr, start, capacity) == NULL) {
|
|
goto cleanup;
|
|
}
|
|
#elif defined(_WIN32) || MG_LWIP || (MG_NET_IF == MG_NET_IF_PIC32)
|
|
/* Only Windoze Vista (and newer) have inet_ntop() */
|
|
char *addr_str = inet_ntoa(sa->sin.sin_addr);
|
|
if (addr_str != NULL) {
|
|
strncpy(buf, inet_ntoa(sa->sin.sin_addr), len - 1);
|
|
} else {
|
|
goto cleanup;
|
|
}
|
|
#else
|
|
if (inet_ntop(AF_INET, (void *) &sa->sin.sin_addr, buf, len) == NULL) {
|
|
goto cleanup;
|
|
}
|
|
#endif
|
|
}
|
|
if (flags & MG_SOCK_STRINGIFY_PORT) {
|
|
int port = ntohs(sa->sin.sin_port);
|
|
if (flags & MG_SOCK_STRINGIFY_IP) {
|
|
int buf_len = strlen(buf);
|
|
snprintf(buf + buf_len, len - (buf_len + 1), "%s:%d", (is_v6 ? "]" : ""),
|
|
port);
|
|
} else {
|
|
snprintf(buf, len, "%d", port);
|
|
}
|
|
}
|
|
|
|
return strlen(buf);
|
|
|
|
cleanup:
|
|
*buf = '\0';
|
|
return 0;
|
|
}
|
|
|
|
int mg_conn_addr_to_str(struct mg_connection *nc, char *buf, size_t len,
|
|
int flags) {
|
|
union socket_address sa;
|
|
memset(&sa, 0, sizeof(sa));
|
|
mg_if_get_conn_addr(nc, flags & MG_SOCK_STRINGIFY_REMOTE, &sa);
|
|
return mg_sock_addr_to_str(&sa, buf, len, flags);
|
|
}
|
|
|
|
#if MG_ENABLE_HEXDUMP
|
|
static int mg_hexdump_n(const void *buf, int len, char *dst, int dst_len,
|
|
int offset) {
|
|
const unsigned char *p = (const unsigned char *) buf;
|
|
char ascii[17] = "";
|
|
int i, idx, n = 0;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
idx = i % 16;
|
|
if (idx == 0) {
|
|
if (i > 0) n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii);
|
|
n += snprintf(dst + n, MAX(dst_len - n, 0), "%04x ", i + offset);
|
|
}
|
|
if (dst_len - n < 0) {
|
|
return n;
|
|
}
|
|
n += snprintf(dst + n, MAX(dst_len - n, 0), " %02x", p[i]);
|
|
ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
|
|
ascii[idx + 1] = '\0';
|
|
}
|
|
|
|
while (i++ % 16) n += snprintf(dst + n, MAX(dst_len - n, 0), "%s", " ");
|
|
n += snprintf(dst + n, MAX(dst_len - n, 0), " %s\n", ascii);
|
|
|
|
return n;
|
|
}
|
|
|
|
int mg_hexdump(const void *buf, int len, char *dst, int dst_len) {
|
|
return mg_hexdump_n(buf, len, dst, dst_len, 0);
|
|
}
|
|
|
|
void mg_hexdumpf(FILE *fp, const void *buf, int len) {
|
|
char tmp[80];
|
|
int offset = 0, n;
|
|
while (len > 0) {
|
|
n = (len < 16 ? len : 16);
|
|
mg_hexdump_n(((const char *) buf) + offset, n, tmp, sizeof(tmp), offset);
|
|
fputs(tmp, fp);
|
|
offset += n;
|
|
len -= n;
|
|
}
|
|
}
|
|
|
|
void mg_hexdump_connection(struct mg_connection *nc, const char *path,
|
|
const void *buf, int num_bytes, int ev) {
|
|
FILE *fp = NULL;
|
|
char src[60], dst[60];
|
|
const char *tag = NULL;
|
|
switch (ev) {
|
|
case MG_EV_RECV:
|
|
tag = "<-";
|
|
break;
|
|
case MG_EV_SEND:
|
|
tag = "->";
|
|
break;
|
|
case MG_EV_ACCEPT:
|
|
tag = "<A";
|
|
break;
|
|
case MG_EV_CONNECT:
|
|
tag = "C>";
|
|
break;
|
|
case MG_EV_CLOSE:
|
|
tag = "XX";
|
|
break;
|
|
}
|
|
if (tag == NULL) return; /* Don't log MG_EV_TIMER, etc */
|
|
|
|
if (strcmp(path, "-") == 0) {
|
|
fp = stdout;
|
|
} else if (strcmp(path, "--") == 0) {
|
|
fp = stderr;
|
|
#if MG_ENABLE_FILESYSTEM
|
|
} else {
|
|
fp = mg_fopen(path, "a");
|
|
#endif
|
|
}
|
|
if (fp == NULL) return;
|
|
|
|
mg_conn_addr_to_str(nc, src, sizeof(src),
|
|
MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
|
|
mg_conn_addr_to_str(nc, dst, sizeof(dst), MG_SOCK_STRINGIFY_IP |
|
|
MG_SOCK_STRINGIFY_PORT |
|
|
MG_SOCK_STRINGIFY_REMOTE);
|
|
fprintf(fp, "%lu %p %s %s %s %d\n", (unsigned long) mg_time(), (void *) nc,
|
|
src, tag, dst, (int) num_bytes);
|
|
if (num_bytes > 0) {
|
|
mg_hexdumpf(fp, buf, num_bytes);
|
|
}
|
|
if (fp != stdout && fp != stderr) fclose(fp);
|
|
}
|
|
#endif
|
|
|
|
int mg_is_big_endian(void) {
|
|
static const int n = 1;
|
|
/* TODO(mkm) use compiletime check with 4-byte char literal */
|
|
return ((char *) &n)[0] == 0;
|
|
}
|
|
|
|
DO_NOT_WARN_UNUSED MG_INTERNAL int mg_get_errno(void) {
|
|
#ifndef WINCE
|
|
return errno;
|
|
#else
|
|
/* TODO(alashkin): translate error codes? */
|
|
return GetLastError();
|
|
#endif
|
|
}
|
|
|
|
void mg_mbuf_append_base64_putc(char ch, void *user_data) {
|
|
struct mbuf *mbuf = (struct mbuf *) user_data;
|
|
mbuf_append(mbuf, &ch, sizeof(ch));
|
|
}
|
|
|
|
void mg_mbuf_append_base64(struct mbuf *mbuf, const void *data, size_t len) {
|
|
struct cs_base64_ctx ctx;
|
|
cs_base64_init(&ctx, mg_mbuf_append_base64_putc, mbuf);
|
|
cs_base64_update(&ctx, (const char *) data, len);
|
|
cs_base64_finish(&ctx);
|
|
}
|
|
|
|
void mg_basic_auth_header(const struct mg_str user, const struct mg_str pass,
|
|
struct mbuf *buf) {
|
|
const char *header_prefix = "Authorization: Basic ";
|
|
const char *header_suffix = "\r\n";
|
|
|
|
struct cs_base64_ctx ctx;
|
|
cs_base64_init(&ctx, mg_mbuf_append_base64_putc, buf);
|
|
|
|
mbuf_append(buf, header_prefix, strlen(header_prefix));
|
|
|
|
cs_base64_update(&ctx, user.p, user.len);
|
|
if (pass.len > 0) {
|
|
cs_base64_update(&ctx, ":", 1);
|
|
cs_base64_update(&ctx, pass.p, pass.len);
|
|
}
|
|
cs_base64_finish(&ctx);
|
|
mbuf_append(buf, header_suffix, strlen(header_suffix));
|
|
}
|
|
|
|
struct mg_str mg_url_encode_opt(const struct mg_str src,
|
|
const struct mg_str safe, unsigned int flags) {
|
|
const char *hex =
|
|
(flags & MG_URL_ENCODE_F_UPPERCASE_HEX ? "0123456789ABCDEF"
|
|
: "0123456789abcdef");
|
|
size_t i = 0;
|
|
struct mbuf mb;
|
|
mbuf_init(&mb, src.len);
|
|
|
|
for (i = 0; i < src.len; i++) {
|
|
const unsigned char c = *((const unsigned char *) src.p + i);
|
|
if (isalnum(c) || mg_strchr(safe, c) != NULL) {
|
|
mbuf_append(&mb, &c, 1);
|
|
} else if (c == ' ' && (flags & MG_URL_ENCODE_F_SPACE_AS_PLUS)) {
|
|
mbuf_append(&mb, "+", 1);
|
|
} else {
|
|
mbuf_append(&mb, "%", 1);
|
|
mbuf_append(&mb, &hex[c >> 4], 1);
|
|
mbuf_append(&mb, &hex[c & 15], 1);
|
|
}
|
|
}
|
|
mbuf_append(&mb, "", 1);
|
|
mbuf_trim(&mb);
|
|
return mg_mk_str_n(mb.buf, mb.len - 1);
|
|
}
|
|
|
|
struct mg_str mg_url_encode(const struct mg_str src) {
|
|
return mg_url_encode_opt(src, mg_mk_str("._-$,;~()/"), 0);
|
|
}
|