#include "config.h" // Must go first - important for test++ #include "util.h" #if MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__) #include #endif char *mg_file_read(const char *path, size_t *sizep) { FILE *fp; char *data = NULL; size_t size = 0; if ((fp = fopen(path, "rb")) != NULL) { fseek(fp, 0, SEEK_END); size = (size_t) ftell(fp); rewind(fp); data = (char *) calloc(1, size + 1); if (data != NULL) { if (fread(data, 1, size, fp) != size) { free(data); data = NULL; } else { data[size] = '\0'; if (sizep != NULL) *sizep = size; } } fclose(fp); } return data; } bool mg_file_write(const char *path, const void *buf, size_t len) { bool result = false; FILE *fp; char tmp[MG_PATH_MAX]; snprintf(tmp, sizeof(tmp), "%s.%d", path, rand()); fp = fopen(tmp, "wb"); if (fp != NULL) { result = fwrite(buf, 1, len, fp) == len; fclose(fp); if (result) { remove(path); rename(tmp, path); } else { remove(tmp); } } return result; } bool mg_file_printf(const char *path, const char *fmt, ...) { char tmp[256], *buf = tmp; bool result; int len; va_list ap; va_start(ap, fmt); len = mg_vasprintf(&buf, sizeof(tmp), fmt, ap); va_end(ap); result = mg_file_write(path, buf, len > 0 ? (size_t) len : 0); if (buf != tmp) free(buf); return result; } #if MG_ENABLE_CUSTOM_RANDOM #else void mg_random(void *buf, size_t len) { bool done = false; unsigned char *p = (unsigned char *) buf; #if MG_ARCH == MG_ARCH_ESP32 while (len--) *p++ = (unsigned char) (esp_random() & 255); done = true; #elif MG_ARCH == MG_ARCH_WIN32 #elif MG_ARCH_UNIX FILE *fp = fopen("/dev/urandom", "rb"); if (fp != NULL) { if (fread(buf, 1, len, fp) == len) done = true; fclose(fp); } #endif // If everything above did not work, fallback to a pseudo random generator while (!done && len--) *p++ = (unsigned char) (rand() & 255); } #endif bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) { size_t i = 0, j = 0, ni = 0, nj = 0; while (i < n1 || j < n2) { if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) { i++, j++; } else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) { ni = i, nj = j + 1, i++; } else if (nj > 0 && nj <= n2 && (s1[ni] == '#' || s2[j] != '/')) { i = ni, j = nj; } else { // printf(">>: [%s] [%s] %d %d %d %d\n", s1, s2, i, j, ni, nj); return false; } } return true; } static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff, size_t *klen, size_t *voff, size_t *vlen) { size_t kvlen, kl; for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != ',';) kvlen++; for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++; if (koff != NULL) *koff = ofs; if (klen != NULL) *klen = kl; if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0; if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0; ofs += kvlen + 1; return ofs > n ? n : ofs; } bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) { size_t koff = 0, klen = 0, voff = 0, vlen = 0; size_t off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen); if (k != NULL) *k = mg_str_n(s->ptr + koff, klen); if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen); *s = mg_str_n(s->ptr + off, s->len - off); return off > 0; } uint32_t mg_ntohl(uint32_t net) { uint8_t data[4] = {0, 0, 0, 0}; memcpy(&data, &net, sizeof(data)); return (((uint32_t) data[3]) << 0) | (((uint32_t) data[2]) << 8) | (((uint32_t) data[1]) << 16) | (((uint32_t) data[0]) << 24); } uint16_t mg_ntohs(uint16_t net) { uint8_t data[2] = {0, 0}; memcpy(&data, &net, sizeof(data)); return (uint16_t) ((uint16_t) data[1] | (((uint16_t) data[0]) << 8)); } char *mg_hexdump(const void *buf, size_t len) { const unsigned char *p = (const unsigned char *) buf; size_t i, idx, n = 0, ofs = 0, dlen = len * 5 + 100; char ascii[17] = "", *dst = (char *) calloc(1, dlen); if (dst == NULL) return dst; for (i = 0; i < len; i++) { idx = i % 16; if (idx == 0) { if (i > 0 && dlen > n) n += (size_t) snprintf(dst + n, dlen - n, " %s\n", ascii); if (dlen > n) n += (size_t) snprintf(dst + n, dlen - n, "%04x ", (int) (i + ofs)); } if (dlen < n) break; n += (size_t) snprintf(dst + n, dlen - n, " %02x", p[i]); ascii[idx] = (char) (p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i]); ascii[idx + 1] = '\0'; } while (i++ % 16) { if (n < dlen) n += (size_t) snprintf(dst + n, dlen - n, "%s", " "); } if (n < dlen) n += (size_t) snprintf(dst + n, dlen - n, " %s\n", ascii); if (n > dlen - 1) n = dlen - 1; dst[n] = '\0'; return dst; } char *mg_hex(const void *buf, size_t len, char *to) { const unsigned char *p = (const unsigned char *) buf; static const char *hex = "0123456789abcdef"; size_t i = 0; for (; len--; p++) { to[i++] = hex[p[0] >> 4]; to[i++] = hex[p[0] & 0x0f]; } to[i] = '\0'; return to; } static unsigned char mg_unhex_nimble(unsigned char c) { return (c >= '0' && c <= '9') ? (unsigned char) (c - '0') : (c >= 'A' && c <= 'F') ? (unsigned char) (c - '7') : (unsigned char) (c - 'W'); } unsigned long mg_unhexn(const char *s, size_t len) { unsigned long i = 0, v = 0; for (i = 0; i < len; i++) v <<= 4, v |= mg_unhex_nimble(((uint8_t *) s)[i]); return v; } void mg_unhex(const char *buf, size_t len, unsigned char *to) { size_t i; for (i = 0; i < len; i += 2) { to[i >> 1] = (unsigned char) mg_unhexn(&buf[i], 2); } } int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap) { va_list ap_copy; int len; va_copy(ap_copy, ap); len = vsnprintf(*buf, size, fmt, ap_copy); va_end(ap_copy); if (len < 0) { // eCos and Windows are not standard-compliant and return -1 when // the buffer is too small. Keep allocating larger buffers until we // succeed or out of memory. // LCOV_EXCL_START *buf = NULL; while (len < 0) { free(*buf); if (size == 0) size = 5; size *= 2; if ((*buf = (char *) calloc(1, size)) == NULL) { len = -1; break; } va_copy(ap_copy, ap); len = vsnprintf(*buf, size - 1, fmt, ap_copy); va_end(ap_copy); } // Microsoft version of vsnprintf() is not always null-terminated, so put // the terminator manually if (*buf != NULL) (*buf)[len] = 0; // LCOV_EXCL_STOP } else if (len >= (int) size) { /// Standard-compliant code path. Allocate a buffer that is large enough if ((*buf = (char *) calloc(1, (size_t) len + 1)) == NULL) { len = -1; // LCOV_EXCL_LINE } else { // LCOV_EXCL_LINE va_copy(ap_copy, ap); len = vsnprintf(*buf, (size_t) len + 1, fmt, ap_copy); va_end(ap_copy); } } return len; } int mg_asprintf(char **buf, size_t size, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = mg_vasprintf(buf, size, fmt, ap); va_end(ap); return ret; } int64_t mg_to64(struct mg_str str) { int64_t result = 0, neg = 1, max = 922337203685477570 /* INT64_MAX/10-10 */; size_t i = 0; while (i < str.len && (str.ptr[i] == ' ' || str.ptr[i] == '\t')) i++; if (i < str.len && str.ptr[i] == '-') neg = -1, i++; while (i < str.len && str.ptr[i] >= '0' && str.ptr[i] <= '9') { if (result > max) return 0; result *= 10; result += (str.ptr[i] - '0'); i++; } return result * neg; } uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len) { int i; crc = ~crc; while (len--) { crc ^= *(unsigned char *) buf++; for (i = 0; i < 8; i++) crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1; } return ~crc; } static int isbyte(int n) { return n >= 0 && n <= 255; } static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) { int n, a, b, c, d, slash = 32, len = 0; if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 || sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && isbyte(a) && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 && slash < 33) { len = n; *net = ((uint32_t) a << 24) | ((uint32_t) b << 16) | ((uint32_t) c << 8) | (uint32_t) d; *mask = slash ? (uint32_t) (0xffffffffU << (32 - slash)) : (uint32_t) 0; } return len; } int mg_check_ip_acl(struct mg_str acl, uint32_t remote_ip) { struct mg_str k, v; int allowed = acl.len == 0 ? '+' : '-'; // If any ACL is set, deny by default while (mg_commalist(&acl, &k, &v)) { uint32_t net, mask; if (k.ptr[0] != '+' && k.ptr[0] != '-') return -1; if (parse_net(&k.ptr[1], &net, &mask) == 0) return -2; if ((remote_ip & mask) == net) allowed = k.ptr[0]; } return allowed == '+'; } double mg_time(void) { #if MG_ARCH == MG_ARCH_WIN32 SYSTEMTIME sysnow; FILETIME ftime; GetLocalTime(&sysnow); SystemTimeToFileTime(&sysnow, &ftime); /* * 1. VC 6.0 doesn't support conversion uint64 -> double, so, using int64 * This should not cause a problems in this (21th) century * 2. Windows FILETIME is a number of 100-nanosecond intervals since January * 1, 1601 while time_t is a number of _seconds_ since January 1, 1970 UTC, * thus, we need to convert to seconds and adjust amount (subtract 11644473600 * seconds) */ return (double) (((int64_t) ftime.dwLowDateTime + ((int64_t) ftime.dwHighDateTime << 32)) / 10000000.0) - 11644473600; #elif MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_AZURERTOS return mg_millis() / 1000.0; #else struct timeval tv; if (gettimeofday(&tv, NULL /* tz */) != 0) return 0; return (double) tv.tv_sec + (((double) tv.tv_usec) / 1000000.0); #endif /* _WIN32 */ } void mg_usleep(unsigned long usecs) { #if MG_ARCH == MG_ARCH_WIN32 Sleep(usecs / 1000); #elif MG_ARCH == MG_ARCH_ESP8266 ets_delay_us(usecs); #elif MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_FREERTOS_LWIP vTaskDelay(pdMS_TO_TICKS(usecs / 1000)); #elif MG_ARCH == MG_ARCH_AZURERTOS tx_thread_sleep((usecs / 1000) * TX_TIMER_TICKS_PER_SECOND); #else usleep((useconds_t) usecs); #endif } unsigned long mg_millis(void) { #if MG_ARCH == MG_ARCH_WIN32 return GetTickCount(); #elif MG_ARCH == MG_ARCH_ESP32 return esp_timer_get_time() / 1000; #elif MG_ARCH == MG_ARCH_ESP8266 return xTaskGetTickCount() * portTICK_PERIOD_MS; #elif MG_ARCH == MG_ARCH_FREERTOS_TCP || MG_ARCH == MG_ARCH_FREERTOS_LWIP return xTaskGetTickCount() * portTICK_PERIOD_MS; #elif MG_ARCH == MG_ARCH_AZURERTOS return tx_time_get() * (1000 /* MS per SEC */ / TX_TIMER_TICKS_PER_SECOND); #elif MG_ARCH == MG_ARCH_UNIX && defined(__APPLE__) uint64_t ticks = mach_absolute_time(); static mach_timebase_info_data_t timebase; mach_timebase_info(&timebase); double ticks_to_nanos = (double)timebase.numer / timebase.denom; uint64_t uptime_nanos = (uint64_t)(ticks_to_nanos * ticks); return (unsigned long) (uptime_nanos / 1000000); #else struct timespec ts; #ifdef _POSIX_MONOTONIC_CLOCK #ifdef CLOCK_MONOTONIC_RAW clock_gettime(CLOCK_MONOTONIC_RAW, &ts); #else clock_gettime(CLOCK_MONOTONIC, &ts); #endif #else clock_gettime(CLOCK_REALTIME, &ts); #endif return (unsigned long) ((uint64_t) ts.tv_sec * 1000 + (uint64_t) ts.tv_nsec / 1000000); #endif }