// Copyright (c) 2004-2013 Sergey Lyubka
// Copyright (c) 2013-2021 Cesanta Software Limited
// All rights reserved
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation. For the terms of this
// license, see <http://www.gnu.org/licenses/>.
//
// You are free to use this software under the terms of the GNU General
// Public License, but WITHOUT ANY WARRANTY; without even the implied
// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// Alternatively, you can license this software under a commercial
// license, as set out in <https://www.cesanta.com/license>.
#pragma once
#define MG_VERSION "7.3"

#ifdef __cplusplus
extern "C" {
#endif

#define _POSIX_SOURCE 1
#define MG_ARCH_CUSTOM 0
#define MG_ARCH_UNIX 1
#define MG_ARCH_WIN32 2
#define MG_ARCH_ESP32 3
#define MG_ARCH_ESP8266 4
#define MG_ARCH_FREERTOS_TCP 5
#define MG_ARCH_FREERTOS_LWIP 6
#define MG_ARCH_W317          317
#define MG_ARCH_ASR           318
#define MG_ARCH_AC79          319
#define MG_ARCH_QUECTEL       320
#define MG_ARCH_TXW81         321
#define MG_ARCH_FIBOCOM       322
#define MG_ARCH_BK            323

/* MG_ARCH 由 Makefile 传入（-DMG_ARCH=319），无需在此硬编码 */
#define PATH_MAX 128
#define MG_ENABLE_LOG 1

#if !defined(MG_ARCH)
#if defined(__unix__) || defined(__APPLE__) || defined(__QNX__)
#define MG_ARCH MG_ARCH_UNIX
#elif defined(_WIN32)
#define MG_ARCH MG_ARCH_WIN32
#elif defined(ICACHE_FLASH) || defined(ICACHE_RAM_ATTR)
#define MG_ARCH MG_ARCH_ESP8266
#elif defined(ESP_PLATFORM)
#define MG_ARCH MG_ARCH_ESP32
#elif defined(FREERTOS_IP_H)
#define MG_ARCH MG_ARCH_FREERTOS_TCP
#endif

#if !defined(MG_ARCH)
#define MG_ARCH MG_ARCH_W317
#endif

#if !defined(MG_ARCH)
#error "MG_ARCH is not specified and we couldn't guess it."
#endif
#endif  // !defined(MG_ARCH)

#if !defined(PRINTF_LIKE)
#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__)
#define PRINTF_LIKE(f, a) __attribute__((format(printf, f, a)))
#else
#define PRINTF_LIKE(f, a)
#endif
#endif

#if MG_ARCH == MG_ARCH_CUSTOM
#include <mongoose_custom.h>
#endif









#if MG_ARCH == MG_ARCH_ESP32

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>

#define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld"
#ifndef MG_PATH_MAX
#define MG_PATH_MAX 128
#endif

#endif


#if MG_ARCH == MG_ARCH_ESP8266

#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <netdb.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>

#include <esp_system.h>

#define MG_DIRSEP '/'
#define MG_INT64_FMT "%lld"

#endif


#if MG_ARCH == MG_ARCH_FREERTOS_LWIP

#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>

#include <sys/time.h>

#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

// #include <freertos/FreeRTOS.h>
#include <portmacro.h>
#include <wm_mem.h>

#include <lwip/sockets.h>

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'

// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib

static inline void *mg_calloc(int cnt, size_t size) {
  void *p = tls_mem_alloc(cnt * size);
  if (p != NULL) memset(p, 0, size);
  return p;
}
#define calloc(a, b) mg_calloc((a), (b))
#define free(a) tls_mem_free(a)
#define malloc(a) tls_mem_alloc(a) // pvPortMalloc(a)

#define gmtime_r(a, b) gmtime(a)

#endif  // MG_ARCH == MG_ARCH_FREERTOS_LWIP


#if MG_ARCH == MG_ARCH_FREERTOS_TCP

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

#include <freertos/FreeRTOS.h>
#include <freertos/FreeRTOS_IP.h>
#include <freertos/FreeRTOS_Sockets.h>
#include <freertos/task.h>

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'

// Why FreeRTOS-TCP did not implement a clean BSD API, but its own thing
// with FreeRTOS_ prefix, is beyond me
#define IPPROTO_TCP FREERTOS_IPPROTO_TCP
#define IPPROTO_UDP FREERTOS_IPPROTO_UDP
#define AF_INET FREERTOS_AF_INET
#define SOCK_STREAM FREERTOS_SOCK_STREAM
#define SOCK_DGRAM FREERTOS_SOCK_DGRAM
#define SO_BROADCAST 0
#define SO_ERROR 0
#define SOL_SOCKET 0
#define SO_REUSEADDR 0
#define sockaddr_in freertos_sockaddr
#define sockaddr freertos_sockaddr
#define accept(a, b, c) FreeRTOS_accept((a), (b), (c))
#define connect(a, b, c) FreeRTOS_connect((a), (b), (c))
#define bind(a, b, c) FreeRTOS_bind((a), (b), (c))
#define listen(a, b) FreeRTOS_listen((a), (b))
#define socket(a, b, c) FreeRTOS_socket((a), (b), (c))
#define send(a, b, c, d) FreeRTOS_send((a), (b), (c), (d))
#define recv(a, b, c, d) FreeRTOS_recv((a), (b), (c), (d))
#define setsockopt(a, b, c, d, e) FreeRTOS_setsockopt((a), (b), (c), (d), (e))
#define sendto(a, b, c, d, e, f) FreeRTOS_sendto((a), (b), (c), (d), (e), (f))
#define recvfrom(a, b, c, d, e, f) \
  FreeRTOS_recvfrom((a), (b), (c), (d), (e), (f))
#define closesocket(x) FreeRTOS_closesocket(x)
#define gethostbyname(x) FreeRTOS_gethostbyname(x)

// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
static inline void *mg_calloc(int cnt, size_t size) {
  void *p = pvPortMalloc(cnt * size);
  if (p != NULL) memset(p, 0, size);
  return p;
}
#define calloc(a, b) mg_calloc((a), (b))
#define free(a) vPortFree(a)
#define malloc(a) pvPortMalloc(a)

#define gmtime_r(a, b) gmtime(a)

#if !defined(__GNUC__)
// copied from GCC on ARM; for some reason useconds are signed
typedef long suseconds_t;
struct timeval {
  time_t tv_sec;
  suseconds_t tv_usec;
};
#endif

#ifndef EINPROGRESS
#define EINPROGRESS pdFREERTOS_ERRNO_EINPROGRESS
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK pdFREERTOS_ERRNO_EWOULDBLOCK
#endif
#ifndef EAGAIN
#define EAGAIN pdFREERTOS_ERRNO_EAGAIN
#endif
#ifndef EINTR
#define EINTR pdFREERTOS_ERRNO_EINTR
#endif

#endif  // MG_ARCH == MG_ARCH_FREERTOS_TCP


#if MG_ARCH == MG_ARCH_UNIX

#define _DARWIN_UNLIMITED_SELECT 1

#include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define MG_DIRSEP '/'
#define MG_ENABLE_POSIX 1
#define MG_INT64_FMT "%" PRId64

#endif

#if MG_ARCH == MG_ARCH_W317

#define _DARWIN_UNLIMITED_SELECT 1
#include "os_api.h"
// #include <arpa/inet.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <netdb.h>
// #include <netinet/tcp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define MG_DIRSEP '/'
#define MG_ENABLE_POSIX 1
#define MG_INT64_FMT "%" PRId64

#ifndef EINPROGRESS
#define EINPROGRESS 36
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK 35
#endif

#endif

#if MG_ARCH == MG_ARCH_WIN32

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif

#ifndef _WINSOCK_DEPRECATED_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#endif

#include <ctype.h>
#if BRTC_PLATFORM != BRTC_PLATFORM_BK
#include <errno.h>
#endif
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>

#if defined(_MSC_VER) && _MSC_VER < 1700
#define __func__ ""
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned char uint8_t;
typedef char int8_t;
typedef unsigned short uint16_t;
typedef short int16_t;
typedef unsigned int uint32_t;
typedef int int32_t;
typedef enum { false = 0, true = 1 } bool;
#else
#include <stdbool.h>
#include <stdint.h>
#include <ws2tcpip.h>
#endif

#include <winsock2.h>

// Protect from calls like std::snprintf in app code
// See https://github.com/cesanta/mongoose/issues/1047
#ifndef __cplusplus
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define strdup(x) _strdup(x)
#endif

typedef unsigned suseconds_t;
typedef int socklen_t;
#define MG_DIRSEP '\\'
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#define realpath(a, b) _fullpath((b), (a), MG_PATH_MAX)
#ifndef va_copy
#ifdef __va_copy
#define va_copy __va_copy
#else
#define va_copy(x, y) (x) = (y)
#endif
#endif
#ifndef S_ISDIR
#define S_ISDIR(x) (((x) &_S_IFMT) == _S_IFDIR)
#endif

#define MG_INT64_FMT "%I64d"

// https://lgtm.com/rules/2154840805/ -gmtime, localtime, ctime and asctime
static __inline struct tm *gmtime_r(time_t *t, struct tm *tm) {
  (void) tm;
  return gmtime(t);
}

static __inline struct tm *localtime_r(time_t *t, struct tm *tm) {
  (void) tm;
  return localtime(t);
}

#endif

#if MG_ARCH == MG_ARCH_ASR

#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>

//#include <sys/time.h>

#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

// #include <freertos/FreeRTOS.h>
//#include <lwip/sockets.h>
#include <sockets.h>

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'
/*
// Re-route calloc/free to the FreeRTOS's functions, don't use stdlib
static inline void *mg_calloc(int cnt, size_t size) {
  void *p = tls_mem_alloc(cnt * size);
  if (p != NULL) memset(p, 0, size);
  return p;
}
#define calloc(a, b) mg_calloc((a), (b))
#define free(a) tls_mem_free(a)
#define malloc(a) tls_mem_alloc(a) // pvPortMalloc(a)
*/
#define gmtime_r(a, b) gmtime(a)

#endif

#if MG_ARCH == MG_ARCH_BK
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
//#include <sys/time.h>
#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

// #include <freertos/FreeRTOS.h>
#include <lwip/sockets.h>
//#include <sockets.h>

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'

#define gmtime_r(a, b) gmtime(a)
#endif

#if MG_ARCH == MG_ARCH_AC79
extern int errno;
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>

//#include <sys/time.h>

#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

#ifndef EINPROGRESS
#define EINPROGRESS 36
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK 35
#endif

// #include <freertos/FreeRTOS.h>
#include <lwip/sockets.h>
//#include <sockets.h>

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'

#define gmtime_r(a, b) gmtime(a)
#endif


#if MG_ARCH == MG_ARCH_QUECTEL
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ql_type.h"
#include "ql_rtos.h"
#include "ql_application.h"
#include "ql_data_call.h"
#include "sockets.h"
#include "netdb.h"

#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

#ifndef EINPROGRESS
#define EINPROGRESS 36
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK 35
#endif

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'

#define gmtime_r(a, b) gmtime(a)
#endif

#if MG_ARCH == MG_ARCH_TXW81

#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>


#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

#ifndef EINPROGRESS
#define EINPROGRESS 36
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK 35
#endif

#include <lwip/sockets.h>

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'

#define gmtime_r(a, b) gmtime(a)
#endif

#if MG_ARCH == MG_ARCH_FIBOCOM
#include "fibo_opencpu.h"
#include <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>

#if MG_ENABLE_FS
#include <sys/stat.h>
#endif

#ifndef EINPROGRESS
#define EINPROGRESS 36
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK 35
#endif

#define MG_INT64_FMT "%lld"
#define MG_DIRSEP '/'

#define gmtime_r(a, b) gmtime(a)
#endif

#ifndef MG_ENABLE_SOCKET
#define MG_ENABLE_SOCKET 1
#endif

#ifndef MG_ENABLE_MBEDTLS
#define MG_ENABLE_MBEDTLS 0
#endif

#ifndef MG_ENABLE_OPENSSL
#define MG_ENABLE_OPENSSL 0
#endif

#ifndef MG_ENABLE_FS
#define MG_ENABLE_FS 0
#endif

#ifndef MG_ENABLE_SSI
#define MG_ENABLE_SSI 0
#endif

#ifndef MG_ENABLE_IPV6
#define MG_ENABLE_IPV6 0
#endif

#ifndef MG_ENABLE_LOG
#define MG_ENABLE_LOG 1
#endif

#ifndef MG_ENABLE_MD5
#define MG_ENABLE_MD5 0
#endif

// Set MG_ENABLE_WINSOCK=0 for Win32 builds with external IP stack (like LWIP)
#ifndef MG_ENABLE_WINSOCK
#define MG_ENABLE_WINSOCK 1
#endif

#ifndef MG_ENABLE_DIRECTORY_LISTING
#define MG_ENABLE_DIRECTORY_LISTING 0
#endif

#ifndef MG_ENABLE_SOCKETPAIR
#define MG_ENABLE_SOCKETPAIR 0
#endif

// Granularity of the send/recv IO buffer growth
#ifndef MG_IO_SIZE
#define MG_IO_SIZE 512
#endif

// Maximum size of the recv IO buffer
#ifndef MG_MAX_RECV_BUF_SIZE
#define MG_MAX_RECV_BUF_SIZE (3 * 1024 * 1024)
#endif

#ifndef MG_MAX_HTTP_HEADERS
#define MG_MAX_HTTP_HEADERS 40
#endif

#ifndef MG_PATH_MAX
#define MG_PATH_MAX PATH_MAX
#endif


#include <ctype.h>
#include <string.h>

struct mg_str {
  const char *ptr;
  size_t len;
};

#define MG_NULL_STR \
  { NULL, 0 }

#define mg_str(s) brtc_mg_str(s)
#define mg_str_n brtc_mg_str_n
#define mg_lower brtc_mg_lower
#define mg_ncasecmp brtc_mg_ncasecmp
#define mg_casecmp brtc_mg_casecmp
#define mg_vcmp brtc_mg_vcmp
#define mg_vcasecmp brtc_mg_vcasecmp
#define mg_strcmp brtc_mg_strcmp
#define mg_strstrip brtc_mg_strstrip
#define mg_strdup brtc_mg_strdup
#define mg_strstr brtc_mg_strstr
#define mg_timer_init brtc_mg_timer_init
#define mg_timer_free brtc_mg_timer_free
#define mg_timer_poll brtc_mg_timer_poll

struct mg_str mg_str(const char *s);
struct mg_str mg_str_n(const char *s, size_t n);
int mg_lower(const char *s);
int mg_ncasecmp(const char *s1, const char *s2, size_t len);
int mg_casecmp(const char *s1, const char *s2);
int mg_vcmp(const struct mg_str *s1, const char *s2);
int mg_vcasecmp(const struct mg_str *str1, const char *str2);
int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
struct mg_str mg_strstrip(struct mg_str s);
struct mg_str mg_strdup(const struct mg_str s);
const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);





#if MG_ENABLE_LOG
#define LOG(level, args)                                                   \
  do {                                                                     \
    if (mg_log_prefix((level), __FILE__, __LINE__, __func__)) mg_log args; \
  } while (0)
enum { LL_NONE, LL_ERROR, LL_INFO, LL_DEBUG, LL_VERBOSE_DEBUG };
bool mg_log_prefix(int ll, const char *file, int line, const char *fname);
void mg_log(const char *fmt, ...);
void mg_log_set(const char *spec);
void mg_log_set_callback(void (*fn)(const void *, size_t, void *), void *param);
#else
#define LOG(level, args) (void) 0
#define mg_log_set(x) (void) (x)
#endif


struct mg_timer {
  unsigned long period_ms;  // Timer period in milliseconds
  unsigned flags;           // Possible flags values below
#define MG_TIMER_REPEAT 1   // Call function periodically, otherwise run once
#define MG_TIMER_RUN_NOW 2  // Call immediately when timer is set
  void (*fn)(void *);       // Function to call
  void *arg;                // Function argument
  unsigned long expire;     // Expiration timestamp in milliseconds
  struct mg_timer *next;    // Linkage in g_timers list
};

extern struct mg_timer *g_timers;  // Global list of timers

void mg_timer_init(struct mg_timer *, unsigned long ms, unsigned,
                   void (*fn)(void *), void *);
void mg_timer_free(struct mg_timer *);
void mg_timer_poll(unsigned long uptime_ms);





// WEAK symbol makes it possible to define a "default" function implementation,
// which could be overridden by the user who can define a function with the
// same name without linking conflict
#if !defined(WEAK)
#if (defined(__GNUC__) || defined(__clang__) || \
     defined(__TI_COMPILER_VERSION__)) &&       \
    !defined(_WIN32) && !defined(__CYGWIN__)
#define WEAK __attribute__((weak))
#else
#define WEAK
#endif
#endif

#define mg_file_read brtc_mg_file_read
#define mg_file_size brtc_mg_file_size
#define mg_file_write brtc_mg_file_write
#define mg_file_printf brtc_mg_file_printf
#define mg_random brtc_mg_random
#define mg_globmatch brtc_mg_globmatch
#define mg_next_comma_entry brtc_mg_next_comma_entry
#define mg_ntohs brtc_mg_ntohs
#define mg_ntohl brtc_mg_ntohl
#define mg_crc32 brtc_mg_crc32
#define mg_hexdump brtc_mg_hexdump
#define mg_hex brtc_mg_hex
#define mg_unhex brtc_mg_unhex
#define mg_unhexn brtc_mg_unhexn
#define mg_asprintf brtc_mg_asprintf
#define mg_vasprintf brtc_mg_vasprintf
#define mg_to64 brtc_mg_to64
#define mg_time brtc_mg_time
#define mg_millis brtc_mg_millis
#define mg_usleep brtc_mg_usleep

char *mg_file_read(const char *path, size_t *size);
int64_t mg_file_size(const char *path);
bool mg_file_write(const char *path, const void *buf, size_t len);
bool mg_file_printf(const char *path, const char *fmt, ...);
void mg_random(void *buf, size_t len) WEAK;
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool mg_next_comma_entry(struct mg_str *s, struct mg_str *k, struct mg_str *v);
uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);
char *mg_hexdump(const void *buf, size_t len);
char *mg_hex(const void *buf, size_t len, char *dst);
void mg_unhex(const char *buf, size_t len, unsigned char *to);
unsigned long mg_unhexn(const char *s, size_t len);
int mg_asprintf(char **buf, size_t size, const char *fmt, ...);
int mg_vasprintf(char **buf, size_t size, const char *fmt, va_list ap);
int64_t mg_to64(struct mg_str str);
double mg_time(void);
unsigned long mg_millis(void);
void mg_usleep(unsigned long usecs);

#if MG_ENABLE_FS
#ifdef _WIN32
typedef struct _stati64 mg_stat_t;
#else
typedef struct stat mg_stat_t;
#endif
int mg_stat(const char *path, mg_stat_t *);
FILE *mg_fopen(const char *fp, const char *mode);
#endif

#define mg_htons(x) mg_ntohs(x)
#define mg_htonl(x) mg_ntohl(x)

// Expands to a string representation of its argument: e.g.
// MG_STRINGIFY_LITERAL(5) expands to "5"
#if !defined(_MSC_VER) || _MSC_VER >= 1900
#define MG_STRINGIFY_LITERAL(...) #__VA_ARGS__
#else
#define MG_STRINGIFY_LITERAL(x) #x
#endif
// Expands to a string representation of its argument, which can be a macro:
// #define FOO 123
// MG_STRINGIFY_MACRO(FOO)  // Expands to 123
#define MG_STRINGIFY_MACRO(x) MG_STRINGIFY_LITERAL(x)

// Linked list management macros
#define LIST_ADD_HEAD(type_, head_, elem_) \
  do {                                     \
    (elem_)->next = (*head_);              \
    *(head_) = (elem_);                    \
  } while (0)

#define LIST_ADD_TAIL(type_, head_, elem_) \
  do {                                     \
    type_ **h = head_;                     \
    while (*h != NULL) h = &(*h)->next;    \
    *h = (elem_);                          \
  } while (0)

#define LIST_DELETE(type_, head_, elem_)   \
  do {                                     \
    type_ **h = head_;                     \
    while (*h != (elem_)) h = &(*h)->next; \
    *h = (elem_)->next;                    \
  } while (0)

#define mg_url_port brtc_mg_url_port
#define mg_url_is_ssl brtc_mg_url_is_ssl
#define mg_url_host brtc_mg_url_host
#define mg_url_user brtc_mg_url_user
#define mg_url_pass brtc_mg_url_pass
#define mg_url_uri brtc_mg_url_uri

#define mg_url_port brtc_mg_url_port
#define mg_url_is_ssl brtc_mg_url_is_ssl
#define mg_url_host brtc_mg_url_host
#define mg_url_user brtc_mg_url_user
#define mg_url_pass brtc_mg_url_pass
#define mg_url_uri brtc_mg_url_uri

unsigned short mg_url_port(const char *url);
int mg_url_is_ssl(const char *url);
struct mg_str mg_url_host(const char *url);
struct mg_str mg_url_user(const char *url);
struct mg_str mg_url_pass(const char *url);
const char *mg_url_uri(const char *url);


#include <stddef.h>

#define mg_iobuf_init brtc_mg_iobuf_init
#define mg_iobuf_resize brtc_mg_iobuf_resize
#define mg_iobuf_free brtc_mg_iobuf_free
#define mg_iobuf_append brtc_mg_iobuf_append
#define mg_iobuf_delete brtc_mg_iobuf_delete

#define mg_base64_update brtc_mg_base64_update
#define mg_base64_final brtc_mg_base64_final
#define mg_base64_encode brtc_mg_base64_encode
#define mg_base64_decode brtc_mg_base64_decode

#define mg_md5_init brtc_mg_md5_init
#define mg_md5_update brtc_mg_md5_update
#define mg_md5_final brtc_mg_md5_final

#define mg_sha1_init brtc_mg_sha1_init
#define mg_sha1_update brtc_mg_sha1_update
#define mg_sha1_final brtc_mg_sha1_final
#define mg_hmac_sha1 brtc_mg_hmac_sha1

#define mg_call brtc_mg_call
#define mg_error brtc_mg_error

#define mg_mgr_poll brtc_mg_mgr_poll
#define mg_mgr_init brtc_mg_mgr_init
#define mg_mgr_free brtc_mg_mgr_free

#define mg_listen brtc_mg_listen
#define mg_connect brtc_mg_connect
#define mg_send brtc_mg_send
#define mg_printf brtc_mg_printf
#define mg_vprintf brtc_mg_vprintf
#define mg_straddr brtc_mg_straddr
#define mg_socketpair brtc_mg_socketpair
#define mg_aton brtc_mg_aton
#define mg_ntoa brtc_mg_ntoa
#define mg_http_parse brtc_mg_http_parse
#define mg_http_get_request_len brtc_mg_http_get_request_len
#define mg_http_printf_chunk brtc_mg_http_printf_chunk
#define mg_http_write_chunk brtc_mg_http_write_chunk
#define mg_http_delete_chunk brtc_mg_http_delete_chunk
#define mg_http_listen brtc_mg_http_listen
#define mg_http_connect brtc_mg_http_connect
#define mg_http_serve_dir brtc_mg_http_serve_dir
#define mg_http_serve_file brtc_mg_http_serve_file
#define mg_http_reply brtc_mg_http_reply
#define mg_http_get_header brtc_mg_http_get_header
#define mg_http_event_handler brtc_mg_http_event_handler
#define mg_http_get_var brtc_mg_http_get_var
#define mg_url_decode brtc_mg_url_decode
#define mg_url_encode brtc_mg_url_encode
#define mg_http_creds brtc_mg_http_creds
#define mg_http_match_uri brtc_mg_http_match_uri
#define mg_http_upload brtc_mg_http_upload
#define mg_http_bauth brtc_mg_http_bauth
#define mg_http_get_header_var brtc_mg_http_get_header_var
#define mg_http_next_multipart brtc_mg_http_next_multipart

#define mg_http_serve_ssi brtc_mg_http_serve_ssi
#define mg_tls_init brtc_mg_tls_init
#define mg_tls_free brtc_mg_tls_free
#define mg_tls_send brtc_mg_tls_send
#define mg_tls_recv brtc_mg_tls_recv
#define mg_tls_handshake brtc_mg_tls_handshake
#define mg_ws_connect brtc_mg_ws_connect
#define mg_ws_upgrade brtc_mg_ws_upgrade
#define mg_ws_send brtc_mg_ws_send

#define mg_sntp_connect brtc_mg_sntp_connect
#define mg_sntp_send brtc_mg_sntp_send
#define mg_sntp_parse brtc_mg_sntp_parse
#define mg_mqtt_connect brtc_mg_mqtt_connect
#define mg_mqtt_listen brtc_mg_mqtt_listen
#define mg_mqtt_pub brtc_mg_mqtt_pub
#define mg_mqtt_sub brtc_mg_mqtt_sub
#define mg_mqtt_parse brtc_mg_mqtt_parse
#define mg_mqtt_send_header brtc_mg_mqtt_send_header
#define mg_mqtt_next_sub brtc_mg_mqtt_next_sub
#define mg_mqtt_next_unsub brtc_mg_mqtt_next_unsub
#define mg_mqtt_ping brtc_mg_mqtt_ping
#define mg_mqtt_pong brtc_mg_mqtt_pong
#define mg_mqtt_disconnect brtc_mg_mqtt_disconnect
#define mg_resolve brtc_mg_resolve
#define mg_resolve_cancel brtc_mg_resolve_cancel
#define mg_dns_parse brtc_mg_dns_parse
#define mg_dns_parse_rr brtc_mg_dns_parse_rr
#define mg_dns_decode_name brtc_mg_dns_decode_name

struct mg_iobuf {
  unsigned char *buf;
  size_t size, len;
};

int mg_iobuf_init(struct mg_iobuf *, size_t);
int mg_iobuf_resize(struct mg_iobuf *, size_t);
void mg_iobuf_free(struct mg_iobuf *);
size_t mg_iobuf_append(struct mg_iobuf *, const void *, size_t, size_t);
size_t mg_iobuf_delete(struct mg_iobuf *, size_t);

int mg_base64_update(unsigned char p, char *to, int len);
int mg_base64_final(char *to, int len);
int mg_base64_encode(const unsigned char *p, int n, char *to);
int mg_base64_decode(const char *src, int n, char *dst);




typedef struct {
  uint32_t buf[4];
  uint32_t bits[2];
  unsigned char in[64];
} mg_md5_ctx;

void mg_md5_init(mg_md5_ctx *c);
void mg_md5_update(mg_md5_ctx *c, const unsigned char *data, size_t len);
void mg_md5_final(mg_md5_ctx *c, unsigned char[16]);




typedef struct {
  uint32_t state[5];
  uint32_t count[2];
  unsigned char buffer[64];
} mg_sha1_ctx;

void mg_sha1_init(mg_sha1_ctx *);
void mg_sha1_update(mg_sha1_ctx *, const unsigned char *data, size_t len);
void mg_sha1_final(unsigned char digest[20], mg_sha1_ctx *);
void mg_hmac_sha1(const unsigned char *key, size_t key_len,
                  const unsigned char *text, size_t text_len,
                  unsigned char out[20]);


struct mg_connection;
typedef void (*mg_event_handler_t)(struct mg_connection *, int ev,
                                   void *ev_data, void *fn_data);
void mg_call(struct mg_connection *c, int ev, void *ev_data);
void mg_error(struct mg_connection *c, const char *fmt, ...);

enum {
  MG_EV_ERROR,       // Error                        char *error_message
  MG_EV_POLL,        // mg_mgr_poll iteration        unsigned long *millis
  MG_EV_RESOLVE,     // Host name is resolved        NULL
  MG_EV_CONNECT,     // Connection established       NULL
  MG_EV_ACCEPT,      // Connection accepted          NULL
  MG_EV_READ,        // Data received from socket    struct mg_str *
  MG_EV_WRITE,       // Data written to socket       long *bytes_written
  MG_EV_CLOSE,       // Connection closed            NULL
  MG_EV_HTTP_MSG,    // HTTP request/response        struct mg_http_message *
  MG_EV_HTTP_CHUNK,  // HTTP chunk (partial msg)     struct mg_http_message *
  MG_EV_WS_OPEN,     // Websocket handshake done     struct mg_http_message *
  MG_EV_WS_MSG,      // Websocket msg, text or bin   struct mg_ws_message *
  MG_EV_WS_CTL,      // Websocket control msg        struct mg_ws_message *
  MG_EV_MQTT_CMD,    // MQTT low-level command       struct mg_mqtt_message *
  MG_EV_MQTT_MSG,    // MQTT PUBLISH received        struct mg_mqtt_message *
  MG_EV_MQTT_OPEN,   // MQTT CONNACK received        int *connack_status_code
  MG_EV_SNTP_TIME,   // SNTP time received           struct timeval *
  MG_EV_USER,        // Starting ID for user events
};







struct mg_dns {
  const char *url;          // DNS server URL
  struct mg_connection *c;  // DNS server connection
};

struct mg_addr {
  uint16_t port;    // TCP or UDP port in network byte order
  uint32_t ip;      // IP address in network byte order
  uint8_t ip6[16];  // IPv6 address
  bool is_ip6;      // True when address is IPv6 address
};

struct mg_mgr {
  struct mg_connection *conns;  // List of active connections
  struct mg_dns dns4;           // DNS for IPv4
  struct mg_dns dns4_2;         // DNS for IPv4
  struct mg_dns dns4_3;         // DNS for IPv4
  struct mg_dns dns6;           // DNS for IPv6
  int dnstimeout;               // DNS resolve timeout in milliseconds
  unsigned long nextid;         // Next connection ID
  void *userdata;               // Arbitrary user data pointer
#if MG_ARCH == MG_ARCH_FREERTOS_TCP
  SocketSet_t ss;  // NOTE(lsm): referenced from socket struct
#endif
};

struct mg_connection {
  struct mg_connection *next;  // Linkage in struct mg_mgr :: connections
  struct mg_mgr *mgr;          // Our container
  struct mg_addr peer;         // Remote peer address
  void *fd;                    // Connected socket, or LWIP data
  unsigned long id;            // Auto-incrementing unique connection ID
  struct mg_iobuf recv;        // Incoming data
  struct mg_iobuf send;        // Outgoing data
  mg_event_handler_t fn;       // User-specified event handler function
  void *fn_data;               // User-specified function parameter
  mg_event_handler_t pfn;      // Protocol-specific handler function
  void *pfn_data;              // Protocol-specific function parameter
  char label[50];              // Arbitrary label
  void *tls;                   // TLS specific data
  unsigned is_listening : 1;   // Listening connection
  unsigned is_client : 1;      // Outbound (client) connection
  unsigned is_accepted : 1;    // Accepted (server) connection
  unsigned is_resolving : 1;   // Non-blocking DNS resolution is in progress
  unsigned is_connecting : 1;  // Non-blocking connect is in progress
  unsigned is_tls : 1;         // TLS-enabled connection
  unsigned is_tls_hs : 1;      // TLS handshake is in progress
  unsigned is_udp : 1;         // UDP connection
  unsigned is_websocket : 1;   // WebSocket connection
  unsigned is_hexdumping : 1;  // Hexdump in/out traffic
  unsigned is_draining : 1;    // Send remaining data, then close and free
  unsigned is_closing : 1;     // Close and free the connection immediately
  unsigned is_readable : 1;    // Connection is ready to read
  unsigned is_writable : 1;    // Connection is ready to write
  unsigned is_dns_timeout: 1;  // Indicate dns req is timeout
};

void mg_mgr_poll(struct mg_mgr *, int ms);
void mg_mgr_init(struct mg_mgr *);
void mg_mgr_free(struct mg_mgr *);

struct mg_connection *mg_listen(struct mg_mgr *, const char *url,
                                mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_connect(struct mg_mgr *, const char *url,
                                 mg_event_handler_t fn, void *fn_data);
bool mg_send(struct mg_connection *, const void *, size_t);
int mg_printf(struct mg_connection *, const char *fmt, ...);
int mg_vprintf(struct mg_connection *, const char *fmt, va_list ap);
char *mg_straddr(struct mg_connection *, char *, size_t);
bool mg_socketpair(int *s1, int *s2);
bool mg_aton(struct mg_str str, struct mg_addr *addr);
char *mg_ntoa(const struct mg_addr *addr, char *buf, size_t len);






struct mg_http_header {
  struct mg_str name;
  struct mg_str value;
};

struct mg_http_message {
  //        GET /foo/bar/baz?aa=b&cc=ddd HTTP/1.1
  // method |-| |----uri---| |--query--| |proto-|

  struct mg_str method, uri, query, proto;             // Request/response line
  struct mg_http_header headers[MG_MAX_HTTP_HEADERS];  // Headers
  struct mg_str body;                                  // Body
  struct mg_str head;                                  // Request + headers
  struct mg_str chunk;    // Chunk for chunked encoding,  or partial body
  struct mg_str message;  // Request + headers + body
};

// Parameter for mg_http_serve_dir()
struct mg_http_serve_opts {
  const char *root_dir;       // Web root directory, must be non-NULL
  const char *ssi_pattern;    // SSI file name pattern, e.g. #.shtml
  const char *extra_headers;  // Extra HTTP headers to add in responses
};

// Parameter for mg_http_next_multipart
struct mg_http_part {
  struct mg_str name;      // Form field name
  struct mg_str filename;  // Filename for file uploads
  struct mg_str body;      // Part contents
};

int mg_http_parse(const char *s, size_t len, struct mg_http_message *);
int mg_http_get_request_len(const unsigned char *buf, size_t buf_len);
void mg_http_printf_chunk(struct mg_connection *cnn, const char *fmt, ...);
void mg_http_write_chunk(struct mg_connection *c, const char *buf, size_t len);
void mg_http_delete_chunk(struct mg_connection *c, struct mg_http_message *hm);
struct mg_connection *mg_http_listen(struct mg_mgr *, const char *url,
                                     mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_http_connect(struct mg_mgr *, const char *url,
                                      mg_event_handler_t fn, void *fn_data);
void mg_http_serve_dir(struct mg_connection *, struct mg_http_message *hm,
                       struct mg_http_serve_opts *);
void mg_http_serve_file(struct mg_connection *, struct mg_http_message *,
                        const char *, const char *mime, const char *headers);
void mg_http_reply(struct mg_connection *, int status_code, const char *headers,
                   const char *body_fmt, ...);
struct mg_str *mg_http_get_header(struct mg_http_message *, const char *name);
void mg_http_event_handler(struct mg_connection *c, int ev);
int mg_http_get_var(const struct mg_str *, const char *name, char *, size_t);
int mg_url_decode(const char *s, size_t n, char *to, size_t to_len, int form);
size_t mg_url_encode(const char *s, size_t n, char *buf, size_t len);
void mg_http_creds(struct mg_http_message *, char *, size_t, char *, size_t);
bool mg_http_match_uri(const struct mg_http_message *, const char *glob);
int mg_http_upload(struct mg_connection *, struct mg_http_message *hm,
                   const char *dir);
void mg_http_bauth(struct mg_connection *, const char *user, const char *pass);
struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v);
size_t mg_http_next_multipart(struct mg_str, size_t, struct mg_http_part *);


void mg_http_serve_ssi(struct mg_connection *c, const char *root,
                       const char *fullpath);



struct mg_tls_opts {
  const char *ca;         // CA certificate file. For both listeners and clients
  const char *cert;       // Certificate
  const char *certkey;    // Certificate key
  const char *ciphers;    // Cipher list
  struct mg_str srvname;  // If not empty, enables server name verification
};

void mg_tls_init(struct mg_connection *, struct mg_tls_opts *);
void mg_tls_free(struct mg_connection *);
long mg_tls_send(struct mg_connection *, const void *buf, size_t len);
long mg_tls_recv(struct mg_connection *, void *buf, size_t len);
void mg_tls_handshake(struct mg_connection *);


#define WEBSOCKET_OP_CONTINUE 0
#define WEBSOCKET_OP_TEXT 1
#define WEBSOCKET_OP_BINARY 2
#define WEBSOCKET_OP_CLOSE 8
#define WEBSOCKET_OP_PING 9
#define WEBSOCKET_OP_PONG 10

#define WEBSOCKET_FLAGS_MASK_FIN 128
#define WEBSOCKET_FLAGS_MASK_OP 15



struct mg_ws_message {
  struct mg_str data;
  uint8_t flags;  // Websocket message flags
};

struct mg_connection *mg_ws_connect(struct mg_mgr *, const char *url,
                                    mg_event_handler_t fn, void *fn_data,
                                    const char *fmt, ...);
void mg_ws_upgrade(struct mg_connection *, struct mg_http_message *,
                   const char *fmt, ...);
size_t mg_ws_send(struct mg_connection *, const char *buf, size_t len, int op);




struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url,
                                      mg_event_handler_t fn, void *fn_data);
void mg_sntp_send(struct mg_connection *c, unsigned long utc);
int mg_sntp_parse(const unsigned char *buf, size_t len, struct timeval *tv);





#define MQTT_CMD_CONNECT 1
#define MQTT_CMD_CONNACK 2
#define MQTT_CMD_PUBLISH 3
#define MQTT_CMD_PUBACK 4
#define MQTT_CMD_PUBREC 5
#define MQTT_CMD_PUBREL 6
#define MQTT_CMD_PUBCOMP 7
#define MQTT_CMD_SUBSCRIBE 8
#define MQTT_CMD_SUBACK 9
#define MQTT_CMD_UNSUBSCRIBE 10
#define MQTT_CMD_UNSUBACK 11
#define MQTT_CMD_PINGREQ 12
#define MQTT_CMD_PINGRESP 13
#define MQTT_CMD_DISCONNECT 14

#define MQTT_QOS(qos) ((qos) << 1)
#define MQTT_GET_QOS(flags) (((flags) &0x6) >> 1)
#define MQTT_SET_QOS(flags, qos) (flags) = ((flags) & ~0x6) | ((qos) << 1)

struct mg_mqtt_opts {
  struct mg_str client_id;
  struct mg_str will_topic;
  struct mg_str will_message;
  uint8_t qos;         // Quality of service
  bool will_retain;    // Retain last will
  bool clean;          // Use clean session, 0 or 1
  uint16_t keepalive;  // Keep-alive timer in seconds
};

struct mg_mqtt_message {
  struct mg_str topic;  // Parsed topic
  struct mg_str data;   // Parsed message
  struct mg_str dgram;  // Whole MQTT datagram, including headers
  uint16_t id;  // Set for PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, PUBLISH
  uint8_t cmd;  // MQTT command, one of MQTT_CMD_*
  uint8_t qos;  // Quality of service
  uint8_t ack;  // Connack return code. 0 - success
};

struct mg_connection *mg_mqtt_connect(struct mg_mgr *, const char *url,
                                      struct mg_mqtt_opts *opts,
                                      mg_event_handler_t fn, void *fn_data);
struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url,
                                     mg_event_handler_t fn, void *fn_data);
void mg_mqtt_pub(struct mg_connection *c, struct mg_str *topic,
                 struct mg_str *data, int qos, bool retain);
void mg_mqtt_sub(struct mg_connection *, struct mg_str *topic, int qos);
int mg_mqtt_parse(const uint8_t *buf, size_t len, struct mg_mqtt_message *m);
void mg_mqtt_send_header(struct mg_connection *, uint8_t cmd, uint8_t flags,
                         uint32_t len);
size_t mg_mqtt_next_sub(struct mg_mqtt_message *msg, struct mg_str *topic,
                        uint8_t *qos, size_t pos);
size_t mg_mqtt_next_unsub(struct mg_mqtt_message *msg, struct mg_str *topic,
                          size_t pos);
void mg_mqtt_ping(struct mg_connection *);
void mg_mqtt_pong(struct mg_connection *);
void mg_mqtt_disconnect(struct mg_connection *);





// Mongoose sends DNS queries that contain only one question:
// either A (IPv4) or AAAA (IPv6) address lookup.
// Therefore, we expect zero or one answer.
// If `resolved` is true, then `addr` contains resolved IPv4 or IPV6 address.
struct mg_dns_message {
  uint16_t txnid;       // Transaction ID
  bool resolved;        // Resolve successful, addr is set
  struct mg_addr addr;  // Resolved address
  char name[256];       // Host name
};

struct mg_dns_header {
  uint16_t txnid;  // Transaction ID
  uint16_t flags;
  uint16_t num_questions;
  uint16_t num_answers;
  uint16_t num_authority_prs;
  uint16_t num_other_prs;
};

// DNS resource record
struct mg_dns_rr {
  uint16_t nlen;    // Name or pointer length
  uint16_t atype;   // Address type
  uint16_t aclass;  // Address class
  uint16_t alen;    // Address length
};

void mg_resolve(struct mg_connection *, struct mg_str *, int);
void mg_resolve_cancel(struct mg_connection *);
bool mg_dns_parse(const uint8_t *buf, size_t len, struct mg_dns_message *);
size_t mg_dns_parse_rr(const uint8_t *buf, size_t len, size_t ofs,
                       bool is_question, struct mg_dns_rr *);
size_t mg_dns_decode_name(const uint8_t *, size_t, size_t, char *, size_t);

#ifdef __cplusplus
}
#endif
