Skip to content
Snippets Groups Projects
mongoose.c 114 KiB
Newer Older
Sergey Lyubka's avatar
Sergey Lyubka committed
static void lsp(struct connection *conn, const char *p, int len, lua_State *L) {
  int i, j, pos = 0;

  for (i = 0; i < len; i++) {
    if (p[i] == '<' && p[i + 1] == '?') {
      for (j = i + 1; j < len ; j++) {
        if (p[j] == '?' && p[j + 1] == '>') {
          mg_write(&conn->mg_conn, p + pos, i - pos);
          if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), "") == LUA_OK) {
            lua_pcall(L, 0, LUA_MULTRET, 0);
          }
          pos = j + 2;
          i = pos - 1;
          break;
        }
      }
    }
  }
  if (i > pos) mg_write(&conn->mg_conn, p + pos, i - pos);
}
Sergey Lyubka's avatar
Sergey Lyubka committed
static void handle_lsp_request(struct connection *conn, const char *path,
                               file_stat_t *st) {
  void *p = NULL;
  lua_State *L = NULL;
  FILE *fp = NULL;

  if ((fp = fopen(path, "r")) == NULL ||
      (p = mmap(NULL, st->st_size, PROT_READ, MAP_PRIVATE,
                fileno(fp), 0)) == MAP_FAILED ||
      (L = luaL_newstate()) == NULL) {
    send_http_error(conn, 500);
  } else {
    // We're not sending HTTP headers here, Lua page must do it.
    prepare_lua_environment(&conn->mg_conn, L);
    lua_pushcclosure(L, &lua_error_handler, 0);
    lua_pushglobaltable(L);
Sergey Lyubka's avatar
Sergey Lyubka committed
    lsp(conn, p, st->st_size, L);
    close_local_endpoint(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed

  if (L != NULL) lua_close(L);
  if (p != NULL) munmap(p, st->st_size);
  if (fp != NULL) fclose(fp);
}
#endif // USE_LUA

static void open_local_endpoint(struct connection *conn) {
  static const char lua_pat[] = LUA_SCRIPT_PATTERN;
  char path[MAX_PATH_SIZE] = {'\0'};
  file_stat_t st;
  int exists = 0, is_directory = 0;
  const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
  const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
  const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
  conn->mg_conn.content_len = cl_hdr == NULL ? 0 : (int) to64(cl_hdr);
  // Call URI handler if one is registered for this URI
  conn->endpoint.uh = find_uri_handler(conn->server, conn->mg_conn.uri);
  if (conn->endpoint.uh != NULL) {
    conn->endpoint_type = EP_USER;
    conn->mg_conn.content = conn->local_iobuf.buf;
#if USE_POST_SIZE_LIMIT > 1
    {
      const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
      if (!strcmp(conn->mg_conn.request_method, "POST") &&
          (cl == NULL || to64(cl) > USE_POST_SIZE_LIMIT)) {
        send_http_error(conn, 500);
      }
    }
#endif
  exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
  is_directory = S_ISDIR(st.st_mode);
  if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
    send_options(conn);
  } else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
    send_http_error(conn, 404);
#ifndef NO_AUTH
  } else if ((!is_dangerous_dav_request(conn) && !is_authorized(conn, path)) ||
             (is_dangerous_dav_request(conn) && !is_authorized_for_dav(conn))) {
    send_authorization_request(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
#endif
#ifndef NO_DAV
  } else if (!strcmp(conn->mg_conn.request_method, "PROPFIND")) {
    handle_propfind(conn, path, &st);
  } else if (!strcmp(conn->mg_conn.request_method, "MKCOL")) {
    handle_mkcol(conn, path);
  } else if (!strcmp(conn->mg_conn.request_method, "DELETE")) {
    handle_delete(conn, path);
  } else if (!strcmp(conn->mg_conn.request_method, "PUT")) {
    handle_put(conn, path);
Sergey Lyubka's avatar
Sergey Lyubka committed
#endif
  } else if (!exists || must_hide_file(conn, path)) {
    send_http_error(conn, 404);
  } else if (is_directory &&
             conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
Sergey Lyubka's avatar
Sergey Lyubka committed
    conn->mg_conn.status_code = 301;
    mg_printf(&conn->mg_conn, "HTTP/1.1 301 Moved Permanently\r\n"
              "Location: %s/\r\n\r\n", conn->mg_conn.uri);
    close_local_endpoint(conn);
  } else if (is_directory && !find_index_file(conn, path, sizeof(path), &st)) {
    if (!mg_strcasecmp(dir_lst, "yes")) {
#ifndef NO_DIRECTORY_LISTING
      send_directory_listing(conn, path);
#else
      send_http_error(conn, 501);
#endif
    } else {
      send_http_error(conn, 403);
    }
  } else if (match_prefix(lua_pat, sizeof(lua_pat) - 1, path) > 0) {
#ifdef USE_LUA
Sergey Lyubka's avatar
Sergey Lyubka committed
    handle_lsp_request(conn, path, &st);
#else
    send_http_error(conn, 501);
#endif
  } else if (match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
    if (strcmp(conn->mg_conn.request_method, "POST") &&
        strcmp(conn->mg_conn.request_method, "HEAD") &&
        strcmp(conn->mg_conn.request_method, "GET")) {
      send_http_error(conn, 501);
    } else {
#if !defined(NO_CGI)
      open_cgi_endpoint(conn, path);
#else
      send_http_error(conn, 501);
#endif // !NO_CGI
  } else if (is_not_modified(conn, &st)) {
    send_http_error(conn, 304);
  } else if ((conn->endpoint.fd = open(path, O_RDONLY | O_BINARY)) != -1) {
    // O_BINARY is required for Windows, otherwise in default text mode
    // two bytes \r\n will be read as one.
    open_file_endpoint(conn, path, &st);
  } else {
    send_http_error(conn, 404);
static void send_continue_if_expected(struct connection *conn) {
  static const char expect_response[] = "HTTP/1.1 100 Continue\r\n\r\n";
  const char *expect_hdr = mg_get_header(&conn->mg_conn, "Expect");
  if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) {
    spool(&conn->remote_iobuf, expect_response, sizeof(expect_response) - 1);
static void forward_post_data(struct connection *conn) {
  struct iobuf *io = &conn->local_iobuf;
  int n = send(conn->endpoint.cgi_sock, io->buf, io->len, 0);
  if (n > 0) {
    memmove(io->buf, io->buf + n, io->len - n);
    io->len -= n;
static int is_valid_uri(const char *uri) {
  // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
  // URI can be an asterisk (*) or should start with slash.
  return uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0');
}
static void process_request(struct connection *conn) {
  struct iobuf *io = &conn->local_iobuf;

  if (conn->request_len == 0 &&
      (conn->request_len = get_request_len(io->buf, io->len)) > 0) {
    // If request is buffered in, remove it from the iobuf. This is because
    // iobuf could be reallocated, and pointers in parsed request could
    // become invalid.
    conn->request = (char *) malloc(conn->request_len);
    memcpy(conn->request, io->buf, conn->request_len);
    DBG(("==> [%.*s]", conn->request_len, conn->request));
    memmove(io->buf, io->buf + conn->request_len, io->len - conn->request_len);
    io->len -= conn->request_len;
    conn->request_len = parse_http_message(conn->request, conn->request_len,
                                           &conn->mg_conn);
  }

  DBG(("%d %d", conn->request_len, io->len));
  if (conn->request_len < 0 ||
      (conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
    send_http_error(conn, 400);
  } else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) {
    send_http_error(conn, 413);
  } else if (conn->request_len > 0 &&
             strcmp(conn->mg_conn.http_version, "1.0") != 0 &&
             strcmp(conn->mg_conn.http_version, "1.1") != 0) {
    send_http_error(conn, 505);
  } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
#ifndef NO_WEBSOCKET
    send_websocket_handshake_if_requested(&conn->mg_conn);
#endif
    send_continue_if_expected(conn);
    open_local_endpoint(conn);
#ifndef NO_CGI
  if (conn->endpoint_type == EP_CGI && io->len > 0) {
    forward_post_data(conn);
  }
#endif
  if (conn->endpoint_type == EP_USER) {
    call_uri_handler_if_data_is_buffered(conn);
#ifndef NO_DAV
  if (conn->endpoint_type == EP_PUT && io->len > 0) {
    forward_put_data(conn);
static void read_from_client(struct connection *conn) {
  char buf[IOBUF_SIZE];
Sergey Lyubka's avatar
Sergey Lyubka committed
  int n = 0;
  if (conn->ssl != NULL) {
#ifdef USE_SSL
    if (conn->flags & CONN_SSL_HANDS_SHAKEN) {
      n = SSL_read(conn->ssl, buf, sizeof(buf));
    } else {
      if (SSL_accept(conn->ssl) == 1) {
        conn->flags |= CONN_SSL_HANDS_SHAKEN;
      }
      return;
    }
#endif
  } else {
    n = recv(conn->client_sock, buf, sizeof(buf), 0);
  }
  if (is_error(n)) {
    conn->flags |= CONN_CLOSE;
  } else if (n > 0) {
    spool(&conn->local_iobuf, buf, n);
    process_request(conn);
static void read_from_cgi(struct connection *conn) {
  char buf[IOBUF_SIZE];
  int len, n = recv(conn->endpoint.cgi_sock, buf, sizeof(buf), 0);
  DBG(("-> %d", n));
  if (is_error(n)) {
    close_local_endpoint(conn);
  } else if (n > 0) {
    if (conn->num_bytes_sent == 0 && conn->remote_iobuf.len == 0) {
      // Parse CGI headers, and modify the reply line if needed
      if ((len = get_request_len(buf, n)) > 0) {
        const char *status = NULL;
        char *s = buf, buf2[sizeof(buf)];
        struct mg_connection c;
        int i, k;

        memset(&c, 0, sizeof(c));
        buf[len - 1] = '\0';
        parse_http_headers(&s, &c);
        if (mg_get_header(&c, "Location") != NULL) {
          status = "302 Moved";
        } else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) {
          status = "200 OK";
        }
        k = mg_snprintf(buf2, sizeof(buf2), "HTTP/1.1 %s\r\n", status);
        spool(&conn->remote_iobuf, buf2, k);
        for (i = 0; i < c.num_headers; i++) {
          k = mg_snprintf(buf2, sizeof(buf2), "%s: %s\r\n",
                          c.http_headers[i].name, c.http_headers[i].value);
          spool(&conn->remote_iobuf, buf2, k);
        }
        spool(&conn->remote_iobuf, "\r\n", 2);
        memmove(buf, buf + len, n - len);
        n -= len;
      } else {
        static const char ok_200[] = "HTTP/1.1 200 OK\r\n";
        spool(&conn->remote_iobuf, ok_200, sizeof(ok_200) - 1);
      }
    }
    spool(&conn->remote_iobuf, buf, n);
#ifndef NO_LOGGING
static void log_header(const struct mg_connection *conn, const char *header,
                       FILE *fp) {
  const char *header_value;
  if ((header_value = mg_get_header(conn, header)) == NULL) {
    (void) fprintf(fp, "%s", " -");
  } else {
    (void) fprintf(fp, " \"%s\"", header_value);
static void log_access(const struct connection *conn, const char *path) {
  const struct mg_connection *c = &conn->mg_conn;
  FILE *fp = (path == NULL) ?  NULL : fopen(path, "a+");
Sergey Lyubka's avatar
Sergey Lyubka committed
  char date[64], user[100];
  if (fp == NULL) return;
  strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
           localtime(&conn->birth_time));
  flockfile(fp);
  mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
                  user, sizeof(user));
  fprintf(fp, "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
Sergey Lyubka's avatar
Sergey Lyubka committed
          c->remote_ip, user[0] == '\0' ? "-" : user, date,
          c->request_method ? c->request_method : "-",
          c->uri ? c->uri : "-", c->http_version,
          c->status_code, conn->num_bytes_sent);
  log_header(c, "Referer", fp);
  log_header(c, "User-Agent", fp);
  fputc('\n', fp);
  fflush(fp);
  funlockfile(fp);
  fclose(fp);
static void close_local_endpoint(struct connection *conn) {
  // Must be done before free()
  int keep_alive = should_keep_alive(&conn->mg_conn) &&
    (conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
  switch (conn->endpoint_type) {
    case EP_FILE: close(conn->endpoint.fd); break;
    case EP_CGI: closesocket(conn->endpoint.cgi_sock); break;
    default: break;
  }
#ifndef NO_LOGGING
  if (conn->mg_conn.status_code != 400) {
    log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
  }
#endif
  conn->endpoint_type = EP_NONE;
  conn->cl = conn->num_bytes_sent = conn->request_len = 0;
  free(conn->request);
  conn->request = NULL;
  if (keep_alive) {
    process_request(conn);  // Can call us recursively if pipelining is used
  } else {
    conn->flags |= conn->remote_iobuf.len == 0 ? CONN_CLOSE : CONN_SPOOL_DONE;
static void transfer_file_data(struct connection *conn) {
  char buf[IOBUF_SIZE];
Sergey Lyubka's avatar
Sergey Lyubka committed
  int n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
               (int) conn->cl : (int) sizeof(buf));
  if (is_error(n)) {
    close_local_endpoint(conn);
  } else if (n > 0) {
    conn->cl -= n;
    spool(&conn->remote_iobuf, buf, n);
    if (conn->cl <= 0) {
      close_local_endpoint(conn);
static void execute_iteration(struct mg_server *server) {
  struct ll *lp, *tmp;
  struct connection *conn;
  union { void (*f)(struct mg_connection *, void *); void *p; } msg[2];

  recv(server->ctl[1], (void *) msg, sizeof(msg), 0);
  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
    msg[0].f(&conn->mg_conn, msg[1].p);
void add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
  FD_SET(sock, set);
  if (sock > *max_fd) {
    *max_fd = sock;
unsigned int mg_poll_server(struct mg_server *server, int milliseconds) {
  struct ll *lp, *tmp;
  struct connection *conn;
  struct timeval tv;
  fd_set read_set, write_set;
  sock_t max_fd = -1;
  time_t current_time = time(NULL), expire_time = current_time -
    atoi(server->config_options[IDLE_TIMEOUT_MS]) / 1000;
  if (server->listening_sock == INVALID_SOCKET) return 0;
  FD_ZERO(&read_set);
  FD_ZERO(&write_set);
  add_to_set(server->listening_sock, &read_set, &max_fd);
  add_to_set(server->ctl[1], &read_set, &max_fd);

  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
    add_to_set(conn->client_sock, &read_set, &max_fd);
    if (conn->endpoint_type == EP_FILE) {
      transfer_file_data(conn);
    } else if (conn->endpoint_type == EP_CGI) {
      add_to_set(conn->endpoint.cgi_sock, &read_set, &max_fd);
    }
    if (conn->remote_iobuf.len > 0) {
      add_to_set(conn->client_sock, &write_set, &max_fd);
    } else if (conn->flags & CONN_CLOSE) {
      close_conn(conn);
  tv.tv_sec = milliseconds / 1000;
  tv.tv_usec = (milliseconds % 1000) * 1000;
  if (select(max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
    if (FD_ISSET(server->ctl[1], &read_set)) {
      execute_iteration(server);
    // Accept new connections
    if (FD_ISSET(server->listening_sock, &read_set)) {
      while ((conn = accept_new_connection(server)) != NULL) {
        conn->birth_time = conn->last_activity_time = current_time;
      }
    }

    // Read/write from clients
    LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
      conn = LINKED_LIST_ENTRY(lp, struct connection, link);
      if (FD_ISSET(conn->client_sock, &read_set)) {
        conn->last_activity_time = current_time;
        read_from_client(conn);
      }
      if (conn->endpoint_type == EP_CGI &&
          FD_ISSET(conn->endpoint.cgi_sock, &read_set)) {
        read_from_cgi(conn);
      if (FD_ISSET(conn->client_sock, &write_set)) {
        conn->last_activity_time = current_time;
        write_to_client(conn);
  // Close expired connections and those that need to be closed
  LINKED_LIST_FOREACH(&server->active_connections, lp, tmp) {
    conn = LINKED_LIST_ENTRY(lp, struct connection, link);
    ping_idle_websocket_connection(conn, current_time);
    if (conn->flags & CONN_CLOSE || conn->last_activity_time < expire_time) {
      close_conn(conn);
    }
void mg_destroy_server(struct mg_server **server) {
  struct ll *lp, *tmp;

  if (server != NULL && *server != NULL) {
    closesocket((*server)->listening_sock);
    closesocket((*server)->ctl[0]);
    closesocket((*server)->ctl[1]);
    LINKED_LIST_FOREACH(&(*server)->active_connections, lp, tmp) {
      free(LINKED_LIST_ENTRY(lp, struct connection, link));
    LINKED_LIST_FOREACH(&(*server)->uri_handlers, lp, tmp) {
      free(LINKED_LIST_ENTRY(lp, struct uri_handler, link));
    }
Sergey Lyubka's avatar
Sergey Lyubka committed
#ifdef USE_SSL
    if ((*server)->ssl_ctx != NULL) SSL_CTX_free((*server)->ssl_ctx);
#endif
    free(*server);
    *server = NULL;
// Apply function to all active connections.
void mg_iterate_over_connections(struct mg_server *server,
                                 void (*func)(struct mg_connection *, void *),
                                 void *param) {
  // Send closure (function + parameter) to the IO thread to execute
  union { void (*f)(struct mg_connection *, void *); void *p; } msg[2];
  msg[0].f = func;
  msg[1].p = param;
  send(server->ctl[0], (void *) msg, sizeof(msg), 0);
void mg_add_uri_handler(struct mg_server *server, const char *uri,
                        mg_handler_t handler) {
  struct uri_handler *p = (struct uri_handler *) malloc(sizeof(*p));
  if (p != NULL) {
    LINKED_LIST_ADD_TO_FRONT(&server->uri_handlers, &p->link);
    p->uri = mg_strdup(uri);
    p->handler = handler;
static int get_var(const char *data, size_t data_len, const char *name,
                   char *dst, size_t dst_len) {
  const char *p, *e, *s;
  size_t name_len;
  int len;
  if (dst == NULL || dst_len == 0) {
    len = -2;
  } else if (data == NULL || name == NULL || data_len == 0) {
    len = -1;
    dst[0] = '\0';
  } else {
    name_len = strlen(name);
    e = data + data_len;
    len = -1;
    dst[0] = '\0';
    // data is "var1=val1&var2=val2...". Find variable first
    for (p = data; p + name_len < e; p++) {
      if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
          !mg_strncasecmp(name, p, name_len)) {
        // Point p to variable value
        p += name_len + 1;
        // Point s to the end of the value
        s = (const char *) memchr(p, '&', (size_t)(e - p));
        if (s == NULL) {
          s = e;
        }
        assert(s >= p);
        // Decode variable into destination buffer
        len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
        // Redirect error code from -1 to -2 (destination buffer too small).
        if (len == -1) {
          len = -2;
        }
        break;
      }
    }
  }
int mg_get_var(const struct mg_connection *conn, const char *name,
               char *dst, size_t dst_len) {
  int len = get_var(conn->query_string, conn->query_string == NULL ? 0 :
                    strlen(conn->query_string), name, dst, dst_len);
  if (len < 0) {
    len = get_var(conn->content, conn->content_len, name, dst, dst_len);
const char **mg_get_valid_option_names(void) {
  return static_config_options;
static int get_option_index(const char *name) {
  int i;

  for (i = 0; static_config_options[i * 2] != NULL; i++) {
    if (strcmp(static_config_options[i * 2], name) == 0) {
      return i;
  }
  return -1;
}

static void set_default_option_values(char **opts) {
  const char *value, **all_opts = mg_get_valid_option_names();
  int i;
  for (i = 0; all_opts[i * 2] != NULL; i++) {
    value = all_opts[i * 2 + 1];
    if (opts[i] == NULL && value != NULL) {
      opts[i] = mg_strdup(value);
// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
static int parse_port_string(const char *str, union socket_address *sa) {
  unsigned int a, b, c, d, port;
  int len = 0;
#ifdef USE_IPV6
  char buf[100];
#endif
  // MacOS needs that. If we do not zero it, subsequent bind() will fail.
  // Also, all-zeroes in the socket address means binding to all addresses
  // for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT).
  memset(sa, 0, sizeof(*sa));
  sa->sin.sin_family = AF_INET;
  if (sscanf(str, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) == 5) {
    // Bind to a specific IPv4 address, e.g. 192.168.1.5:8080
    sa->sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
    sa->sin.sin_port = htons((uint16_t) port);
#if defined(USE_IPV6)
  } else if (sscanf(str, "[%49[^]]]:%d%n", buf, &port, &len) == 2 &&
             inet_pton(AF_INET6, buf, &sa->sin6.sin6_addr)) {
    // IPv6 address, e.g. [3ffe:2a00:100:7031::1]:8080
    sa->sin6.sin6_family = AF_INET6;
    sa->sin6.sin6_port = htons((uint16_t) port);
#endif
  } else if (sscanf(str, "%u%n", &port, &len) == 1) {
    // If only port is specified, bind to IPv4, INADDR_ANY
    sa->sin.sin_port = htons((uint16_t) port);
  } else {
    port = 0;   // Parsing failure. Make port invalid.

  return port > 0 && port < 0xffff && str[len] == '\0';
const char *mg_set_option(struct mg_server *server, const char *name,
                          const char *value) {
  int ind = get_option_index(name);
  const char *error_msg = NULL;

  if (ind < 0) {
    error_msg = "No such option";
  } else {
    if (server->config_options[ind] != NULL) {
      free(server->config_options[ind]);
    }
    server->config_options[ind] = mg_strdup(value);
    DBG(("%s => %s", name, value));

    if (ind == LISTENING_PORT) {
      if (server->listening_sock != INVALID_SOCKET) {
        closesocket(server->listening_sock);
      }
      parse_port_string(server->config_options[LISTENING_PORT], &server->lsa);
      server->listening_sock = open_listening_socket(&server->lsa);
      if (server->listening_sock == INVALID_SOCKET) {
        error_msg = "Cannot bind to port";
      } else {
        set_non_blocking_mode(server->listening_sock);
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
#ifndef _WIN32
    } else if (ind == RUN_AS_USER) {
Sergey Lyubka's avatar
Sergey Lyubka committed
      struct passwd *pw;
      if ((pw = getpwnam(value)) == NULL) {
        error_msg = "Unknown user";
      } else if (setgid(pw->pw_gid) != 0) {
        error_msg = "setgid() failed";
      } else if (setuid(pw->pw_uid) != 0) {
        error_msg = "setuid() failed";
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
#endif
#ifdef USE_SSL
    } else if (ind == SSL_CERTIFICATE) {
Sergey Lyubka's avatar
Sergey Lyubka committed
      SSL_library_init();
      if ((server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
        error_msg = "SSL_CTX_new() failed";
      } else if (SSL_CTX_use_certificate_file(server->ssl_ctx, value, 1) == 0 ||
                 SSL_CTX_use_PrivateKey_file(server->ssl_ctx, value, 1) == 0) {
        error_msg = "Cannot load PEM file";
      } else {
        SSL_CTX_use_certificate_chain_file(server->ssl_ctx, value);
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
#endif
void mg_set_listening_socket(struct mg_server *server, int sock) {
  if (server->listening_sock != INVALID_SOCKET) {
    closesocket(server->listening_sock);
  }
  server->listening_sock = sock;
}

int mg_get_listening_socket(struct mg_server *server) {
  return server->listening_sock;
}

const char *mg_get_option(const struct mg_server *server, const char *name) {
  const char **opts = (const char **) server->config_options;
  int i = get_option_index(name);
  return i == -1 ? NULL : opts[i] == NULL ? "" : opts[i];
}

struct mg_server *mg_create_server(void *server_data) {
  struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));

#ifdef _WIN32
  WSADATA data;
  WSAStartup(MAKEWORD(2, 2), &data);
#else
  // Ignore SIGPIPE signal, so if browser cancels the request, it
  // won't kill the whole process.
  signal(SIGPIPE, SIG_IGN);
#endif

  LINKED_LIST_INIT(&server->active_connections);
  LINKED_LIST_INIT(&server->uri_handlers);
Sergey Lyubka's avatar
Sergey Lyubka committed

  // Create control socket pair. Do it in a loop to protect from
  // interrupted syscalls in mg_socketpair().
  do {
    mg_socketpair(server->ctl);
  } while (server->ctl[0] == INVALID_SOCKET);
  server->server_data = server_data;
  server->listening_sock = INVALID_SOCKET;
  set_default_option_values(server->config_options);
  return server;