mongoose.c 146 KB
Newer Older
1
// Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
2
// Copyright (c) 2013-2014 Cesanta Software Limited
3 4 5 6 7 8 9 10 11 12 13 14 15
// All rights reserved
//
// This library 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 library 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 library under a commercial
16
// license, as set out in <http://cesanta.com/>.
17

18 19 20
#ifdef NOEMBED_NET_SKELETON
#include "net_skeleton.h"
#else
Sergey Lyubka's avatar
Sergey Lyubka committed
21
// net_skeleton start
Sergey Lyubka's avatar
Sergey Lyubka committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

// Copyright (c) 2014 Cesanta Software Limited
// All rights reserved
//
// This library 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 library 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 library under a commercial
// license, as set out in <http://cesanta.com/>.

#ifndef NS_SKELETON_HEADER_INCLUDED
#define NS_SKELETON_HEADER_INCLUDED

#define NS_SKELETON_VERSION "1.0"

#undef UNICODE                  // Use ANSI WinAPI functions
#undef _UNICODE                 // Use multibyte encoding on Windows
#define _MBCS                   // Use multibyte encoding on Windows
#define _INTEGRAL_MAX_BITS 64   // Enable _stati64() on Windows
#define _CRT_SECURE_NO_WARNINGS // Disable deprecation warning in VS2005+
#undef WIN32_LEAN_AND_MEAN      // Let windows.h always include winsock2.h
#define _XOPEN_SOURCE 600       // For flockfile() on Linux
#define __STDC_FORMAT_MACROS    // <inttypes.h> wants this for C++
#define __STDC_LIMIT_MACROS     // C++ wants that for INT64_MAX
#define _LARGEFILE_SOURCE       // Enable fseeko() and ftello() functions
#define _FILE_OFFSET_BITS 64    // Enable 64-bit file offsets

#ifdef _MSC_VER
#pragma warning (disable : 4127)  // FD_SET() emits warning, disable it
#pragma warning (disable : 4204)  // missing c99 support
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
Sergey Lyubka's avatar
Sergey Lyubka committed
72
#include <signal.h>
Sergey Lyubka's avatar
Sergey Lyubka committed
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib")    // Linking with winsock library
#include <windows.h>
#include <process.h>
#ifndef EINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
#ifndef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
#ifndef __func__
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
#endif
#ifndef va_copy
#define va_copy(x,y) x = y
#endif // MINGW #defines va_copy
#define snprintf _snprintf
#define vsnprintf _vsnprintf
#define to64(x) _atoi64(x)
typedef int socklen_t;
typedef unsigned char uint8_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned __int64 uint64_t;
typedef __int64   int64_t;
typedef SOCKET sock_t;
#else
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <pthread.h>
#include <stdarg.h>
#include <unistd.h>
#include <arpa/inet.h>  // For inet_pton() when NS_ENABLE_IPV6 is defined
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>
#define closesocket(x) close(x)
#define __cdecl
#define INVALID_SOCKET (-1)
#define to64(x) strtoll(x, NULL, 10)
typedef int sock_t;
#endif

#ifdef NS_ENABLE_DEBUG
#define DBG(x) do { printf("%-20s ", __func__); printf x; putchar('\n'); \
  fflush(stdout); } while(0)
#else
#define DBG(x)
#endif

#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0]))

#ifdef NS_ENABLE_SSL
#ifdef __APPLE__
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <openssl/ssl.h>
#else
typedef void *SSL;
typedef void *SSL_CTX;
#endif

#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

union socket_address {
  struct sockaddr sa;
  struct sockaddr_in sin;
#ifdef NS_ENABLE_IPV6
  struct sockaddr_in6 sin6;
#endif
};

struct iobuf {
  char *buf;
  int len;
  int size;
};

void iobuf_init(struct iobuf *, int initial_size);
void iobuf_free(struct iobuf *);
int iobuf_append(struct iobuf *, const void *data, int data_size);
void iobuf_remove(struct iobuf *, int data_size);

struct ns_connection;
enum ns_event { NS_POLL, NS_ACCEPT, NS_CONNECT, NS_RECV, NS_SEND, NS_CLOSE };
typedef void (*ns_callback_t)(struct ns_connection *, enum ns_event, void *);

struct ns_server {
  void *server_data;
  union socket_address listening_sa;
  sock_t listening_sock;
  struct ns_connection *active_connections;
  ns_callback_t callback;
  SSL_CTX *ssl_ctx;
  SSL_CTX *client_ssl_ctx;
Sergey Lyubka's avatar
Sergey Lyubka committed
174
  sock_t ctl[2];
Sergey Lyubka's avatar
Sergey Lyubka committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
};

struct ns_connection {
  struct ns_connection *prev, *next;
  struct ns_server *server;
  void *connection_data;
  time_t last_io_time;
  sock_t sock;
  struct iobuf recv_iobuf;
  struct iobuf send_iobuf;
  SSL *ssl;
  unsigned int flags;
#define NSF_FINISHED_SENDING_DATA   (1 << 0)
#define NSF_BUFFER_BUT_DONT_SEND    (1 << 1)
#define NSF_SSL_HANDSHAKE_DONE      (1 << 2)
#define NSF_CONNECTING              (1 << 3)
#define NSF_CLOSE_IMMEDIATELY       (1 << 4)
#define NSF_ACCEPTED                (1 << 5)
#define NSF_USER_1                  (1 << 6)
#define NSF_USER_2                  (1 << 7)
#define NSF_USER_3                  (1 << 8)
#define NSF_USER_4                  (1 << 9)
};

void ns_server_init(struct ns_server *, void *server_data, ns_callback_t);
void ns_server_free(struct ns_server *);
int ns_server_poll(struct ns_server *, int milli);
Sergey Lyubka's avatar
Sergey Lyubka committed
202
void ns_server_wakeup(struct ns_server *);
Sergey Lyubka's avatar
Sergey Lyubka committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
void ns_iterate(struct ns_server *, ns_callback_t cb, void *param);
struct ns_connection *ns_add_sock(struct ns_server *, sock_t sock, void *p);

int ns_bind(struct ns_server *, const char *addr);
int ns_set_ssl_cert(struct ns_server *, const char *ssl_cert);
struct ns_connection *ns_connect(struct ns_server *, const char *host,
                                 int port, int ssl, void *connection_param);

int ns_send(struct ns_connection *, const void *buf, int len);
int ns_printf(struct ns_connection *, const char *fmt, ...);
int ns_vprintf(struct ns_connection *, const char *fmt, va_list ap);

// Utility functions
void *ns_start_thread(void *(*f)(void *), void *p);
int ns_socketpair(sock_t [2]);
void ns_set_close_on_exec(sock_t);

#ifdef __cplusplus
}
#endif // __cplusplus

#endif // NS_SKELETON_HEADER_INCLUDED
// Copyright (c) 2014 Cesanta Software Limited
// All rights reserved
//
// This library 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 library 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 library under a commercial
// license, as set out in <http://cesanta.com/>.


#ifndef NS_MALLOC
#define NS_MALLOC malloc
#endif

#ifndef NS_REALLOC
#define NS_REALLOC realloc
#endif

#ifndef NS_FREE
#define NS_FREE free
#endif

#ifndef IOBUF_RESIZE_MULTIPLIER
#define IOBUF_RESIZE_MULTIPLIER 2.0
#endif

void iobuf_init(struct iobuf *iobuf, int size) {
  iobuf->len = iobuf->size = 0;
  iobuf->buf = NULL;

  if (size > 0 && (iobuf->buf = (char *) NS_MALLOC(size)) != NULL) {
    iobuf->size = size;
  }
}

void iobuf_free(struct iobuf *iobuf) {
  if (iobuf != NULL) {
    if (iobuf->buf != NULL) NS_FREE(iobuf->buf);
    iobuf_init(iobuf, 0);
  }
}

int iobuf_append(struct iobuf *io, const void *buf, int len) {
  static const double mult = IOBUF_RESIZE_MULTIPLIER;
  char *p = NULL;
  int new_len = 0;

  assert(io->len >= 0);
  assert(io->len <= io->size);

  if (len <= 0) {
  } else if ((new_len = io->len + len) < io->size) {
    memcpy(io->buf + io->len, buf, len);
    io->len = new_len;
  } else if ((p = (char *)
              NS_REALLOC(io->buf, (int) (new_len * mult))) != NULL) {
    io->buf = p;
    memcpy(io->buf + io->len, buf, len);
    io->len = new_len;
    io->size = (int) (new_len * mult);
  } else {
    len = 0;
  }

  return len;
}

void iobuf_remove(struct iobuf *io, int n) {
  if (n >= 0 && n <= io->len) {
    memmove(io->buf, io->buf + n, io->len - n);
    io->len -= n;
  }
}

#ifndef NS_DISABLE_THREADS
void *ns_start_thread(void *(*f)(void *), void *p) {
#ifdef _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 NS_STACK_SIZE > 1
  (void) pthread_attr_setstacksize(&attr, NS_STACK_SIZE);
#endif

  pthread_create(&thread_id, &attr, f, p);
  pthread_attr_destroy(&attr);

  return (void *) thread_id;
#endif
}
#endif  // NS_DISABLE_THREADS

329
static void ns_add_conn(struct ns_server *server, struct ns_connection *c) {
Sergey Lyubka's avatar
Sergey Lyubka committed
330 331 332 333 334 335
  c->next = server->active_connections;
  server->active_connections = c;
  c->prev = NULL;
  if (c->next != NULL) c->next->prev = c;
}

336
static void ns_remove_conn(struct ns_connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
337 338 339 340 341 342 343 344
  if (conn->prev == NULL) conn->server->active_connections = conn->next;
  if (conn->prev) conn->prev->next = conn->next;
  if (conn->next) conn->next->prev = conn->prev;
}

// Print message to buffer. If buffer is large enough to hold the message,
// return buffer. If buffer is to small, allocate large enough buffer on heap,
// and return allocated buffer.
345
static int ns_avprintf(char **buf, size_t size, const char *fmt, va_list ap) {
Sergey Lyubka's avatar
Sergey Lyubka committed
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
  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.
    *buf = NULL;
    while (len < 0) {
      if (*buf) free(*buf);
      size *= 2;
      if ((*buf = (char *) NS_MALLOC(size)) == NULL) break;
      va_copy(ap_copy, ap);
      len = vsnprintf(*buf, size, fmt, ap_copy);
      va_end(ap_copy);
    }
  } else if (len > (int) size) {
    // Standard-compliant code path. Allocate a buffer that is large enough.
    if ((*buf = (char *) NS_MALLOC(len + 1)) == NULL) {
      len = -1;
    } else {
      va_copy(ap_copy, ap);
      len = vsnprintf(*buf, len + 1, fmt, ap_copy);
      va_end(ap_copy);
    }
  }

  return len;
}

int ns_vprintf(struct ns_connection *conn, const char *fmt, va_list ap) {
  char mem[2000], *buf = mem;
  int len;

384
  if ((len = ns_avprintf(&buf, sizeof(mem), fmt, ap)) > 0) {
Sergey Lyubka's avatar
Sergey Lyubka committed
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402
    iobuf_append(&conn->send_iobuf, buf, len);
  }
  if (buf != mem && buf != NULL) {
    free(buf);
  }

  return len;
}

int ns_printf(struct ns_connection *conn, const char *fmt, ...) {
  int len;
  va_list ap;
  va_start(ap, fmt);
  len = ns_vprintf(conn, fmt, ap);
  va_end(ap);
  return len;
}

403
static void ns_call(struct ns_connection *conn, enum ns_event ev, void *p) {
Sergey Lyubka's avatar
Sergey Lyubka committed
404 405 406
  if (conn->server->callback) conn->server->callback(conn, ev, p);
}

407
static void ns_close_conn(struct ns_connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
408
  DBG(("%p %d", conn, conn->flags));
409 410
  ns_call(conn, NS_CLOSE, NULL);
  ns_remove_conn(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
411 412 413 414 415 416 417 418 419 420 421 422 423 424
  closesocket(conn->sock);
  iobuf_free(&conn->recv_iobuf);
  iobuf_free(&conn->send_iobuf);
  NS_FREE(conn);
}

void ns_set_close_on_exec(sock_t sock) {
#ifdef _WIN32
  (void) SetHandleInformation((HANDLE) sock, HANDLE_FLAG_INHERIT, 0);
#else
  fcntl(sock, F_SETFD, FD_CLOEXEC);
#endif
}

425
static void ns_set_non_blocking_mode(sock_t sock) {
Sergey Lyubka's avatar
Sergey Lyubka committed
426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
#ifdef _WIN32
  unsigned long on = 1;
  ioctlsocket(sock, FIONBIO, &on);
#else
  int flags = fcntl(sock, F_GETFL, 0);
  fcntl(sock, F_SETFL, flags | O_NONBLOCK);
#endif
}

#ifndef NS_DISABLE_SOCKETPAIR
int ns_socketpair(sock_t sp[2]) {
  struct sockaddr_in sa;
  sock_t sock;
  socklen_t len = sizeof(sa);
  int ret = 0;

  sp[0] = sp[1] = INVALID_SOCKET;

  (void) memset(&sa, 0, sizeof(sa));
  sa.sin_family = AF_INET;
  sa.sin_port = htons(0);
  sa.sin_addr.s_addr = htonl(0x7f000001);

  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET &&
      !bind(sock, (struct sockaddr *) &sa, len) &&
      !listen(sock, 1) &&
      !getsockname(sock, (struct sockaddr *) &sa, &len) &&
      (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
      !connect(sp[0], (struct sockaddr *) &sa, len) &&
      (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != INVALID_SOCKET) {
    ns_set_close_on_exec(sp[0]);
    ns_set_close_on_exec(sp[1]);
    ret = 1;
  } else {
    if (sp[0] != INVALID_SOCKET) closesocket(sp[0]);
    if (sp[1] != INVALID_SOCKET) closesocket(sp[1]);
    sp[0] = sp[1] = INVALID_SOCKET;
  }
  closesocket(sock);

  return ret;
}
#endif  // NS_DISABLE_SOCKETPAIR

// Valid listening port spec is: [ip_address:]port, e.g. "80", "127.0.0.1:3128"
471
static int ns_parse_port_string(const char *str, union socket_address *sa) {
Sergey Lyubka's avatar
Sergey Lyubka committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
  unsigned int a, b, c, d, port;
  int len = 0;
#ifdef NS_ENABLE_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);
#ifdef NS_ENABLE_IPV6
  } else if (sscanf(str, "[%49[^]]]:%u%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 <= 0xffff && str[len] == '\0';
}

// 'sa' must be an initialized address to bind to
506
static sock_t ns_open_listening_socket(union socket_address *sa) {
Sergey Lyubka's avatar
Sergey Lyubka committed
507 508 509 510 511 512 513 514
  socklen_t len = sizeof(*sa);
  sock_t on = 1, sock = INVALID_SOCKET;

  if ((sock = socket(sa->sa.sa_family, SOCK_STREAM, 6)) != INVALID_SOCKET &&
      !setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, sizeof(on)) &&
      !bind(sock, &sa->sa, sa->sa.sa_family == AF_INET ?
            sizeof(sa->sin) : sizeof(sa->sa)) &&
      !listen(sock, SOMAXCONN)) {
515
    ns_set_non_blocking_mode(sock);
Sergey Lyubka's avatar
Sergey Lyubka committed
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
    // In case port was set to 0, get the real port number
    (void) getsockname(sock, &sa->sa, &len);
  } else if (sock != INVALID_SOCKET) {
    closesocket(sock);
    sock = INVALID_SOCKET;
  }

  return sock;
}


int ns_set_ssl_cert(struct ns_server *server, const char *cert) {
#ifdef NS_ENABLE_SSL
  if (cert != NULL &&
      (server->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
    return -1;
  } else if (SSL_CTX_use_certificate_file(server->ssl_ctx, cert, 1) == 0 ||
             SSL_CTX_use_PrivateKey_file(server->ssl_ctx, cert, 1) == 0) {
    return -2;
  } else {
    SSL_CTX_use_certificate_chain_file(server->ssl_ctx, cert);
  }
  return 0;
#else
  return server != NULL && cert == NULL ? 0 : -3;
#endif
}

int ns_bind(struct ns_server *server, const char *str) {
545
  ns_parse_port_string(str, &server->listening_sa);
Sergey Lyubka's avatar
Sergey Lyubka committed
546 547 548
  if (server->listening_sock != INVALID_SOCKET) {
    closesocket(server->listening_sock);
  }
549
  server->listening_sock = ns_open_listening_socket(&server->listening_sa);
Sergey Lyubka's avatar
Sergey Lyubka committed
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
  return server->listening_sock == INVALID_SOCKET ? -1 :
    (int) ntohs(server->listening_sa.sin.sin_port);
}


static struct ns_connection *accept_conn(struct ns_server *server) {
  struct ns_connection *c = NULL;
  union socket_address sa;
  socklen_t len = sizeof(sa);
  sock_t sock = INVALID_SOCKET;

  // NOTE(lsm): on Windows, sock is always > FD_SETSIZE
  if ((sock = accept(server->listening_sock, &sa.sa, &len)) == INVALID_SOCKET) {
    closesocket(sock);
  } else if ((c = (struct ns_connection *) NS_MALLOC(sizeof(*c))) == NULL ||
             memset(c, 0, sizeof(*c)) == NULL) {
    closesocket(sock);
#ifdef NS_ENABLE_SSL
  } else if (server->ssl_ctx != NULL &&
             ((c->ssl = SSL_new(server->ssl_ctx)) == NULL ||
              SSL_set_fd(c->ssl, sock) != 1)) {
    DBG(("SSL error"));
    closesocket(sock);
    free(c);
    c = NULL;
#endif
  } else {
    ns_set_close_on_exec(sock);
578
    ns_set_non_blocking_mode(sock);
Sergey Lyubka's avatar
Sergey Lyubka committed
579 580 581 582
    c->server = server;
    c->sock = sock;
    c->flags |= NSF_ACCEPTED;

583 584
    ns_add_conn(server, c);
    ns_call(c, NS_ACCEPT, &sa);
Sergey Lyubka's avatar
Sergey Lyubka committed
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
    DBG(("%p %d %p %p", c, c->sock, c->ssl, server->ssl_ctx));
  }

  return c;
}

static int ns_is_error(int n) {
  return n == 0 ||
    (n < 0 && errno != EINTR && errno != EINPROGRESS &&
     errno != EAGAIN && errno != EWOULDBLOCK
#ifdef _WIN32
     && WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK
#endif
    );
}

#ifdef NS_ENABLE_HEXDUMP
602 603
static void ns_hexdump(const struct ns_connection *conn, const void *buf,
                       int len, const char *marker) {
Sergey Lyubka's avatar
Sergey Lyubka committed
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
  const unsigned char *p = (const unsigned char *) buf;
  char path[500], date[100], ascii[17];
  FILE *fp;

#if 0
  if (!match_prefix(NS_ENABLE_HEXDUMP, strlen(NS_ENABLE_HEXDUMP),
                    conn->remote_ip)) {
    return;
  }

  snprintf(path, sizeof(path), "%s.%hu.txt",
           conn->mg_conn.remote_ip, conn->mg_conn.remote_port);
#endif
  snprintf(path, sizeof(path), "%p.txt", conn);

  if ((fp = fopen(path, "a")) != NULL) {
    time_t cur_time = time(NULL);
    int i, idx;

    strftime(date, sizeof(date), "%d/%b/%Y %H:%M:%S", localtime(&cur_time));
    fprintf(fp, "%s %s %d bytes\n", marker, date, len);

    for (i = 0; i < len; i++) {
      idx = i % 16;
      if (idx == 0) {
        if (i > 0) fprintf(fp, "  %s\n", ascii);
        fprintf(fp, "%04x ", i);
      }
      fprintf(fp, " %02x", p[i]);
      ascii[idx] = p[i] < 0x20 || p[i] > 0x7e ? '.' : p[i];
      ascii[idx + 1] = '\0';
    }

    while (i++ % 16) fprintf(fp, "%s", "   ");
    fprintf(fp, "  %s\n\n", ascii);

    fclose(fp);
  }
}
#endif

645
static void ns_read_from_socket(struct ns_connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
646 647 648 649 650 651 652 653
  char buf[2048];
  int n = 0;

  if (conn->flags & NSF_CONNECTING) {
    int ok = 1, ret;
    socklen_t len = sizeof(ok);

    ret = getsockopt(conn->sock, SOL_SOCKET, SO_ERROR, (char *) &ok, &len);
654
    (void) ret;
Sergey Lyubka's avatar
Sergey Lyubka committed
655 656 657 658
#ifdef NS_ENABLE_SSL
    if (ret == 0 && ok == 0 && conn->ssl != NULL) {
      int res = SSL_connect(conn->ssl);
      int ssl_err = SSL_get_error(conn->ssl, res);
Sergey Lyubka's avatar
Sergey Lyubka committed
659
      DBG(("%p SSL_connect %d %d", conn, res, ssl_err));
Sergey Lyubka's avatar
Sergey Lyubka committed
660
      if (res == 1) {
Sergey Lyubka's avatar
Sergey Lyubka committed
661
        conn->flags = NSF_SSL_HANDSHAKE_DONE;
Sergey Lyubka's avatar
Sergey Lyubka committed
662
      } else if (ssl_err == 2 || ssl_err == 3) {
Sergey Lyubka's avatar
Sergey Lyubka committed
663 664 665 666 667 668
        return; // Call us again
      } else {
        ok = 1;
      }
    }
#endif
669
    conn->flags &= ~NSF_CONNECTING;
Sergey Lyubka's avatar
Sergey Lyubka committed
670 671 672 673
    DBG(("%p ok=%d", conn, ok));
    if (ok != 0) {
      conn->flags |= NSF_CLOSE_IMMEDIATELY;
    }
674
    ns_call(conn, NS_CONNECT, &ok);
Sergey Lyubka's avatar
Sergey Lyubka committed
675 676 677 678 679 680 681 682
    return;
  }

#ifdef NS_ENABLE_SSL
  if (conn->ssl != NULL) {
    if (conn->flags & NSF_SSL_HANDSHAKE_DONE) {
      n = SSL_read(conn->ssl, buf, sizeof(buf));
    } else {
Sergey Lyubka's avatar
Sergey Lyubka committed
683 684
      int res = SSL_accept(conn->ssl);
      int ssl_err = SSL_get_error(conn->ssl, res);
Sergey Lyubka's avatar
Sergey Lyubka committed
685
      DBG(("%p SSL_accept %d %d", conn, res, ssl_err));
Sergey Lyubka's avatar
Sergey Lyubka committed
686
      if (res == 1) {
Sergey Lyubka's avatar
Sergey Lyubka committed
687
        conn->flags |= NSF_SSL_HANDSHAKE_DONE;
Sergey Lyubka's avatar
Sergey Lyubka committed
688
      } else if (ssl_err == 2 || ssl_err == 3) {
Sergey Lyubka's avatar
Sergey Lyubka committed
689 690 691
        return; // Call us again
      } else {
        conn->flags |= NSF_CLOSE_IMMEDIATELY;
Sergey Lyubka's avatar
Sergey Lyubka committed
692 693 694 695 696 697 698 699 700 701
      }
      return;
    }
  } else
#endif
  {
    n = recv(conn->sock, buf, sizeof(buf), 0);
  }

#ifdef NS_ENABLE_HEXDUMP
702
  ns_hexdump(conn, buf, n, "<-");
Sergey Lyubka's avatar
Sergey Lyubka committed
703 704 705 706 707 708 709 710 711
#endif

  DBG(("%p <- %d bytes [%.*s%s]",
       conn, n, n < 40 ? n : 40, buf, n < 40 ? "" : "..."));

  if (ns_is_error(n)) {
    conn->flags |= NSF_CLOSE_IMMEDIATELY;
  } else if (n > 0) {
    iobuf_append(&conn->recv_iobuf, buf, n);
712
    ns_call(conn, NS_RECV, &n);
Sergey Lyubka's avatar
Sergey Lyubka committed
713 714 715
  }
}

716
static void ns_write_to_socket(struct ns_connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
717 718 719 720 721 722 723 724 725 726 727 728
  struct iobuf *io = &conn->send_iobuf;
  int n = 0;

#ifdef NS_ENABLE_SSL
  if (conn->ssl != NULL) {
    n = SSL_write(conn->ssl, io->buf, io->len);
  } else
#endif
  { n = send(conn->sock, io->buf, io->len, 0); }


#ifdef NS_ENABLE_HEXDUMP
729
  ns_hexdump(conn, io->buf, n, "->");
Sergey Lyubka's avatar
Sergey Lyubka committed
730 731
#endif

732 733
  DBG(("%p -> %d bytes %d [%.*s%s]", conn, n, conn->flags,
       io->len < 40 ? io->len : 40,
Sergey Lyubka's avatar
Sergey Lyubka committed
734 735 736 737 738 739 740 741 742 743 744 745 746
       io->buf, io->len < 40 ? "" : "..."));

  if (ns_is_error(n)) {
    conn->flags |= NSF_CLOSE_IMMEDIATELY;
  } else if (n > 0) {
    iobuf_remove(io, n);
    //conn->num_bytes_sent += n;
  }

  if (io->len == 0 && conn->flags & NSF_FINISHED_SENDING_DATA) {
    conn->flags |= NSF_CLOSE_IMMEDIATELY;
  }

747
  ns_call(conn, NS_SEND, NULL);
Sergey Lyubka's avatar
Sergey Lyubka committed
748 749 750 751 752 753
}

int ns_send(struct ns_connection *conn, const void *buf, int len) {
  return iobuf_append(&conn->send_iobuf, buf, len);
}

754
static void ns_add_to_set(sock_t sock, fd_set *set, sock_t *max_fd) {
755 756 757 758 759
  if (sock != INVALID_SOCKET) {
    FD_SET(sock, set);
    if (*max_fd == INVALID_SOCKET || sock > *max_fd) {
      *max_fd = sock;
    }
Sergey Lyubka's avatar
Sergey Lyubka committed
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
  }
}

int ns_server_poll(struct ns_server *server, int milli) {
  struct ns_connection *conn, *tmp_conn;
  struct timeval tv;
  fd_set read_set, write_set;
  int num_active_connections = 0;
  sock_t max_fd = INVALID_SOCKET;
  time_t current_time = time(NULL);

  if (server->listening_sock == INVALID_SOCKET &&
      server->active_connections == NULL) return 0;

  FD_ZERO(&read_set);
  FD_ZERO(&write_set);
776
  ns_add_to_set(server->listening_sock, &read_set, &max_fd);
777
  ns_add_to_set(server->ctl[1], &read_set, &max_fd);
Sergey Lyubka's avatar
Sergey Lyubka committed
778 779 780

  for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
    tmp_conn = conn->next;
781 782
    ns_call(conn, NS_POLL, &current_time);
    ns_add_to_set(conn->sock, &read_set, &max_fd);
Sergey Lyubka's avatar
Sergey Lyubka committed
783
    if (conn->flags & NSF_CONNECTING) {
784
      ns_add_to_set(conn->sock, &write_set, &max_fd);
Sergey Lyubka's avatar
Sergey Lyubka committed
785 786
    }
    if (conn->send_iobuf.len > 0 && !(conn->flags & NSF_BUFFER_BUT_DONT_SEND)) {
787
      ns_add_to_set(conn->sock, &write_set, &max_fd);
Sergey Lyubka's avatar
Sergey Lyubka committed
788
    } else if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
789
      ns_close_conn(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
790 791 792 793 794 795 796 797
    }
  }

  tv.tv_sec = milli / 1000;
  tv.tv_usec = (milli % 1000) * 1000;

  if (select((int) max_fd + 1, &read_set, &write_set, NULL, &tv) > 0) {
    // Accept new connections
798
    if (server->listening_sock != INVALID_SOCKET &&
Sergey Lyubka's avatar
Sergey Lyubka committed
799 800 801 802 803 804 805 806 807
        FD_ISSET(server->listening_sock, &read_set)) {
      // We're not looping here, and accepting just one connection at
      // a time. The reason is that eCos does not respect non-blocking
      // flag on a listening socket and hangs in a loop.
      if ((conn = accept_conn(server)) != NULL) {
        conn->last_io_time = current_time;
      }
    }

808 809 810 811 812 813 814 815
    // Read possible wakeup calls
    if (server->ctl[1] != INVALID_SOCKET &&
        FD_ISSET(server->ctl[1], &read_set)) {
      unsigned char ch;
      recv(server->ctl[1], &ch, 1, 0);
      send(server->ctl[1], &ch, 1, 0);
    }

Sergey Lyubka's avatar
Sergey Lyubka committed
816 817 818 819
    for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
      tmp_conn = conn->next;
      if (FD_ISSET(conn->sock, &read_set)) {
        conn->last_io_time = current_time;
820
        ns_read_from_socket(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
821 822 823
      }
      if (FD_ISSET(conn->sock, &write_set)) {
        if (conn->flags & NSF_CONNECTING) {
824
          ns_read_from_socket(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
825 826
        } else if (!(conn->flags & NSF_BUFFER_BUT_DONT_SEND)) {
          conn->last_io_time = current_time;
827
          ns_write_to_socket(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
828 829 830 831 832 833 834 835 836
        }
      }
    }
  }

  for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
    tmp_conn = conn->next;
    num_active_connections++;
    if (conn->flags & NSF_CLOSE_IMMEDIATELY) {
837
      ns_close_conn(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
    }
  }
  //DBG(("%d active connections", num_active_connections));

  return num_active_connections;
}

struct ns_connection *ns_connect(struct ns_server *server, const char *host,
                                 int port, int use_ssl, void *param) {
  sock_t sock = INVALID_SOCKET;
  struct sockaddr_in sin;
  struct hostent *he = NULL;
  struct ns_connection *conn = NULL;
  int connect_ret_val;

853
  (void) use_ssl;
Sergey Lyubka's avatar
Sergey Lyubka committed
854
  if (host == NULL || (he = gethostbyname(host)) == NULL ||
855
      (sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
Sergey Lyubka's avatar
Sergey Lyubka committed
856 857 858 859 860 861 862
    DBG(("gethostbyname(%s) failed: %s", host, strerror(errno)));
    return NULL;
  }

  sin.sin_family = AF_INET;
  sin.sin_port = htons((uint16_t) port);
  sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
863
  ns_set_non_blocking_mode(sock);
Sergey Lyubka's avatar
Sergey Lyubka committed
864 865 866

  connect_ret_val = connect(sock, (struct sockaddr *) &sin, sizeof(sin));
  if (ns_is_error(connect_ret_val)) {
Sergey Lyubka's avatar
Sergey Lyubka committed
867
    closesocket(sock);
Sergey Lyubka's avatar
Sergey Lyubka committed
868 869 870 871 872 873 874 875 876 877 878 879
    return NULL;
  } else if ((conn = (struct ns_connection *)
              NS_MALLOC(sizeof(*conn))) == NULL) {
    closesocket(sock);
    return NULL;
  }

  memset(conn, 0, sizeof(*conn));
  conn->server = server;
  conn->sock = sock;
  conn->connection_data = param;
  conn->flags = NSF_CONNECTING;
880
  conn->last_io_time = time(NULL);
Sergey Lyubka's avatar
Sergey Lyubka committed
881 882 883 884 885 886 887 888

#ifdef NS_ENABLE_SSL
  if (use_ssl &&
      (conn->ssl = SSL_new(server->client_ssl_ctx)) != NULL) {
    SSL_set_fd(conn->ssl, sock);
  }
#endif

889
  ns_add_conn(server, conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
890 891 892 893 894 895 896 897 898
  DBG(("%p %s:%d %d %p", conn, host, port, conn->sock, conn->ssl));

  return conn;
}

struct ns_connection *ns_add_sock(struct ns_server *s, sock_t sock, void *p) {
  struct ns_connection *conn;
  if ((conn = (struct ns_connection *) NS_MALLOC(sizeof(*conn))) != NULL) {
    memset(conn, 0, sizeof(*conn));
899
    ns_set_non_blocking_mode(sock);
Sergey Lyubka's avatar
Sergey Lyubka committed
900 901 902
    conn->sock = sock;
    conn->connection_data = p;
    conn->server = s;
903
    conn->last_io_time = time(NULL);
904
    ns_add_conn(s, conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
905 906 907 908 909 910 911 912 913 914 915 916 917 918
    DBG(("%p %d", conn, sock));
  }
  return conn;
}

void ns_iterate(struct ns_server *server, ns_callback_t cb, void *param) {
  struct ns_connection *conn, *tmp_conn;

  for (conn = server->active_connections; conn != NULL; conn = tmp_conn) {
    tmp_conn = conn->next;
    cb(conn, NS_POLL, param);
  }
}

Sergey Lyubka's avatar
Sergey Lyubka committed
919 920 921 922 923 924 925 926
void ns_server_wakeup(struct ns_server *server) {
  unsigned char ch = 0;
  if (server->ctl[0] != INVALID_SOCKET) {
    send(server->ctl[0], &ch, 1, 0);
    recv(server->ctl[0], &ch, 1, 0);
  }
}

Sergey Lyubka's avatar
Sergey Lyubka committed
927 928
void ns_server_init(struct ns_server *s, void *server_data, ns_callback_t cb) {
  memset(s, 0, sizeof(*s));
Sergey Lyubka's avatar
Sergey Lyubka committed
929
  s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
Sergey Lyubka's avatar
Sergey Lyubka committed
930 931 932 933 934 935 936 937 938 939 940
  s->server_data = server_data;
  s->callback = cb;

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

Sergey Lyubka's avatar
Sergey Lyubka committed
941 942 943 944 945 946
#ifndef NS_DISABLE_SOCKETPAIR
  do {
    ns_socketpair(s->ctl);
  } while (s->ctl[0] == INVALID_SOCKET);
#endif

Sergey Lyubka's avatar
Sergey Lyubka committed
947 948 949 950 951 952 953 954 955 956 957 958 959 960
#ifdef NS_ENABLE_SSL
  SSL_library_init();
  s->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method());
#endif
}

void ns_server_free(struct ns_server *s) {
  struct ns_connection *conn, *tmp_conn;

  DBG(("%p", s));
  if (s == NULL) return;
  // Do one last poll, see https://github.com/cesanta/mongoose/issues/286
  ns_server_poll(s, 0);

Sergey Lyubka's avatar
Sergey Lyubka committed
961 962 963 964
  if (s->listening_sock != INVALID_SOCKET) closesocket(s->listening_sock);
  if (s->ctl[0] != INVALID_SOCKET) closesocket(s->ctl[0]);
  if (s->ctl[1] != INVALID_SOCKET) closesocket(s->ctl[1]);
  s->listening_sock = s->ctl[0] = s->ctl[1] = INVALID_SOCKET;
Sergey Lyubka's avatar
Sergey Lyubka committed
965 966 967

  for (conn = s->active_connections; conn != NULL; conn = tmp_conn) {
    tmp_conn = conn->next;
968
    ns_close_conn(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
969 970 971 972 973 974 975 976
  }

#ifdef NS_ENABLE_SSL
  if (s->ssl_ctx != NULL) SSL_CTX_free(s->ssl_ctx);
  if (s->client_ssl_ctx != NULL) SSL_CTX_free(s->client_ssl_ctx);
#endif
}

Sergey Lyubka's avatar
Sergey Lyubka committed
977
// net_skeleton end
978
#endif  // NOEMBED_NET_SKELETON
979 980 981

#include <ctype.h>

Sergey Lyubka's avatar
Sergey Lyubka committed
982 983 984
#ifdef _WIN32         //////////////// Windows specific defines and includes
#include <io.h>       // For _lseeki64
#include <direct.h>   // For _mkdir
985
#ifndef S_ISDIR
986
#define S_ISDIR(x) ((x) & _S_IFDIR)
987
#endif
988
#define sleep(x) Sleep((x) * 1000)
989 990 991
#define stat(x, y) mg_stat((x), (y))
#define fopen(x, y) mg_fopen((x), (y))
#define open(x, y) mg_open((x), (y))
992
#define lseek(x, y, z) _lseeki64((x), (y), (z))
993 994
#define popen(x, y) _popen((x), (y))
#define pclose(x) _pclose(x)
995 996 997 998 999 1000
#define mkdir(x, y) _mkdir(x)
#define to64(x) _atoi64(x)
#ifndef __func__
#define STRX(x) #x
#define STR(x) STRX(x)
#define __func__ __FILE__ ":" STR(__LINE__)
1001
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
1002 1003 1004 1005 1006 1007
#define INT64_FMT  "I64d"
#define stat(x, y) mg_stat((x), (y))
#define fopen(x, y) mg_fopen((x), (y))
#define open(x, y) mg_open((x), (y))
#define flockfile(x)      ((void) (x))
#define funlockfile(x)    ((void) (x))
Sergey Lyubka's avatar
Sergey Lyubka committed
1008
typedef struct _stati64 file_stat_t;
Sergey Lyubka's avatar
Sergey Lyubka committed
1009 1010
typedef HANDLE pid_t;
#else                    ////////////// UNIX specific defines and includes
1011
#include <dirent.h>
Sergey Lyubka's avatar
Sergey Lyubka committed
1012 1013
#include <inttypes.h>
#include <pwd.h>
1014
#define O_BINARY 0
Sergey Lyubka's avatar
Sergey Lyubka committed
1015 1016
#define INT64_FMT PRId64
typedef struct stat file_stat_t;
Sergey Lyubka's avatar
Sergey Lyubka committed
1017
#endif                  //////// End of platform-specific defines and includes
Sergey Lyubka's avatar
Sergey Lyubka committed
1018

1019 1020
#include "mongoose.h"

1021 1022 1023
#define MAX_REQUEST_SIZE 16384
#define IOBUF_SIZE 8192
#define MAX_PATH_SIZE 8192
Sergey Lyubka's avatar
Sergey Lyubka committed
1024
#define LUA_SCRIPT_PATTERN "**.lp$"
1025
#define DEFAULT_CGI_PATTERN "**.cgi$|**.pl$|**.php$"
1026
#define CGI_ENVIRONMENT_SIZE 8192
1027
#define MAX_CGI_ENVIR_VARS 64
1028 1029
#define ENV_EXPORT_TO_CGI "MONGOOSE_CGI"
#define PASSWORDS_FILE_NAME ".htpasswd"
1030

1031 1032
#ifndef MONGOOSE_USE_WEBSOCKET_PING_INTERVAL
#define MONGOOSE_USE_WEBSOCKET_PING_INTERVAL 5
1033
#endif
1034 1035

// Extra HTTP headers to send in every static file reply
1036 1037
#if !defined(MONGOOSE_USE_EXTRA_HTTP_HEADERS)
#define MONGOOSE_USE_EXTRA_HTTP_HEADERS ""
1038 1039
#endif

Sergey Lyubka's avatar
Sergey Lyubka committed
1040 1041
#ifndef MONGOOSE_POST_SIZE_LIMIT
#define MONGOOSE_POST_SIZE_LIMIT 0
1042 1043
#endif

Sergey Lyubka's avatar
Sergey Lyubka committed
1044 1045
#ifndef MONGOOSE_IDLE_TIMEOUT_SECONDS
#define MONGOOSE_IDLE_TIMEOUT_SECONDS 30
1046 1047
#endif

Sergey Lyubka's avatar
Sergey Lyubka committed
1048 1049 1050 1051
#ifdef MONGOOSE_NO_SOCKETPAIR
#define MONGOOSE_NO_CGI
#endif

1052 1053 1054 1055 1056 1057
#ifdef MONGOOSE_NO_FILESYSTEM
#define MONGOOSE_NO_AUTH
#define MONGOOSE_NO_CGI
#define MONGOOSE_NO_DAV
#define MONGOOSE_NO_DIRECTORY_LISTING
#define MONGOOSE_NO_LOGGING
1058
#define MONGOOSE_NO_SSI
Sergey Lyubka's avatar
Sergey Lyubka committed
1059 1060
#endif

1061 1062
struct vec {
  const char *ptr;
1063
  int len;
1064 1065
};

1066 1067 1068 1069 1070
// For directory listing and WevDAV support
struct dir_entry {
  struct connection *conn;
  char *file_name;
  file_stat_t st;
1071 1072 1073 1074
};

// NOTE(lsm): this enum shoulds be in sync with the config_options.
enum {
Sergey Lyubka's avatar
Sergey Lyubka committed
1075
  ACCESS_CONTROL_LIST,
1076
#ifndef MONGOOSE_NO_FILESYSTEM
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
  ACCESS_LOG_FILE,
#ifndef MONGOOSE_NO_AUTH
  AUTH_DOMAIN,
#endif
#ifndef MONGOOSE_NO_CGI
  CGI_INTERPRETER,
  CGI_PATTERN,
#endif
#ifndef MONGOOSE_NO_DAV
  DAV_AUTH_FILE,
#endif
  DOCUMENT_ROOT,
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
  ENABLE_DIRECTORY_LISTING,
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
1092 1093
#endif
  EXTRA_MIME_TYPES,
1094
#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
Sergey Lyubka's avatar
Sergey Lyubka committed
1095 1096 1097
  GLOBAL_AUTH_FILE,
#endif
  HIDE_FILES_PATTERN,
1098
#ifndef MONGOOSE_NO_FILESYSTEM
Sergey Lyubka's avatar
Sergey Lyubka committed
1099 1100 1101
  INDEX_FILES,
#endif
  LISTENING_PORT,
1102 1103 1104
#ifndef _WIN32
  RUN_AS_USER,
#endif
1105 1106 1107
#ifndef MONGOOSE_NO_SSI
  SSI_PATTERN,
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
1108
#ifdef NS_ENABLE_SSL
1109 1110
  SSL_CERTIFICATE,
#endif
1111 1112
  URL_REWRITES,
  NUM_OPTIONS
1113 1114
};

Sergey Lyubka's avatar
Sergey Lyubka committed
1115 1116
static const char *static_config_options[] = {
  "access_control_list", NULL,
1117
#ifndef MONGOOSE_NO_FILESYSTEM
Sergey Lyubka's avatar
Sergey Lyubka committed
1118
  "access_log_file", NULL,
1119
#ifndef MONGOOSE_NO_AUTH
Sergey Lyubka's avatar
Sergey Lyubka committed
1120
  "auth_domain", "mydomain.com",
1121 1122
#endif
#ifndef MONGOOSE_NO_CGI
Sergey Lyubka's avatar
Sergey Lyubka committed
1123
  "cgi_interpreter", NULL,
1124 1125 1126
  "cgi_pattern", DEFAULT_CGI_PATTERN,
#endif
#ifndef MONGOOSE_NO_DAV
Sergey Lyubka's avatar
Sergey Lyubka committed
1127
  "dav_auth_file", NULL,
1128
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
1129
  "document_root",  NULL,
1130
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
Sergey Lyubka's avatar
Sergey Lyubka committed
1131
  "enable_directory_listing", "yes",
1132
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
1133 1134
#endif
  "extra_mime_types", NULL,
1135
#if !defined(MONGOOSE_NO_FILESYSTEM) && !defined(MONGOOSE_NO_AUTH)
Sergey Lyubka's avatar
Sergey Lyubka committed
1136 1137 1138
  "global_auth_file", NULL,
#endif
  "hide_files_patterns", NULL,
1139
#ifndef MONGOOSE_NO_FILESYSTEM
1140
  "index_files","index.html,index.htm,index.shtml,index.cgi,index.php,index.lp",
Sergey Lyubka's avatar
Sergey Lyubka committed
1141 1142 1143 1144 1145
#endif
  "listening_port", NULL,
#ifndef _WIN32
  "run_as_user", NULL,
#endif
1146 1147 1148
#ifndef MONGOOSE_NO_SSI
  "ssi_pattern", "**.shtml$|**.shtm$",
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
1149
#ifdef NS_ENABLE_SSL
Sergey Lyubka's avatar
Sergey Lyubka committed
1150 1151 1152 1153 1154 1155
  "ssl_certificate", NULL,
#endif
  "url_rewrites", NULL,
  NULL
};

1156
struct mg_server {
Sergey Lyubka's avatar
Sergey Lyubka committed
1157
  struct ns_server ns_server;
1158
  union socket_address lsa;   // Listening socket address
1159
  mg_handler_t event_handler;
1160
  char *config_options[NUM_OPTIONS];
1161
  char local_ip[48];
1162 1163
};

1164 1165
// Local endpoint representation
union endpoint {
Sergey Lyubka's avatar
Sergey Lyubka committed
1166 1167
  int fd;                           // Opened regular local file
  struct ns_connection *cgi_conn;   // CGI socket
1168 1169
};

1170
enum endpoint_type { EP_NONE, EP_FILE, EP_CGI, EP_USER, EP_PUT, EP_CLIENT };
Sergey Lyubka's avatar
Sergey Lyubka committed
1171 1172 1173 1174

#define MG_HEADERS_SENT NSF_USER_1
#define MG_LONG_RUNNING NSF_USER_2
#define MG_CGI_CONN NSF_USER_3
1175 1176

struct connection {
Sergey Lyubka's avatar
Sergey Lyubka committed
1177 1178
  struct ns_connection *ns_conn;
  struct mg_connection mg_conn;
1179 1180 1181 1182 1183 1184 1185 1186
  struct mg_server *server;
  union endpoint endpoint;
  enum endpoint_type endpoint_type;
  char *path_info;
  char *request;
  int64_t num_bytes_sent; // Total number of bytes sent
  int64_t cl;             // Reply content length, for Range support
  int request_len;  // Request length, including last \r\n after last header
Sergey Lyubka's avatar
Sergey Lyubka committed
1187
  //int flags;        // CONN_* flags: CONN_CLOSE, CONN_SPOOL_DONE, etc
1188
  //mg_handler_t handler;  // Callback for HTTP client
1189 1190
};

Sergey Lyubka's avatar
Sergey Lyubka committed
1191 1192 1193
#define MG_CONN_2_CONN(c) ((struct connection *) ((char *) (c) - \
  offsetof(struct connection, mg_conn)))

1194
static void open_local_endpoint(struct connection *conn, int skip_user);
1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
static void close_local_endpoint(struct connection *conn);

static const struct {
  const char *extension;
  size_t ext_len;
  const char *mime_type;
} static_builtin_mime_types[] = {
  {".html", 5, "text/html"},
  {".htm", 4, "text/html"},
  {".shtm", 5, "text/html"},
  {".shtml", 6, "text/html"},
  {".css", 4, "text/css"},
  {".js",  3, "application/x-javascript"},
  {".ico", 4, "image/x-icon"},
  {".gif", 4, "image/gif"},
  {".jpg", 4, "image/jpeg"},
  {".jpeg", 5, "image/jpeg"},
  {".png", 4, "image/png"},
  {".svg", 4, "image/svg+xml"},
  {".txt", 4, "text/plain"},
  {".torrent", 8, "application/x-bittorrent"},
  {".wav", 4, "audio/x-wav"},
  {".mp3", 4, "audio/x-mp3"},
  {".mid", 4, "audio/mid"},
  {".m3u", 4, "audio/x-mpegurl"},
  {".ogg", 4, "application/ogg"},
  {".ram", 4, "audio/x-pn-realaudio"},
  {".xml", 4, "text/xml"},
  {".json",  5, "text/json"},
  {".xslt", 5, "application/xml"},
  {".xsl", 4, "application/xml"},
  {".ra",  3, "audio/x-pn-realaudio"},
  {".doc", 4, "application/msword"},
  {".exe", 4, "application/octet-stream"},
  {".zip", 4, "application/x-zip-compressed"},
  {".xls", 4, "application/excel"},
  {".tgz", 4, "application/x-tar-gz"},
  {".tar", 4, "application/x-tar"},
  {".gz",  3, "application/x-gunzip"},
  {".arj", 4, "application/x-arj-compressed"},
  {".rar", 4, "application/x-arj-compressed"},
  {".rtf", 4, "application/rtf"},
  {".pdf", 4, "application/pdf"},
  {".swf", 4, "application/x-shockwave-flash"},
  {".mpg", 4, "video/mpeg"},
  {".webm", 5, "video/webm"},
  {".mpeg", 5, "video/mpeg"},
  {".mov", 4, "video/quicktime"},
  {".mp4", 4, "video/mp4"},
  {".m4v", 4, "video/x-m4v"},
  {".asf", 4, "video/x-ms-asf"},
  {".avi", 4, "video/x-msvideo"},
  {".bmp", 4, "image/bmp"},
  {".ttf", 4, "application/x-font-ttf"},
  {NULL,  0, NULL}
};

1252
#ifndef MONGOOSE_NO_THREADS
1253
void *mg_start_thread(void *(*f)(void *), void *p) {
Sergey Lyubka's avatar
Sergey Lyubka committed
1254
  return ns_start_thread(f, p);
1255
}
1256
#endif  // MONGOOSE_NO_THREADS
1257

1258
#ifdef _WIN32
1259
#ifndef MONGOOSE_NO_FILESYSTEM
1260 1261
// Encode 'path' which is assumed UTF-8 string, into UNICODE string.
// wbuf and wbuf_len is a target buffer and its length.
1262
static void to_wchar(const char *path, wchar_t *wbuf, size_t wbuf_len) {
Sergey Lyubka's avatar
Sergey Lyubka committed
1263
  char buf[MAX_PATH_SIZE * 2], buf2[MAX_PATH_SIZE * 2], *p;
1264 1265 1266

  strncpy(buf, path, sizeof(buf));
  buf[sizeof(buf) - 1] = '\0';
Sergey Lyubka's avatar
Sergey Lyubka committed
1267

1268
  // Trim trailing slashes. Leave backslash for paths like "X:\"
Sergey Lyubka's avatar
Sergey Lyubka committed
1269
  p = buf + strlen(buf) - 1;
1270
  while (p > buf && p[-1] != ':' && (p[0] == '\\' || p[0] == '/')) *p-- = '\0';
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284

  // Convert to Unicode and back. If doubly-converted string does not
  // match the original, something is fishy, reject.
  memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
  MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
  WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
                      NULL, NULL);
  if (strcmp(buf, buf2) != 0) {
    wbuf[0] = L'\0';
  }
}

static int mg_stat(const char *path, file_stat_t *st) {
  wchar_t wpath[MAX_PATH_SIZE];
1285
  to_wchar(path, wpath, ARRAY_SIZE(wpath));
Sergey Lyubka's avatar
Sergey Lyubka committed
1286
  DBG(("[%ls] -> %d", wpath, _wstati64(wpath, st)));
1287 1288 1289 1290 1291
  return _wstati64(wpath, st);
}

static FILE *mg_fopen(const char *path, const char *mode) {
  wchar_t wpath[MAX_PATH_SIZE], wmode[10];
1292 1293
  to_wchar(path, wpath, ARRAY_SIZE(wpath));
  to_wchar(mode, wmode, ARRAY_SIZE(wmode));
1294 1295 1296 1297 1298
  return _wfopen(wpath, wmode);
}

static int mg_open(const char *path, int flag) {
  wchar_t wpath[MAX_PATH_SIZE];
1299
  to_wchar(path, wpath, ARRAY_SIZE(wpath));
1300 1301 1302
  return _wopen(wpath, flag);
}
#endif
1303
#endif // MONGOOSE_NO_FILESYSTEM
1304

1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343
// A helper function for traversing a comma separated list of values.
// It returns a list pointer shifted to the next value, or NULL if the end
// of the list found.
// Value is stored in val vector. If value has form "x=y", then eq_val
// vector is initialized to point to the "y" part, and val vector length
// is adjusted to point only to "x".
static const char *next_option(const char *list, struct vec *val,
                               struct vec *eq_val) {
  if (list == NULL || *list == '\0') {
    // End of the list
    list = NULL;
  } else {
    val->ptr = list;
    if ((list = strchr(val->ptr, ',')) != NULL) {
      // Comma found. Store length and shift the list ptr
      val->len = list - val->ptr;
      list++;
    } else {
      // This value is the last one
      list = val->ptr + strlen(val->ptr);
      val->len = list - val->ptr;
    }

    if (eq_val != NULL) {
      // Value has form "x=y", adjust pointers and lengths
      // so that val points to "x", and eq_val points to "y".
      eq_val->len = 0;
      eq_val->ptr = (const char *) memchr(val->ptr, '=', val->len);
      if (eq_val->ptr != NULL) {
        eq_val->ptr++;  // Skip over '=' character
        eq_val->len = val->ptr + val->len - eq_val->ptr;
        val->len = (eq_val->ptr - val->ptr) - 1;
      }
    }
  }

  return list;
}

1344 1345 1346 1347
// Like snprintf(), but never returns negative value, or a value
// that is larger than a supplied buffer.
static int mg_vsnprintf(char *buf, size_t buflen, const char *fmt, va_list ap) {
  int n;
1348
  if (buflen < 1) return 0;
1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367
  n = vsnprintf(buf, buflen, fmt, ap);
  if (n < 0) {
    n = 0;
  } else if (n >= (int) buflen) {
    n = (int) buflen - 1;
  }
  buf[n] = '\0';
  return n;
}

static int mg_snprintf(char *buf, size_t buflen, const char *fmt, ...) {
  va_list ap;
  int n;
  va_start(ap, fmt);
  n = mg_vsnprintf(buf, buflen, fmt, ap);
  va_end(ap);
  return n;
}

1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
// Check whether full request is buffered. Return:
//   -1  if request is malformed
//    0  if request is not yet fully buffered
//   >0  actual request length, including last \r\n\r\n
static int get_request_len(const char *s, int buf_len) {
  const unsigned char *buf = (unsigned char *) s;
  int i;

  for (i = 0; i < buf_len; i++) {
    // Control characters are not allowed but >=128 are.
    // Abort scan as soon as one malformed character is found.
    if (!isprint(buf[i]) && buf[i] != '\r' && buf[i] != '\n' && buf[i] < 128) {
      return -1;
    } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
      return i + 2;
    } else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
               buf[i + 2] == '\n') {
      return i + 3;
    }
  }

  return 0;
}

// Skip the characters until one of the delimiters characters found.
// 0-terminate resulting word. Skip the rest of the delimiters if any.
// Advance pointer to buffer to the next word. Return found 0-terminated word.
static char *skip(char **buf, const char *delimiters) {
  char *p, *begin_word, *end_word, *end_delimiters;

  begin_word = *buf;
  end_word = begin_word + strcspn(begin_word, delimiters);
  end_delimiters = end_word + strspn(end_word, delimiters);

  for (p = end_word; p < end_delimiters; p++) {
    *p = '\0';
  }

  *buf = end_delimiters;

  return begin_word;
}

// Parse HTTP headers from the given buffer, advance buffer to the point
// where parsing stopped.
static void parse_http_headers(char **buf, struct mg_connection *ri) {
  size_t i;

  for (i = 0; i < ARRAY_SIZE(ri->http_headers); i++) {
    ri->http_headers[i].name = skip(buf, ": ");
    ri->http_headers[i].value = skip(buf, "\r\n");
    if (ri->http_headers[i].name[0] == '\0')
      break;
    ri->num_headers = i + 1;
  }
}

1425 1426 1427 1428 1429
static const char *status_code_to_str(int status_code) {
  switch (status_code) {
    case 200: return "OK";
    case 201: return "Created";
    case 204: return "No Content";
1430 1431
    case 301: return "Moved Permanently";
    case 302: return "Found";
1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
    case 304: return "Not Modified";
    case 400: return "Bad Request";
    case 403: return "Forbidden";
    case 404: return "Not Found";
    case 405: return "Method Not Allowed";
    case 409: return "Conflict";
    case 411: return "Length Required";
    case 413: return "Request Entity Too Large";
    case 415: return "Unsupported Media Type";
    case 423: return "Locked";
    case 500: return "Server Error";
    case 501: return "Not Implemented";
    default:  return "Server Error";
  }
}

1448 1449 1450 1451 1452 1453
static int call_user(struct connection *conn, enum mg_event ev) {
  return conn != NULL && conn->server != NULL &&
    conn->server->event_handler != NULL ?
    conn->server->event_handler(&conn->mg_conn, ev) : MG_FALSE;
}

1454 1455
static void send_http_error(struct connection *conn, int code,
                            const char *fmt, ...) {
1456
  const char *message = status_code_to_str(code);
1457
  const char *rewrites = conn->server->config_options[URL_REWRITES];
1458
  char headers[200], body[200];
1459
  struct vec a, b;
1460
  va_list ap;
1461 1462
  int body_len, headers_len, match_code;

1463 1464 1465
  conn->mg_conn.status_code = code;

  // Invoke error handler if it is set
1466
  if (call_user(conn, MG_HTTP_ERROR) == MG_TRUE) {
1467 1468 1469 1470
    close_local_endpoint(conn);
    return;
  }

1471 1472 1473
  // Handle error code rewrites
  while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
    if ((match_code = atoi(a.ptr)) > 0 && match_code == code) {
1474 1475 1476 1477 1478 1479
      struct mg_connection *c = &conn->mg_conn;
      c->status_code = 302;
      mg_printf(c, "HTTP/1.1 %d Moved\r\n"
                "Location: %.*s?code=%d&orig_uri=%s&query_string=%s\r\n\r\n",
                c->status_code, b.len, b.ptr, code, c->uri,
                c->query_string == NULL ? "" : c->query_string);
1480 1481 1482 1483
      close_local_endpoint(conn);
      return;
    }
  }
1484 1485

  body_len = mg_snprintf(body, sizeof(body), "%d %s\n", code, message);
1486 1487
  if (fmt != NULL) {
    va_start(ap, fmt);
1488
    body_len += mg_vsnprintf(body + body_len, sizeof(body) - body_len, fmt, ap);
1489 1490
    va_end(ap);
  }
1491 1492 1493 1494 1495 1496 1497 1498
  if (code >= 300 && code <= 399) {
    // 3xx errors do not have body
    body_len = 0;
  }
  headers_len = mg_snprintf(headers, sizeof(headers),
                            "HTTP/1.1 %d %s\r\nContent-Length: %d\r\n"
                            "Content-Type: text/plain\r\n\r\n",
                            code, message, body_len);
Sergey Lyubka's avatar
Sergey Lyubka committed
1499 1500
  ns_send(conn->ns_conn, headers, headers_len);
  ns_send(conn->ns_conn, body, body_len);
1501 1502 1503
  close_local_endpoint(conn);  // This will write to the log file
}

1504 1505 1506
static void write_chunk(struct connection *conn, const char *buf, int len) {
  char chunk_size[50];
  int n = mg_snprintf(chunk_size, sizeof(chunk_size), "%X\r\n", len);
Sergey Lyubka's avatar
Sergey Lyubka committed
1507 1508 1509
  ns_send(conn->ns_conn, chunk_size, n);
  ns_send(conn->ns_conn, buf, len);
  ns_send(conn->ns_conn, "\r\n", 2);
Sergey Lyubka's avatar
Sergey Lyubka committed
1510 1511 1512
}

int mg_printf(struct mg_connection *conn, const char *fmt, ...) {
Sergey Lyubka's avatar
Sergey Lyubka committed
1513
  struct connection *c = MG_CONN_2_CONN(conn);
1514
  int len;
Sergey Lyubka's avatar
Sergey Lyubka committed
1515
  va_list ap;
Sergey Lyubka's avatar
Sergey Lyubka committed
1516

Sergey Lyubka's avatar
Sergey Lyubka committed
1517
  va_start(ap, fmt);
Sergey Lyubka's avatar
Sergey Lyubka committed
1518
  len = ns_vprintf(c->ns_conn, fmt, ap);
1519
  va_end(ap);
1520

Sergey Lyubka's avatar
Sergey Lyubka committed
1521
  return len;
1522 1523
}

1524
#ifndef MONGOOSE_NO_CGI
1525
#ifdef _WIN32
Sergey Lyubka's avatar
Sergey Lyubka committed
1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579
struct threadparam {
  sock_t s;
  HANDLE hPipe;
};

static int wait_until_ready(sock_t sock, int for_read) {
  fd_set set;
  FD_ZERO(&set);
  FD_SET(sock, &set);
  select(sock + 1, for_read ? &set : 0, for_read ? 0 : &set, 0, 0);
  return 1;
}

static void *push_to_stdin(void *arg) {
  struct threadparam *tp = arg;
  int n, sent, stop = 0;
  DWORD k;
  char buf[IOBUF_SIZE];

  while (!stop && wait_until_ready(tp->s, 1) &&
         (n = recv(tp->s, buf, sizeof(buf), 0)) > 0) {
    if (n == -1 && GetLastError() == WSAEWOULDBLOCK) continue;
    for (sent = 0; !stop && sent < n; sent += k) {
      if (!WriteFile(tp->hPipe, buf + sent, n - sent, &k, 0)) stop = 1;
    }
  }
  DBG(("%s", "FORWARED EVERYTHING TO CGI"));
  CloseHandle(tp->hPipe);
  free(tp);
  _endthread();
  return NULL;
}

static void *pull_from_stdout(void *arg) {
  struct threadparam *tp = arg;
  int k, stop = 0;
  DWORD n, sent;
  char buf[IOBUF_SIZE];

  while (!stop && ReadFile(tp->hPipe, buf, sizeof(buf), &n, NULL)) {
    for (sent = 0; !stop && sent < n; sent += k) {
      if (wait_until_ready(tp->s, 0) &&
          (k = send(tp->s, buf + sent, n - sent, 0)) <= 0) stop = 1;
    }
  }
  DBG(("%s", "EOF FROM CGI"));
  CloseHandle(tp->hPipe);
  shutdown(tp->s, 2);  // Without this, IO thread may get truncated data
  closesocket(tp->s);
  free(tp);
  _endthread();
  return NULL;
}

Sergey Lyubka's avatar
Sergey Lyubka committed
1580 1581
static void spawn_stdio_thread(sock_t sock, HANDLE hPipe,
                               void *(*func)(void *)) {
Sergey Lyubka's avatar
Sergey Lyubka committed
1582 1583 1584 1585 1586 1587 1588 1589
  struct threadparam *tp = malloc(sizeof(*tp));
  if (tp != NULL) {
    tp->s = sock;
    tp->hPipe = hPipe;
    mg_start_thread(func, tp);
  }
}

1590 1591 1592 1593 1594 1595 1596
static void abs_path(const char *utf8_path, char *abs_path, size_t len) {
  wchar_t buf[MAX_PATH_SIZE], buf2[MAX_PATH_SIZE];
  to_wchar(utf8_path, buf, ARRAY_SIZE(buf));
  GetFullPathNameW(buf, ARRAY_SIZE(buf2), buf2, NULL);
  WideCharToMultiByte(CP_UTF8, 0, buf2, wcslen(buf2) + 1, abs_path, len, 0, 0);
}

1597 1598
static pid_t start_process(char *interp, const char *cmd, const char *env,
                           const char *envp[], const char *dir, sock_t sock) {
1599
  STARTUPINFOW si = {0};
1600
  PROCESS_INFORMATION pi = {0};
Sergey Lyubka's avatar
Sergey Lyubka committed
1601
  HANDLE a[2], b[2], me = GetCurrentProcess();
1602 1603 1604
  wchar_t wcmd[MAX_PATH_SIZE], full_dir[MAX_PATH_SIZE];
  char buf[MAX_PATH_SIZE], buf4[MAX_PATH_SIZE], buf5[MAX_PATH_SIZE],
       cmdline[MAX_PATH_SIZE], *p;
Sergey Lyubka's avatar
Sergey Lyubka committed
1605
  DWORD flags = DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS;
1606
  FILE *fp;
1607

1608 1609 1610 1611
  si.cb = sizeof(si);
  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  si.wShowWindow = SW_HIDE;
  si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
Sergey Lyubka's avatar
Sergey Lyubka committed
1612 1613 1614 1615 1616

  CreatePipe(&a[0], &a[1], NULL, 0);
  CreatePipe(&b[0], &b[1], NULL, 0);
  DuplicateHandle(me, a[0], me, &si.hStdInput, 0, TRUE, flags);
  DuplicateHandle(me, b[1], me, &si.hStdOutput, 0, TRUE, flags);
1617

1618 1619 1620 1621 1622 1623 1624 1625
  if (interp == NULL && (fp = fopen(cmd, "r")) != NULL) {
    buf[0] = buf[1] = '\0';
    fgets(buf, sizeof(buf), fp);
    buf[sizeof(buf) - 1] = '\0';
    if (buf[0] == '#' && buf[1] == '!') {
      interp = buf + 2;
      for (p = interp + strlen(interp);
           isspace(* (uint8_t *) p) && p > interp; p--) *p = '\0';
1626
    }
1627
    fclose(fp);
1628 1629
  }

1630
  if (interp != NULL) {
1631
    abs_path(interp, buf4, ARRAY_SIZE(buf4));
1632
    interp = buf4;
1633
  }
1634 1635
  abs_path(dir, buf5, ARRAY_SIZE(buf5));
  to_wchar(dir, full_dir, ARRAY_SIZE(full_dir));
1636 1637
  mg_snprintf(cmdline, sizeof(cmdline), "%s%s\"%s\"",
              interp ? interp : "", interp ? " " : "", cmd);
1638
  to_wchar(cmdline, wcmd, ARRAY_SIZE(wcmd));
1639

1640 1641
  if (CreateProcessW(NULL, wcmd, NULL, NULL, TRUE, CREATE_NEW_PROCESS_GROUP,
                     (void *) env, full_dir, &si, &pi) != 0) {
Sergey Lyubka's avatar
Sergey Lyubka committed
1642 1643 1644 1645 1646 1647 1648
    spawn_stdio_thread(sock, a[1], push_to_stdin);
    spawn_stdio_thread(sock, b[0], pull_from_stdout);
  } else {
    CloseHandle(a[1]);
    CloseHandle(b[0]);
    closesocket(sock);
  }
1649
  DBG(("CGI command: [%ls] -> %p", wcmd, pi.hProcess));
1650 1651 1652

  CloseHandle(si.hStdOutput);
  CloseHandle(si.hStdInput);
Sergey Lyubka's avatar
Sergey Lyubka committed
1653 1654
  CloseHandle(a[0]);
  CloseHandle(b[1]);
1655
  CloseHandle(pi.hThread);
Sergey Lyubka's avatar
Sergey Lyubka committed
1656
  CloseHandle(pi.hProcess);
1657

Sergey Lyubka's avatar
Sergey Lyubka committed
1658
  return (pid_t) pi.hProcess;
1659
}
1660 1661 1662
#else
static pid_t start_process(const char *interp, const char *cmd, const char *env,
                           const char *envp[], const char *dir, sock_t sock) {
1663
  char buf[500];
1664 1665 1666 1667
  pid_t pid = fork();
  (void) env;

  if (pid == 0) {
1668 1669 1670
    (void) chdir(dir);
    (void) dup2(sock, 0);
    (void) dup2(sock, 1);
1671
    closesocket(sock);
1672

1673 1674 1675 1676 1677
    // After exec, all signal handlers are restored to their default values,
    // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
    // implementation, SIGCHLD's handler will leave unchanged after exec
    // if it was set to be ignored. Restore it to default action.
    signal(SIGCHLD, SIG_DFL);
1678

1679 1680
    if (interp == NULL) {
      execle(cmd, cmd, NULL, envp);
1681
    } else {
1682
      execle(interp, interp, cmd, NULL, envp);
1683
    }
1684 1685 1686 1687
    snprintf(buf, sizeof(buf), "Status: 500\r\n\r\n"
             "500 Server Error: %s%s%s: %s", interp == NULL ? "" : interp,
             interp == NULL ? "" : " ", cmd, strerror(errno));
    send(1, buf, strlen(buf), 0);
1688
    exit(EXIT_FAILURE);  // exec call failed
1689 1690
  }

1691
  return pid;
1692
}
1693
#endif  // _WIN32
1694

1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
// This structure helps to create an environment for the spawned CGI program.
// Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings,
// last element must be NULL.
// However, on Windows there is a requirement that all these VARIABLE=VALUE\0
// strings must reside in a contiguous buffer. The end of the buffer is
// marked by two '\0' characters.
// We satisfy both worlds: we create an envp array (which is vars), all
// entries are actually pointers inside buf.
struct cgi_env_block {
  struct mg_connection *conn;
  char buf[CGI_ENVIRONMENT_SIZE];       // Environment buffer
  const char *vars[MAX_CGI_ENVIR_VARS]; // char *envp[]
  int len;                              // Space taken
  int nvars;                            // Number of variables in envp[]
};
1710

1711 1712 1713 1714 1715 1716
// Append VARIABLE=VALUE\0 string to the buffer, and add a respective
// pointer into the vars array.
static char *addenv(struct cgi_env_block *block, const char *fmt, ...) {
  int n, space;
  char *added;
  va_list ap;
1717

1718 1719 1720
  // Calculate how much space is left in the buffer
  space = sizeof(block->buf) - block->len - 2;
  assert(space >= 0);
1721

1722 1723
  // Make a pointer to the free space int the buffer
  added = block->buf + block->len;
1724

1725 1726 1727 1728
  // Copy VARIABLE=VALUE\0 string into the free space
  va_start(ap, fmt);
  n = mg_vsnprintf(added, (size_t) space, fmt, ap);
  va_end(ap);
1729

1730 1731 1732 1733 1734 1735 1736
  // Make sure we do not overflow buffer and the envp array
  if (n > 0 && n + 1 < space &&
      block->nvars < (int) ARRAY_SIZE(block->vars) - 2) {
    // Append a pointer to the added string into the envp array
    block->vars[block->nvars++] = added;
    // Bump up used length counter. Include \0 terminator
    block->len += n + 1;
1737 1738
  }

1739
  return added;
1740 1741
}

1742 1743 1744
static void addenv2(struct cgi_env_block *blk, const char *name) {
  const char *s;
  if ((s = getenv(name)) != NULL) addenv(blk, "%s=%s", name, s);
1745 1746
}

1747 1748 1749 1750 1751 1752 1753
static void prepare_cgi_environment(struct connection *conn,
                                    const char *prog,
                                    struct cgi_env_block *blk) {
  struct mg_connection *ri = &conn->mg_conn;
  const char *s, *slash;
  char *p, **opts = conn->server->config_options;
  int  i;
1754

1755 1756
  blk->len = blk->nvars = 0;
  blk->conn = ri;
1757

1758 1759 1760 1761 1762
  if ((s = getenv("SERVER_NAME")) != NULL) {
    addenv(blk, "SERVER_NAME=%s", s);
  } else {
    addenv(blk, "SERVER_NAME=%s", conn->server->local_ip);
  }
1763 1764 1765
  addenv(blk, "SERVER_ROOT=%s", opts[DOCUMENT_ROOT]);
  addenv(blk, "DOCUMENT_ROOT=%s", opts[DOCUMENT_ROOT]);
  addenv(blk, "SERVER_SOFTWARE=%s/%s", "Mongoose", MONGOOSE_VERSION);
1766

1767 1768 1769 1770
  // Prepare the environment block
  addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
  addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
  addenv(blk, "%s", "REDIRECT_STATUS=200"); // For PHP
1771

1772
  // TODO(lsm): fix this for IPv6 case
1773
  //addenv(blk, "SERVER_PORT=%d", ri->remote_port);
1774

1775 1776 1777 1778 1779 1780
  addenv(blk, "REQUEST_METHOD=%s", ri->request_method);
  addenv(blk, "REMOTE_ADDR=%s", ri->remote_ip);
  addenv(blk, "REMOTE_PORT=%d", ri->remote_port);
  addenv(blk, "REQUEST_URI=%s%s%s", ri->uri,
         ri->query_string == NULL ? "" : "?",
         ri->query_string == NULL ? "" : ri->query_string);
1781

1782 1783 1784 1785 1786
  // SCRIPT_NAME
  if (conn->path_info != NULL) {
    addenv(blk, "SCRIPT_NAME=%.*s",
           (int) (strlen(ri->uri) - strlen(conn->path_info)), ri->uri);
    addenv(blk, "PATH_INFO=%s", conn->path_info);
1787
  } else {
1788 1789 1790 1791 1792
    s = strrchr(prog, '/');
    slash = strrchr(ri->uri, '/');
    addenv(blk, "SCRIPT_NAME=%.*s%s",
           slash == NULL ? 0 : (int) (slash - ri->uri), ri->uri,
           s == NULL ? prog : s);
1793 1794
  }

1795 1796
  addenv(blk, "SCRIPT_FILENAME=%s", prog);
  addenv(blk, "PATH_TRANSLATED=%s", prog);
Sergey Lyubka's avatar
Sergey Lyubka committed
1797
  addenv(blk, "HTTPS=%s", conn->ns_conn->ssl != NULL ? "on" : "off");
1798

1799 1800
  if ((s = mg_get_header(ri, "Content-Type")) != NULL)
    addenv(blk, "CONTENT_TYPE=%s", s);
1801

1802 1803
  if (ri->query_string != NULL)
    addenv(blk, "QUERY_STRING=%s", ri->query_string);
1804

1805 1806
  if ((s = mg_get_header(ri, "Content-Length")) != NULL)
    addenv(blk, "CONTENT_LENGTH=%s", s);
1807

1808
  addenv2(blk, "PATH");
1809 1810
  addenv2(blk, "TMP");
  addenv2(blk, "TEMP");
1811
  addenv2(blk, "TMPDIR");
1812 1813
  addenv2(blk, "PERLLIB");
  addenv2(blk, ENV_EXPORT_TO_CGI);
1814

1815 1816 1817 1818 1819 1820 1821 1822 1823 1824
#if defined(_WIN32)
  addenv2(blk, "COMSPEC");
  addenv2(blk, "SYSTEMROOT");
  addenv2(blk, "SystemDrive");
  addenv2(blk, "ProgramFiles");
  addenv2(blk, "ProgramFiles(x86)");
  addenv2(blk, "CommonProgramFiles(x86)");
#else
  addenv2(blk, "LD_LIBRARY_PATH");
#endif // _WIN32
1825

1826 1827 1828 1829
  // Add all headers as HTTP_* variables
  for (i = 0; i < ri->num_headers; i++) {
    p = addenv(blk, "HTTP_%s=%s",
        ri->http_headers[i].name, ri->http_headers[i].value);
1830

1831 1832 1833 1834 1835
    // Convert variable name into uppercase, and change - to _
    for (; *p != '=' && *p != '\0'; p++) {
      if (*p == '-')
        *p = '_';
      *p = (char) toupper(* (unsigned char *) p);
1836 1837 1838
    }
  }

1839 1840
  blk->vars[blk->nvars++] = NULL;
  blk->buf[blk->len++] = '\0';
1841

1842 1843 1844
  assert(blk->nvars < (int) ARRAY_SIZE(blk->vars));
  assert(blk->len > 0);
  assert(blk->len < (int) sizeof(blk->buf));
1845 1846
}

1847
static const char cgi_status[] = "HTTP/1.1 200 OK\r\n";
Sergey Lyubka's avatar
Sergey Lyubka committed
1848

1849 1850
static void open_cgi_endpoint(struct connection *conn, const char *prog) {
  struct cgi_env_block blk;
1851 1852
  char dir[MAX_PATH_SIZE];
  const char *p;
1853 1854 1855 1856 1857 1858
  sock_t fds[2];

  prepare_cgi_environment(conn, prog, &blk);
  // CGI must be executed in its own directory. 'dir' must point to the
  // directory containing executable program, 'p' must point to the
  // executable program name relative to 'dir'.
1859
  if ((p = strrchr(prog, '/')) == NULL) {
Sergey Lyubka's avatar
Sergey Lyubka committed
1860
    mg_snprintf(dir, sizeof(dir), "%s", ".");
1861
  } else {
1862
    mg_snprintf(dir, sizeof(dir), "%.*s", (int) (p - prog), prog);
1863 1864
  }

Sergey Lyubka's avatar
Sergey Lyubka committed
1865
  // Try to create socketpair in a loop until success. ns_socketpair()
1866 1867 1868
  // can be interrupted by a signal and fail.
  // TODO(lsm): use sigaction to restart interrupted syscall
  do {
Sergey Lyubka's avatar
Sergey Lyubka committed
1869
    ns_socketpair(fds);
1870
  } while (fds[0] == INVALID_SOCKET);
1871

1872 1873 1874
  if (start_process(conn->server->config_options[CGI_INTERPRETER],
                    prog, blk.buf, blk.vars, dir, fds[1]) > 0) {
    conn->endpoint_type = EP_CGI;
Sergey Lyubka's avatar
Sergey Lyubka committed
1875 1876 1877 1878
    conn->endpoint.cgi_conn = ns_add_sock(&conn->server->ns_server,
                                          fds[0], conn);
    conn->endpoint.cgi_conn->flags |= MG_CGI_CONN;
    ns_send(conn->ns_conn, cgi_status, sizeof(cgi_status) - 1);
1879
    conn->mg_conn.status_code = 200;
Sergey Lyubka's avatar
Sergey Lyubka committed
1880
    conn->ns_conn->flags |= NSF_BUFFER_BUT_DONT_SEND;
1881 1882 1883
    // Pass POST data to the CGI process
    conn->endpoint.cgi_conn->send_iobuf = conn->ns_conn->recv_iobuf;
    iobuf_init(&conn->ns_conn->recv_iobuf, 0);
1884 1885
  } else {
    closesocket(fds[0]);
1886
    send_http_error(conn, 500, "start_process(%s) failed", prog);
1887
  }
1888

Sergey Lyubka's avatar
Sergey Lyubka committed
1889 1890 1891
#ifndef _WIN32
  closesocket(fds[1]);  // On Windows, CGI stdio thread closes that socket
#endif
1892
}
1893

Sergey Lyubka's avatar
Sergey Lyubka committed
1894 1895
static void on_cgi_data(struct ns_connection *nc) {
  struct connection *conn = (struct connection *) nc->connection_data;
Sergey Lyubka's avatar
Sergey Lyubka committed
1896 1897
  const char *status = "500";
  struct mg_connection c;
1898

Sergey Lyubka's avatar
Sergey Lyubka committed
1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925
  if (!conn) return;

  // Copy CGI data from CGI socket to the client send buffer
  ns_send(conn->ns_conn, nc->recv_iobuf.buf, nc->recv_iobuf.len);
  iobuf_remove(&nc->recv_iobuf, nc->recv_iobuf.len);

  // If reply has not been parsed yet, parse it
  if (conn->ns_conn->flags & NSF_BUFFER_BUT_DONT_SEND) {
    struct iobuf *io = &conn->ns_conn->send_iobuf;
    int s_len = sizeof(cgi_status) - 1;
    int len = get_request_len(io->buf + s_len, io->len - s_len);
    char buf[MAX_REQUEST_SIZE], *s = buf;

    if (len == 0) return;

    if (len < 0 || len > (int) sizeof(buf)) {
      iobuf_remove(io, io->len);
      send_http_error(conn, 500, "%s", "CGI program sent malformed headers");
    } else {
      memset(&c, 0, sizeof(c));
      memcpy(buf, io->buf + s_len, len);
      buf[len - 1] = '\0';
      parse_http_headers(&s, &c);
      if (mg_get_header(&c, "Location") != NULL) {
        status = "302";
      } else if ((status = (char *) mg_get_header(&c, "Status")) == NULL) {
        status = "200";
1926
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
1927
      memcpy(io->buf + 9, status, 3);
1928
      conn->mg_conn.status_code = atoi(status);
1929
    }
Sergey Lyubka's avatar
Sergey Lyubka committed
1930
    conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
1931 1932 1933
  }
}

1934
static void forward_post_data(struct connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
1935 1936 1937 1938
  struct iobuf *io = &conn->ns_conn->recv_iobuf;
  if (conn->endpoint.cgi_conn != NULL) {
    ns_send(conn->endpoint.cgi_conn, io->buf, io->len);
    iobuf_remove(io, io->len);
1939
  }
1940
}
Sergey Lyubka's avatar
Sergey Lyubka committed
1941
#endif  // !MONGOOSE_NO_CGI
1942

1943 1944 1945 1946
static char *mg_strdup(const char *str) {
  char *copy = (char *) malloc(strlen(str) + 1);
  if (copy != NULL) {
    strcpy(copy, str);
1947
  }
1948
  return copy;
1949 1950
}

1951 1952 1953
static int isbyte(int n) {
  return n >= 0 && n <= 255;
}
1954

1955 1956
static int parse_net(const char *spec, uint32_t *net, uint32_t *mask) {
  int n, a, b, c, d, slash = 32, len = 0;
1957

1958 1959 1960 1961 1962 1963 1964 1965
  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) | d;
    *mask = slash ? 0xffffffffU << (32 - slash) : 0;
  }
1966

1967 1968
  return len;
}
1969

1970 1971 1972 1973 1974 1975
// Verify given socket address against the ACL.
// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
static int check_acl(const char *acl, uint32_t remote_ip) {
  int allowed, flag;
  uint32_t net, mask;
  struct vec vec;
1976

1977 1978
  // If any ACL is set, deny by default
  allowed = acl == NULL ? '+' : '-';
1979

1980 1981 1982 1983 1984
  while ((acl = next_option(acl, &vec, NULL)) != NULL) {
    flag = vec.ptr[0];
    if ((flag != '+' && flag != '-') ||
        parse_net(&vec.ptr[1], &net, &mask) == 0) {
      return -1;
1985
    }
1986

1987 1988
    if (net == (remote_ip & mask)) {
      allowed = flag;
1989
    }
1990 1991
  }

1992
  return allowed == '+';
1993 1994
}

1995 1996 1997
static void sockaddr_to_string(char *buf, size_t len,
                               const union socket_address *usa) {
  buf[0] = '\0';
Sergey Lyubka's avatar
Sergey Lyubka committed
1998
#if defined(NS_ENABLE_IPV6)
1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
  inet_ntop(usa->sa.sa_family, usa->sa.sa_family == AF_INET ?
            (void *) &usa->sin.sin_addr :
            (void *) &usa->sin6.sin6_addr, buf, len);
#elif defined(_WIN32)
  // Only Windoze Vista (and newer) have inet_ntop()
  strncpy(buf, inet_ntoa(usa->sin.sin_addr), len);
#else
  inet_ntop(usa->sa.sa_family, (void *) &usa->sin.sin_addr, buf, len);
#endif
}

2010 2011 2012 2013
// Protect against directory disclosure attack by removing '..',
// excessive '/' and '\' characters
static void remove_double_dots_and_double_slashes(char *s) {
  char *p = s;
2014

2015 2016 2017 2018 2019 2020 2021 2022 2023
  while (*s != '\0') {
    *p++ = *s++;
    if (s[-1] == '/' || s[-1] == '\\') {
      // Skip all following slashes, backslashes and double-dots
      while (s[0] != '\0') {
        if (s[0] == '/' || s[0] == '\\') { s++; }
        else if (s[0] == '.' && s[1] == '.') { s += 2; }
        else { break; }
      }
2024 2025
    }
  }
2026 2027 2028 2029 2030 2031 2032
  *p = '\0';
}

int mg_url_decode(const char *src, int src_len, char *dst,
                  int dst_len, int is_form_url_encoded) {
  int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
2033

2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046
  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) {
    if (src[i] == '%' && i < src_len - 2 &&
        isxdigit(* (const unsigned char *) (src + i + 1)) &&
        isxdigit(* (const unsigned char *) (src + i + 2))) {
      a = tolower(* (const unsigned char *) (src + i + 1));
      b = tolower(* (const unsigned char *) (src + i + 2));
      dst[j] = (char) ((HEXTOI(a) << 4) | HEXTOI(b));
      i += 2;
    } else if (is_form_url_encoded && src[i] == '+') {
      dst[j] = ' ';
    } else {
      dst[j] = src[i];
    }
2047 2048
  }

2049
  dst[j] = '\0'; // Null-terminate the destination
2050

2051
  return i >= src_len ? j : -1;
2052 2053
}

2054 2055 2056 2057 2058 2059 2060
static int is_valid_http_method(const char *method) {
  return !strcmp(method, "GET") || !strcmp(method, "POST") ||
    !strcmp(method, "HEAD") || !strcmp(method, "CONNECT") ||
    !strcmp(method, "PUT") || !strcmp(method, "DELETE") ||
    !strcmp(method, "OPTIONS") || !strcmp(method, "PROPFIND")
    || !strcmp(method, "MKCOL");
}
2061

2062 2063 2064 2065 2066 2067 2068
// Parse HTTP request, fill in mg_request structure.
// This function modifies the buffer by NUL-terminating
// HTTP request components, header names and header values.
// Note that len must point to the last \n of HTTP headers.
static int parse_http_message(char *buf, int len, struct mg_connection *ri) {
  int is_request, n;

2069 2070 2071 2072 2073
  // Reset the connection. Make sure that we don't touch fields that are
  // set elsewhere: remote_ip, remote_port, server_param
  ri->request_method = ri->uri = ri->http_version = ri->query_string = NULL;
  ri->num_headers = ri->status_code = ri->is_websocket = ri->content_len = 0;

2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094
  buf[len - 1] = '\0';

  // RFC says that all initial whitespaces should be ingored
  while (*buf != '\0' && isspace(* (unsigned char *) buf)) {
    buf++;
  }
  ri->request_method = skip(&buf, " ");
  ri->uri = skip(&buf, " ");
  ri->http_version = skip(&buf, "\r\n");

  // HTTP message could be either HTTP request or HTTP response, e.g.
  // "GET / HTTP/1.0 ...." or  "HTTP/1.0 200 OK ..."
  is_request = is_valid_http_method(ri->request_method);
  if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) ||
      (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) {
    len = -1;
  } else {
    if (is_request) {
      ri->http_version += 5;
    }
    parse_http_headers(&buf, ri);
2095

2096 2097 2098 2099 2100 2101
    if ((ri->query_string = strchr(ri->uri, '?')) != NULL) {
      *(char *) ri->query_string++ = '\0';
    }
    n = (int) strlen(ri->uri);
    mg_url_decode(ri->uri, n, (char *) ri->uri, n + 1, 0);
    remove_double_dots_and_double_slashes((char *) ri->uri);
2102 2103
  }

2104 2105
  return len;
}
2106

2107 2108 2109
static int lowercase(const char *s) {
  return tolower(* (const unsigned char *) s);
}
2110

2111 2112
static int mg_strcasecmp(const char *s1, const char *s2) {
  int diff;
2113

2114 2115 2116
  do {
    diff = lowercase(s1++) - lowercase(s2++);
  } while (diff == 0 && s1[-1] != '\0');
2117

2118 2119
  return diff;
}
2120

2121 2122
static int mg_strncasecmp(const char *s1, const char *s2, size_t len) {
  int diff = 0;
2123

2124 2125 2126 2127
  if (len > 0)
    do {
      diff = lowercase(s1++) - lowercase(s2++);
    } while (diff == 0 && s1[-1] != '\0' && --len > 0);
2128

2129
  return diff;
2130 2131
}

Sergey Lyubka's avatar
Sergey Lyubka committed
2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142
// Return HTTP header value, or NULL if not found.
const char *mg_get_header(const struct mg_connection *ri, const char *s) {
  int i;

  for (i = 0; i < ri->num_headers; i++)
    if (!mg_strcasecmp(s, ri->http_headers[i].name))
      return ri->http_headers[i].value;

  return NULL;
}

2143
#ifndef MONGOOSE_NO_FILESYSTEM
2144
// Perform case-insensitive match of string against pattern
2145
int mg_match_prefix(const char *pattern, int pattern_len, const char *str) {
2146
  const char *or_str;
Sergey Lyubka's avatar
Sergey Lyubka committed
2147
  int len, res, i = 0, j = 0;
2148

2149
  if ((or_str = (const char *) memchr(pattern, '|', pattern_len)) != NULL) {
2150 2151 2152
    res = mg_match_prefix(pattern, or_str - pattern, str);
    return res > 0 ? res : mg_match_prefix(or_str + 1,
      (pattern + pattern_len) - (or_str + 1), str);
2153 2154
  }

2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169
  for (; i < pattern_len; i++, j++) {
    if (pattern[i] == '?' && str[j] != '\0') {
      continue;
    } else if (pattern[i] == '$') {
      return str[j] == '\0' ? j : -1;
    } else if (pattern[i] == '*') {
      i++;
      if (pattern[i] == '*') {
        i++;
        len = (int) strlen(str + j);
      } else {
        len = (int) strcspn(str + j, "/");
      }
      if (i == pattern_len) {
        return j + len;
2170
      }
2171
      do {
2172
        res = mg_match_prefix(pattern + i, pattern_len - i, str + j + len);
2173 2174 2175 2176
      } while (res == -1 && len-- > 0);
      return res == -1 ? -1 : j + res + len;
    } else if (lowercase(&pattern[i]) != lowercase(&str[j])) {
      return -1;
2177
    }
2178 2179 2180
  }
  return j;
}
2181

2182 2183 2184
static int must_hide_file(struct connection *conn, const char *path) {
  const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$";
  const char *pattern = conn->server->config_options[HIDE_FILES_PATTERN];
2185 2186
  return mg_match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 ||
    (pattern != NULL && mg_match_prefix(pattern, strlen(pattern), path) > 0);
2187 2188
}

2189 2190 2191 2192
// Return 1 if real file has been found, 0 otherwise
static int convert_uri_to_file_name(struct connection *conn, char *buf,
                                    size_t buf_len, file_stat_t *st) {
  struct vec a, b;
Sergey Lyubka's avatar
Sergey Lyubka committed
2193 2194
  const char *rewrites = conn->server->config_options[URL_REWRITES];
  const char *root = conn->server->config_options[DOCUMENT_ROOT];
2195
#ifndef MONGOOSE_NO_CGI
Sergey Lyubka's avatar
Sergey Lyubka committed
2196
  const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
2197
  char *p;
2198 2199
#endif
  const char *uri = conn->mg_conn.uri;
2200 2201 2202 2203 2204 2205
  const char *domain = mg_get_header(&conn->mg_conn, "Host");
  int match_len, root_len = root == NULL ? 0 : strlen(root);

  // Perform virtual hosting rewrites
  if (rewrites != NULL && domain != NULL) {
    const char *colon = strchr(domain, ':');
2206
    int domain_len = colon == NULL ? (int) strlen(domain) : colon - domain;
2207 2208 2209 2210 2211 2212 2213 2214 2215 2216

    while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
      if (a.len > 1 && a.ptr[0] == '@' && a.len == domain_len + 1 &&
          mg_strncasecmp(a.ptr + 1, domain, domain_len) == 0) {
        root = b.ptr;
        root_len = b.len;
        break;
      }
    }
  }
2217

2218
  // No filesystem access
2219
  if (root == NULL || root_len == 0) return 0;
2220

2221
  // Handle URL rewrites
2222
  mg_snprintf(buf, buf_len, "%.*s%s", root_len, root, uri);
2223
  rewrites = conn->server->config_options[URL_REWRITES];  // Re-initialize!
2224
  while ((rewrites = next_option(rewrites, &a, &b)) != NULL) {
2225
    if ((match_len = mg_match_prefix(a.ptr, a.len, uri)) > 0) {
2226
      mg_snprintf(buf, buf_len, "%.*s%s", (int) b.len, b.ptr, uri + match_len);
2227 2228
      break;
    }
2229
  }
2230

2231
  if (stat(buf, st) == 0) return 1;
2232

2233
#ifndef MONGOOSE_NO_CGI
2234 2235 2236 2237
  // Support PATH_INFO for CGI scripts.
  for (p = buf + strlen(root) + 2; *p != '\0'; p++) {
    if (*p == '/') {
      *p = '\0';
2238 2239
      if (mg_match_prefix(cgi_pat, strlen(cgi_pat), buf) > 0 &&
          !stat(buf, st)) {
2240 2241 2242 2243 2244 2245 2246
      DBG(("!!!! [%s]", buf));
        *p = '/';
        conn->path_info = mg_strdup(p);
        *p = '\0';
        return 1;
      }
      *p = '/';
2247 2248
    }
  }
2249
#endif
2250

2251
  return 0;
2252
}
2253
#endif  // MONGOOSE_NO_FILESYSTEM
2254

2255
static int should_keep_alive(const struct mg_connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2256
  struct connection *c = MG_CONN_2_CONN(conn);
2257 2258 2259
  const char *method = conn->request_method;
  const char *http_version = conn->http_version;
  const char *header = mg_get_header(conn, "Connection");
Sergey Lyubka's avatar
Sergey Lyubka committed
2260 2261
  return method != NULL &&
    (!strcmp(method, "GET") || c->endpoint_type == EP_USER) &&
2262 2263 2264 2265
    ((header != NULL && !mg_strcasecmp(header, "keep-alive")) ||
     (header == NULL && http_version && !strcmp(http_version, "1.1")));
}

2266
int mg_write(struct mg_connection *c, const void *buf, int len) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2267 2268
  struct connection *conn = MG_CONN_2_CONN(c);
  return ns_send(conn->ns_conn, buf, len);
2269
}
2270

2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286
void mg_send_status(struct mg_connection *c, int status) {
  if (c->status_code == 0) {
    c->status_code = status;
    mg_printf(c, "HTTP/1.1 %d %s\r\n", status, status_code_to_str(status));
  }
}

void mg_send_header(struct mg_connection *c, const char *name, const char *v) {
  if (c->status_code == 0) {
    c->status_code = 200;
    mg_printf(c, "HTTP/1.1 %d %s\r\n", 200, status_code_to_str(200));
  }
  mg_printf(c, "%s: %s\r\n", name, v);
}

static void terminate_headers(struct mg_connection *c) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2287 2288
  struct connection *conn = MG_CONN_2_CONN(c);
  if (!(conn->ns_conn->flags & MG_HEADERS_SENT)) {
2289 2290
    mg_send_header(c, "Transfer-Encoding", "chunked");
    mg_write(c, "\r\n", 2);
Sergey Lyubka's avatar
Sergey Lyubka committed
2291
    conn->ns_conn->flags |= MG_HEADERS_SENT;
2292 2293 2294 2295 2296
  }
}

void mg_send_data(struct mg_connection *c, const void *data, int data_len) {
  terminate_headers(c);
Sergey Lyubka's avatar
Sergey Lyubka committed
2297
  write_chunk(MG_CONN_2_CONN(c), (const char *) data, data_len);
2298 2299 2300
}

void mg_printf_data(struct mg_connection *c, const char *fmt, ...) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2301
  struct connection *conn = MG_CONN_2_CONN(c);
2302
  va_list ap;
2303 2304
  int len;
  char mem[IOBUF_SIZE], *buf = mem;
2305 2306 2307 2308

  terminate_headers(c);

  va_start(ap, fmt);
2309
  len = ns_avprintf(&buf, sizeof(mem), fmt, ap);
2310
  va_end(ap);
2311

2312 2313 2314 2315 2316 2317
  if (len > 0) {
    write_chunk((struct connection *) conn, buf, len);
  }
  if (buf != mem && buf != NULL) {
    free(buf);
  }
2318 2319
}

2320
#if !defined(MONGOOSE_NO_WEBSOCKET) || !defined(MONGOOSE_NO_AUTH)
2321 2322 2323 2324 2325
static int is_big_endian(void) {
  static const int n = 1;
  return ((char *) &n)[0] == 0;
}
#endif
2326

2327
#ifndef MONGOOSE_NO_WEBSOCKET
2328 2329 2330 2331 2332
// START OF SHA-1 code
// Copyright(c) By Steve Reid <steve@edmweb.com>
#define SHA1HANDSOFF
#if defined(__sun)
#include "solarisfixes.h"
2333
#endif
2334

2335
union char64long16 { unsigned char c[64]; uint32_t l[16]; };
2336

2337
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
2338

2339 2340 2341 2342 2343 2344 2345 2346
static uint32_t blk0(union char64long16 *block, int i) {
  // Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN
  if (!is_big_endian()) {
    block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
      (rol(block->l[i], 8) & 0x00FF00FF);
  }
  return block->l[i];
}
2347

2348 2349 2350 2351 2352 2353 2354
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
    ^block->l[(i+2)&15]^block->l[i&15],1))
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(block, i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
2355

2356 2357 2358 2359 2360
typedef struct {
    uint32_t state[5];
    uint32_t count[2];
    unsigned char buffer[64];
} SHA1_CTX;
2361

2362 2363 2364
static void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) {
  uint32_t a, b, c, d, e;
  union char64long16 block[1];
2365

2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396
  memcpy(block, buffer, 64);
  a = state[0];
  b = state[1];
  c = state[2];
  d = state[3];
  e = state[4];
  R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
  R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
  R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
  R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
  R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
  R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
  R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
  R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
  R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
  R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
  R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
  R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
  R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
  R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
  R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
  R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
  R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
  R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
  R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
  R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;
  state[4] += e;
2397 2398
  // Erase working structures. The order of operations is important,
  // used to ensure that compiler doesn't optimize those out.
Sergey Lyubka's avatar
Sergey Lyubka committed
2399
  memset(block, 0, sizeof(block));
Sergey Lyubka's avatar
Sergey Lyubka committed
2400
  a = b = c = d = e = 0;
Sergey Lyubka's avatar
Sergey Lyubka committed
2401
  (void) a; (void) b; (void) c; (void) d; (void) e;
2402 2403
}

2404 2405 2406 2407 2408 2409 2410
static void SHA1Init(SHA1_CTX* context) {
  context->state[0] = 0x67452301;
  context->state[1] = 0xEFCDAB89;
  context->state[2] = 0x98BADCFE;
  context->state[3] = 0x10325476;
  context->state[4] = 0xC3D2E1F0;
  context->count[0] = context->count[1] = 0;
2411 2412
}

2413 2414 2415
static void SHA1Update(SHA1_CTX* context, const unsigned char* data,
                       uint32_t len) {
  uint32_t i, j;
2416

2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431
  j = context->count[0];
  if ((context->count[0] += len << 3) < j)
    context->count[1]++;
  context->count[1] += (len>>29);
  j = (j >> 3) & 63;
  if ((j + len) > 63) {
    memcpy(&context->buffer[j], data, (i = 64-j));
    SHA1Transform(context->state, context->buffer);
    for ( ; i + 63 < len; i += 64) {
      SHA1Transform(context->state, &data[i]);
    }
    j = 0;
  }
  else i = 0;
  memcpy(&context->buffer[j], &data[i], len - i);
2432 2433
}

2434 2435 2436
static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) {
  unsigned i;
  unsigned char finalcount[8], c;
2437

2438 2439 2440
  for (i = 0; i < 8; i++) {
    finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
                                     >> ((3-(i & 3)) * 8) ) & 255);
2441
  }
2442 2443 2444 2445 2446 2447 2448 2449 2450 2451
  c = 0200;
  SHA1Update(context, &c, 1);
  while ((context->count[0] & 504) != 448) {
    c = 0000;
    SHA1Update(context, &c, 1);
  }
  SHA1Update(context, finalcount, 8);
  for (i = 0; i < 20; i++) {
    digest[i] = (unsigned char)
      ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
2452
  }
2453 2454
  memset(context, '\0', sizeof(*context));
  memset(&finalcount, '\0', sizeof(finalcount));
2455
}
2456
// END OF SHA1 CODE
2457

2458 2459 2460 2461
static void base64_encode(const unsigned char *src, int src_len, char *dst) {
  static const char *b64 =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  int i, j, a, b, c;
2462

2463 2464 2465 2466
  for (i = j = 0; i < src_len; i += 3) {
    a = src[i];
    b = i + 1 >= src_len ? 0 : src[i + 1];
    c = i + 2 >= src_len ? 0 : src[i + 2];
2467

2468 2469 2470 2471 2472 2473 2474
    dst[j++] = b64[a >> 2];
    dst[j++] = b64[((a & 3) << 4) | (b >> 4)];
    if (i + 1 < src_len) {
      dst[j++] = b64[(b & 15) << 2 | (c >> 6)];
    }
    if (i + 2 < src_len) {
      dst[j++] = b64[c & 63];
2475 2476
    }
  }
2477 2478
  while (j % 4 != 0) {
    dst[j++] = '=';
2479
  }
2480 2481
  dst[j++] = '\0';
}
2482

2483 2484
static void send_websocket_handshake(struct mg_connection *conn,
                                     const char *key) {
2485
  static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
2486
  char buf[500], sha[20], b64_sha[sizeof(sha) * 2];
2487 2488
  SHA1_CTX sha_ctx;

2489
  mg_snprintf(buf, sizeof(buf), "%s%s", key, magic);
2490 2491 2492 2493
  SHA1Init(&sha_ctx);
  SHA1Update(&sha_ctx, (unsigned char *) buf, strlen(buf));
  SHA1Final((unsigned char *) sha, &sha_ctx);
  base64_encode((unsigned char *) sha, sizeof(sha), b64_sha);
2494 2495 2496 2497 2498 2499 2500 2501 2502 2503
  mg_snprintf(buf, sizeof(buf), "%s%s%s",
              "HTTP/1.1 101 Switching Protocols\r\n"
              "Upgrade: websocket\r\n"
              "Connection: Upgrade\r\n"
              "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");

  mg_write(conn, buf, strlen(buf));
}

static int deliver_websocket_frame(struct connection *conn) {
2504
  // Having buf unsigned char * is important, as it is used below in arithmetic
Sergey Lyubka's avatar
Sergey Lyubka committed
2505 2506
  unsigned char *buf = (unsigned char *) conn->ns_conn->recv_iobuf.buf;
  int i, len, buf_len = conn->ns_conn->recv_iobuf.len, frame_len = 0,
2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529
      mask_len = 0, header_len = 0, data_len = 0, buffered = 0;

  if (buf_len >= 2) {
    len = buf[1] & 127;
    mask_len = buf[1] & 128 ? 4 : 0;
    if (len < 126 && buf_len >= mask_len) {
      data_len = len;
      header_len = 2 + mask_len;
    } else if (len == 126 && buf_len >= 4 + mask_len) {
      header_len = 4 + mask_len;
      data_len = ((((int) buf[2]) << 8) + buf[3]);
    } else if (buf_len >= 10 + mask_len) {
      header_len = 10 + mask_len;
      data_len = (int) (((uint64_t) htonl(* (uint32_t *) &buf[2])) << 32) +
        htonl(* (uint32_t *) &buf[6]);
    }
  }

  frame_len = header_len + data_len;
  buffered = frame_len > 0 && frame_len <= buf_len;

  if (buffered) {
    conn->mg_conn.content_len = data_len;
2530
    conn->mg_conn.content = (char *) buf + header_len;
2531 2532 2533 2534 2535 2536
    conn->mg_conn.wsbits = buf[0];

    // Apply mask if necessary
    if (mask_len > 0) {
      for (i = 0; i < data_len; i++) {
        buf[i + header_len] ^= (buf + header_len - mask_len)[i % 4];
2537 2538 2539
      }
    }

2540
    // Call the handler and remove frame from the iobuf
2541
    if (call_user(conn, MG_REQUEST) == MG_FALSE) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2542
      conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
2543
    }
Sergey Lyubka's avatar
Sergey Lyubka committed
2544
    iobuf_remove(&conn->ns_conn->recv_iobuf, frame_len);
2545 2546
  }

2547
  return buffered;
2548 2549
}

2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570
int mg_websocket_write(struct mg_connection* conn, int opcode,
                       const char *data, size_t data_len) {
    unsigned char *copy;
    size_t copy_len = 0;
    int retval = -1;

    if ((copy = (unsigned char *) malloc(data_len + 10)) == NULL) {
      return -1;
    }

    copy[0] = 0x80 + (opcode & 0x0f);

    // Frame format: http://tools.ietf.org/html/rfc6455#section-5.2
    if (data_len < 126) {
      // Inline 7-bit length field
      copy[1] = data_len;
      memcpy(copy + 2, data, data_len);
      copy_len = 2 + data_len;
    } else if (data_len <= 0xFFFF) {
      // 16-bit length field
      copy[1] = 126;
2571
      * (uint16_t *) (copy + 2) = (uint16_t) htons((uint16_t) data_len);
2572 2573 2574 2575 2576
      memcpy(copy + 4, data, data_len);
      copy_len = 4 + data_len;
    } else {
      // 64-bit length field
      copy[1] = 127;
Sergey Lyubka's avatar
Sergey Lyubka committed
2577 2578
      * (uint32_t *) (copy + 2) = (uint32_t)
        htonl((uint32_t) ((uint64_t) data_len >> 32));
2579
      * (uint32_t *) (copy + 6) = (uint32_t) htonl(data_len & 0xffffffff);
2580 2581 2582 2583
      memcpy(copy + 10, data, data_len);
      copy_len = 10 + data_len;
    }

2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599
    if (copy_len > 0) {
      retval = mg_write(conn, copy, copy_len);
    }
    free(copy);

    return retval;
}

static void send_websocket_handshake_if_requested(struct mg_connection *conn) {
  const char *ver = mg_get_header(conn, "Sec-WebSocket-Version"),
        *key = mg_get_header(conn, "Sec-WebSocket-Key");
  if (ver != NULL && key != NULL) {
    conn->is_websocket = 1;
    send_websocket_handshake(conn, key);
  }
}
2600 2601

static void ping_idle_websocket_connection(struct connection *conn, time_t t) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2602
  if (t - conn->ns_conn->last_io_time > MONGOOSE_USE_WEBSOCKET_PING_INTERVAL) {
2603 2604 2605 2606 2607
    mg_websocket_write(&conn->mg_conn, 0x9, "", 0);
  }
}
#else
#define ping_idle_websocket_connection(conn, t)
2608
#endif // !MONGOOSE_NO_WEBSOCKET
2609

2610 2611 2612 2613
static void write_terminating_chunk(struct connection *conn) {
  mg_write(&conn->mg_conn, "0\r\n\r\n", 5);
}

2614 2615
static int call_request_handler(struct connection *conn) {
  int result;
Sergey Lyubka's avatar
Sergey Lyubka committed
2616
  conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
2617
  if ((result = call_user(conn, MG_REQUEST)) == MG_TRUE) {
2618 2619 2620 2621
    if (conn->ns_conn->flags & MG_HEADERS_SENT) {
      write_terminating_chunk(conn);
    }
    close_local_endpoint(conn);
2622
  }
2623
  return result;
2624 2625
}

2626
const char *mg_get_mime_type(const char *path, const char *default_mime_type) {
2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639
  const char *ext;
  size_t i, path_len;

  path_len = strlen(path);

  for (i = 0; static_builtin_mime_types[i].extension != NULL; i++) {
    ext = path + (path_len - static_builtin_mime_types[i].ext_len);
    if (path_len > static_builtin_mime_types[i].ext_len &&
        mg_strcasecmp(ext, static_builtin_mime_types[i].extension) == 0) {
      return static_builtin_mime_types[i].mime_type;
    }
  }

2640
  return default_mime_type;
2641 2642
}

2643
#ifndef MONGOOSE_NO_FILESYSTEM
2644 2645
// Convert month to the month number. Return -1 on error, or month number
static int get_month_index(const char *s) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2646 2647 2648 2649
  static const char *month_names[] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  };
2650
  int i;
2651

Sergey Lyubka's avatar
Sergey Lyubka committed
2652 2653
  for (i = 0; i < (int) ARRAY_SIZE(month_names); i++)
    if (!strcmp(s, month_names[i]))
2654 2655 2656
      return i;

  return -1;
2657 2658
}

2659 2660
static int num_leap_years(int year) {
  return year / 4 - year / 100 + year / 400;
2661 2662
}

2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686
// Parse UTC date-time string, and return the corresponding time_t value.
static time_t parse_date_string(const char *datetime) {
  static const unsigned short days_before_month[] = {
    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
  };
  char month_str[32];
  int second, minute, hour, day, month, year, leap_days, days;
  time_t result = (time_t) 0;

  if (((sscanf(datetime, "%d/%3s/%d %d:%d:%d",
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
       (sscanf(datetime, "%d %3s %d %d:%d:%d",
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
       (sscanf(datetime, "%*3s, %d %3s %d %d:%d:%d",
               &day, month_str, &year, &hour, &minute, &second) == 6) ||
       (sscanf(datetime, "%d-%3s-%d %d:%d:%d",
               &day, month_str, &year, &hour, &minute, &second) == 6)) &&
      year > 1970 &&
      (month = get_month_index(month_str)) != -1) {
    leap_days = num_leap_years(year) - num_leap_years(1970);
    year -= 1970;
    days = year * 365 + days_before_month[month] + (day - 1) + leap_days;
    result = days * 24 * 3600 + hour * 3600 + minute * 60 + second;
  }
2687

2688 2689
  return result;
}
2690

Sergey Lyubka's avatar
Sergey Lyubka committed
2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712
// Look at the "path" extension and figure what mime type it has.
// Store mime type in the vector.
static void get_mime_type(const struct mg_server *server, const char *path,
                          struct vec *vec) {
  struct vec ext_vec, mime_vec;
  const char *list, *ext;
  size_t path_len;

  path_len = strlen(path);

  // Scan user-defined mime types first, in case user wants to
  // override default mime types.
  list = server->config_options[EXTRA_MIME_TYPES];
  while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) {
    // ext now points to the path suffix
    ext = path + path_len - ext_vec.len;
    if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) {
      *vec = mime_vec;
      return;
    }
  }

2713
  vec->ptr = mg_get_mime_type(path, "text/plain");
Sergey Lyubka's avatar
Sergey Lyubka committed
2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725
  vec->len = strlen(vec->ptr);
}

static const char *suggest_connection_header(const struct mg_connection *conn) {
  return should_keep_alive(conn) ? "keep-alive" : "close";
}

static void construct_etag(char *buf, size_t buf_len, const file_stat_t *st) {
  mg_snprintf(buf, buf_len, "\"%lx.%" INT64_FMT "\"",
              (unsigned long) st->st_mtime, (int64_t) st->st_size);
}

2726 2727 2728 2729 2730 2731 2732 2733 2734 2735
// Return True if we should reply 304 Not Modified.
static int is_not_modified(const struct connection *conn,
                           const file_stat_t *stp) {
  char etag[64];
  const char *ims = mg_get_header(&conn->mg_conn, "If-Modified-Since");
  const char *inm = mg_get_header(&conn->mg_conn, "If-None-Match");
  construct_etag(etag, sizeof(etag), stp);
  return (inm != NULL && !mg_strcasecmp(etag, inm)) ||
    (ims != NULL && stp->st_mtime <= parse_date_string(ims));
}
2736

2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751
// For given directory path, substitute it to valid index file.
// Return 0 if index file has been found, -1 if not found.
// If the file is found, it's stats is returned in stp.
static int find_index_file(struct connection *conn, char *path,
                           size_t path_len, file_stat_t *stp) {
  const char *list = conn->server->config_options[INDEX_FILES];
  file_stat_t st;
  struct vec filename_vec;
  size_t n = strlen(path), found = 0;

  // The 'path' given to us points to the directory. Remove all trailing
  // directory separator characters from the end of the path, and
  // then append single directory separator character.
  while (n > 0 && path[n - 1] == '/') {
    n--;
2752
  }
2753
  path[n] = '/';
2754

2755 2756 2757
  // Traverse index files list. For each entry, append it to the given
  // path and see if the file exists. If it exists, break the loop
  while ((list = next_option(list, &filename_vec, NULL)) != NULL) {
2758

2759 2760 2761
    // Ignore too long entries that may overflow path buffer
    if (filename_vec.len > (int) (path_len - (n + 2)))
      continue;
2762

2763
    // Prepare full path to the index file
Sergey Lyubka's avatar
Sergey Lyubka committed
2764 2765
    strncpy(path + n + 1, filename_vec.ptr, filename_vec.len);
    path[n + 1 + filename_vec.len] = '\0';
2766

2767 2768 2769 2770 2771 2772 2773 2774 2775
    //DBG(("[%s]", path));

    // Does it exist?
    if (!stat(path, &st)) {
      // Yes it does, break the loop
      *stp = st;
      found = 1;
      break;
    }
2776 2777
  }

2778 2779 2780 2781 2782 2783
  // If no index file exists, restore directory path
  if (!found) {
    path[n] = '\0';
  }

  return found;
2784 2785
}

Sergey Lyubka's avatar
Sergey Lyubka committed
2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803
static int parse_range_header(const char *header, int64_t *a, int64_t *b) {
  return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b);
}

static void gmt_time_string(char *buf, size_t buf_len, time_t *t) {
  strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(t));
}

static void open_file_endpoint(struct connection *conn, const char *path,
                               file_stat_t *st) {
  char date[64], lm[64], etag[64], range[64], headers[500];
  const char *msg = "OK", *hdr;
  time_t curtime = time(NULL);
  int64_t r1, r2;
  struct vec mime_vec;
  int n;

  conn->endpoint_type = EP_FILE;
Sergey Lyubka's avatar
Sergey Lyubka committed
2804
  ns_set_close_on_exec(conn->endpoint.fd);
Sergey Lyubka's avatar
Sergey Lyubka committed
2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843
  conn->mg_conn.status_code = 200;

  get_mime_type(conn->server, path, &mime_vec);
  conn->cl = st->st_size;
  range[0] = '\0';

  // If Range: header specified, act accordingly
  r1 = r2 = 0;
  hdr = mg_get_header(&conn->mg_conn, "Range");
  if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 &&
      r1 >= 0 && r2 >= 0) {
    conn->mg_conn.status_code = 206;
    conn->cl = n == 2 ? (r2 > conn->cl ? conn->cl : r2) - r1 + 1: conn->cl - r1;
    mg_snprintf(range, sizeof(range), "Content-Range: bytes "
                "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n",
                r1, r1 + conn->cl - 1, (int64_t) st->st_size);
    msg = "Partial Content";
    lseek(conn->endpoint.fd, r1, SEEK_SET);
  }

  // Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to
  // http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3
  gmt_time_string(date, sizeof(date), &curtime);
  gmt_time_string(lm, sizeof(lm), &st->st_mtime);
  construct_etag(etag, sizeof(etag), st);

  n = mg_snprintf(headers, sizeof(headers),
                  "HTTP/1.1 %d %s\r\n"
                  "Date: %s\r\n"
                  "Last-Modified: %s\r\n"
                  "Etag: %s\r\n"
                  "Content-Type: %.*s\r\n"
                  "Content-Length: %" INT64_FMT "\r\n"
                  "Connection: %s\r\n"
                  "Accept-Ranges: bytes\r\n"
                  "%s%s\r\n",
                  conn->mg_conn.status_code, msg, date, lm, etag,
                  (int) mime_vec.len, mime_vec.ptr, conn->cl,
                  suggest_connection_header(&conn->mg_conn),
2844
                  range, MONGOOSE_USE_EXTRA_HTTP_HEADERS);
Sergey Lyubka's avatar
Sergey Lyubka committed
2845
  ns_send(conn->ns_conn, headers, n);
Sergey Lyubka's avatar
Sergey Lyubka committed
2846 2847

  if (!strcmp(conn->mg_conn.request_method, "HEAD")) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2848
    conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
Sergey Lyubka's avatar
Sergey Lyubka committed
2849 2850 2851 2852
    close(conn->endpoint.fd);
    conn->endpoint_type = EP_NONE;
  }
}
2853
#endif  // MONGOOSE_NO_FILESYSTEM
Sergey Lyubka's avatar
Sergey Lyubka committed
2854

2855
static void call_request_handler_if_data_is_buffered(struct connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
2856
  struct iobuf *loc = &conn->ns_conn->recv_iobuf;
2857 2858
  struct mg_connection *c = &conn->mg_conn;

2859
#ifndef MONGOOSE_NO_WEBSOCKET
2860 2861 2862
  if (conn->mg_conn.is_websocket) {
    do { } while (deliver_websocket_frame(conn));
  } else
2863
#endif
2864
  if ((size_t) loc->len >= c->content_len &&
Sergey Lyubka's avatar
Sergey Lyubka committed
2865
      call_request_handler(conn) == MG_FALSE) {
2866
    open_local_endpoint(conn, 1);
2867
  }
2868 2869
}

2870
#if !defined(MONGOOSE_NO_DIRECTORY_LISTING) || !defined(MONGOOSE_NO_DAV)
2871

2872 2873 2874 2875
#ifdef _WIN32
struct dirent {
  char d_name[MAX_PATH_SIZE];
};
2876

2877 2878 2879 2880 2881
typedef struct DIR {
  HANDLE   handle;
  WIN32_FIND_DATAW info;
  struct dirent result;
} DIR;
2882

2883 2884 2885 2886 2887
// Implementation of POSIX opendir/closedir/readdir for Windows.
static DIR *opendir(const char *name) {
  DIR *dir = NULL;
  wchar_t wpath[MAX_PATH_SIZE];
  DWORD attrs;
2888

2889 2890 2891 2892 2893
  if (name == NULL) {
    SetLastError(ERROR_BAD_ARGUMENTS);
  } else if ((dir = (DIR *) malloc(sizeof(*dir))) == NULL) {
    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  } else {
2894
    to_wchar(name, wpath, ARRAY_SIZE(wpath));
2895 2896 2897 2898 2899 2900 2901 2902 2903 2904
    attrs = GetFileAttributesW(wpath);
    if (attrs != 0xFFFFFFFF &&
        ((attrs & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) {
      (void) wcscat(wpath, L"\\*");
      dir->handle = FindFirstFileW(wpath, &dir->info);
      dir->result.d_name[0] = '\0';
    } else {
      free(dir);
      dir = NULL;
    }
2905 2906
  }

2907
  return dir;
2908 2909
}

2910 2911
static int closedir(DIR *dir) {
  int result = 0;
2912

2913 2914 2915
  if (dir != NULL) {
    if (dir->handle != INVALID_HANDLE_VALUE)
      result = FindClose(dir->handle) ? 0 : -1;
2916

2917 2918 2919 2920
    free(dir);
  } else {
    result = -1;
    SetLastError(ERROR_BAD_ARGUMENTS);
2921
  }
2922

2923
  return result;
2924 2925
}

2926 2927
static struct dirent *readdir(DIR *dir) {
  struct dirent *result = 0;
2928

2929 2930 2931 2932 2933 2934
  if (dir) {
    if (dir->handle != INVALID_HANDLE_VALUE) {
      result = &dir->result;
      (void) WideCharToMultiByte(CP_UTF8, 0,
          dir->info.cFileName, -1, result->d_name,
          sizeof(result->d_name), NULL, NULL);
2935

2936 2937 2938 2939
      if (!FindNextFileW(dir->handle, &dir->info)) {
        (void) FindClose(dir->handle);
        dir->handle = INVALID_HANDLE_VALUE;
      }
2940

2941 2942
    } else {
      SetLastError(ERROR_FILE_NOT_FOUND);
2943
    }
2944 2945
  } else {
    SetLastError(ERROR_BAD_ARGUMENTS);
2946 2947
  }

2948 2949 2950
  return result;
}
#endif // _WIN32  POSIX opendir/closedir/readdir implementation
2951

2952 2953 2954 2955 2956
static int scan_directory(struct connection *conn, const char *dir,
                          struct dir_entry **arr) {
  char path[MAX_PATH_SIZE];
  struct dir_entry *p;
  struct dirent *dp;
2957
  int arr_size = 0, arr_ind = 0, inc = 100;
2958 2959
  DIR *dirp;

2960
  *arr = NULL;
2961 2962 2963 2964 2965 2966 2967 2968
  if ((dirp = (opendir(dir))) == NULL) return 0;

  while ((dp = readdir(dirp)) != NULL) {
    // Do not show current dir and hidden files
    if (!strcmp(dp->d_name, ".") ||
        !strcmp(dp->d_name, "..") ||
        must_hide_file(conn, dp->d_name)) {
      continue;
2969
    }
2970
    mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
2971

2972
    // Resize the array if nesessary
2973
    if (arr_ind >= arr_size) {
2974
      if ((p = (struct dir_entry *)
2975 2976
           realloc(*arr, (inc + arr_size) * sizeof(**arr))) != NULL) {
        // Memset new chunk to zero, otherwize st_mtime will have garbage which
2977 2978
        // can make strftime() segfault, see
        // http://code.google.com/p/mongoose/issues/detail?id=79
2979
        memset(p + arr_size, 0, sizeof(**arr) * inc);
2980 2981

        *arr = p;
2982
        arr_size += inc;
2983 2984
      }
    }
2985 2986 2987 2988 2989 2990 2991

    if (arr_ind < arr_size) {
      (*arr)[arr_ind].conn = conn;
      (*arr)[arr_ind].file_name = strdup(dp->d_name);
      stat(path, &(*arr)[arr_ind].st);
      arr_ind++;
    }
2992
  }
2993
  closedir(dirp);
2994

2995
  return arr_ind;
2996 2997
}

2998 2999 3000 3001
static void mg_url_encode(const char *src, char *dst, size_t dst_len) {
  static const char *dont_escape = "._-$,;~()";
  static const char *hex = "0123456789abcdef";
  const char *end = dst + dst_len - 1;
3002

3003 3004 3005 3006 3007 3008 3009 3010 3011 3012
  for (; *src != '\0' && dst < end; src++, dst++) {
    if (isalnum(*(const unsigned char *) src) ||
        strchr(dont_escape, * (const unsigned char *) src) != NULL) {
      *dst = *src;
    } else if (dst + 2 < end) {
      dst[0] = '%';
      dst[1] = hex[(* (const unsigned char *) src) >> 4];
      dst[2] = hex[(* (const unsigned char *) src) & 0xf];
      dst += 2;
    }
3013
  }
3014 3015

  *dst = '\0';
3016
}
3017
#endif  // !NO_DIRECTORY_LISTING || !MONGOOSE_NO_DAV
3018

3019
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
3020

3021
static void print_dir_entry(const struct dir_entry *de) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3022
  char size[64], mod[64], href[MAX_PATH_SIZE * 3];
3023
  int64_t fsize = de->st.st_size;
Sergey Lyubka's avatar
Sergey Lyubka committed
3024
  int is_dir = S_ISDIR(de->st.st_mode);
3025
  const char *slash = is_dir ? "/" : "";
3026

3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039
  if (is_dir) {
    mg_snprintf(size, sizeof(size), "%s", "[DIRECTORY]");
  } else {
     // We use (signed) cast below because MSVC 6 compiler cannot
     // convert unsigned __int64 to double.
    if (fsize < 1024) {
      mg_snprintf(size, sizeof(size), "%d", (int) fsize);
    } else if (fsize < 0x100000) {
      mg_snprintf(size, sizeof(size), "%.1fk", (double) fsize / 1024.0);
    } else if (fsize < 0x40000000) {
      mg_snprintf(size, sizeof(size), "%.1fM", (double) fsize / 1048576);
    } else {
      mg_snprintf(size, sizeof(size), "%.1fG", (double) fsize / 1073741824);
3040 3041
    }
  }
3042 3043
  strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", localtime(&de->st.st_mtime));
  mg_url_encode(de->file_name, href, sizeof(href));
Sergey Lyubka's avatar
Sergey Lyubka committed
3044
  mg_printf_data(&de->conn->mg_conn,
3045 3046 3047 3048 3049
                  "<tr><td><a href=\"%s%s%s\">%s%s</a></td>"
                  "<td>&nbsp;%s</td><td>&nbsp;&nbsp;%s</td></tr>\n",
                  de->conn->mg_conn.uri, href, slash, de->file_name, slash,
                  mod, size);
}
3050

3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072
// Sort directory entries by size, or name, or modification time.
// On windows, __cdecl specification is needed in case if project is built
// with __stdcall convention. qsort always requires __cdels callback.
static int __cdecl compare_dir_entries(const void *p1, const void *p2) {
  const struct dir_entry *a = (const struct dir_entry *) p1,
        *b = (const struct dir_entry *) p2;
  const char *qs = a->conn->mg_conn.query_string ?
    a->conn->mg_conn.query_string : "na";
  int cmp_result = 0;

  if (S_ISDIR(a->st.st_mode) && !S_ISDIR(b->st.st_mode)) {
    return -1;  // Always put directories on top
  } else if (!S_ISDIR(a->st.st_mode) && S_ISDIR(b->st.st_mode)) {
    return 1;   // Always put directories on top
  } else if (*qs == 'n') {
    cmp_result = strcmp(a->file_name, b->file_name);
  } else if (*qs == 's') {
    cmp_result = a->st.st_size == b->st.st_size ? 0 :
      a->st.st_size > b->st.st_size ? 1 : -1;
  } else if (*qs == 'd') {
    cmp_result = a->st.st_mtime == b->st.st_mtime ? 0 :
      a->st.st_mtime > b->st.st_mtime ? 1 : -1;
3073
  }
3074 3075

  return qs[1] == 'd' ? -cmp_result : cmp_result;
3076 3077
}

3078 3079 3080 3081 3082
static void send_directory_listing(struct connection *conn, const char *dir) {
  struct dir_entry *arr = NULL;
  int i, num_entries, sort_direction = conn->mg_conn.query_string != NULL &&
    conn->mg_conn.query_string[1] == 'd' ? 'a' : 'd';

Sergey Lyubka's avatar
Sergey Lyubka committed
3083 3084
  mg_send_header(&conn->mg_conn, "Transfer-Encoding", "chunked");
  mg_send_header(&conn->mg_conn, "Content-Type", "text/html; charset=utf-8");
3085

Sergey Lyubka's avatar
Sergey Lyubka committed
3086
  mg_printf_data(&conn->mg_conn,
3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104
              "<html><head><title>Index of %s</title>"
              "<style>th {text-align: left;}</style></head>"
              "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">"
              "<tr><th><a href=\"?n%c\">Name</a></th>"
              "<th><a href=\"?d%c\">Modified</a></th>"
              "<th><a href=\"?s%c\">Size</a></th></tr>"
              "<tr><td colspan=\"3\"><hr></td></tr>",
              conn->mg_conn.uri, conn->mg_conn.uri,
              sort_direction, sort_direction, sort_direction);

  num_entries = scan_directory(conn, dir, &arr);
  qsort(arr, num_entries, sizeof(arr[0]), compare_dir_entries);
  for (i = 0; i < num_entries; i++) {
    print_dir_entry(&arr[i]);
    free(arr[i].file_name);
  }
  free(arr);

3105
  write_terminating_chunk(conn);
3106 3107
  close_local_endpoint(conn);
}
3108
#endif  // MONGOOSE_NO_DIRECTORY_LISTING
3109

3110
#ifndef MONGOOSE_NO_DAV
3111 3112
static void print_props(struct connection *conn, const char *uri,
                        file_stat_t *stp) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3113
  char mtime[64];
3114 3115

  gmt_time_string(mtime, sizeof(mtime), &stp->st_mtime);
Sergey Lyubka's avatar
Sergey Lyubka committed
3116
  mg_printf(&conn->mg_conn,
3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129
      "<d:response>"
       "<d:href>%s</d:href>"
       "<d:propstat>"
        "<d:prop>"
         "<d:resourcetype>%s</d:resourcetype>"
         "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>"
         "<d:getlastmodified>%s</d:getlastmodified>"
        "</d:prop>"
        "<d:status>HTTP/1.1 200 OK</d:status>"
       "</d:propstat>"
      "</d:response>\n",
      uri, S_ISDIR(stp->st_mode) ? "<d:collection/>" : "",
      (int64_t) stp->st_size, mtime);
3130 3131
}

3132 3133 3134 3135 3136 3137 3138 3139 3140 3141
static void handle_propfind(struct connection *conn, const char *path,
                            file_stat_t *stp) {
  static const char header[] = "HTTP/1.1 207 Multi-Status\r\n"
    "Connection: close\r\n"
    "Content-Type: text/xml; charset=utf-8\r\n\r\n"
    "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
    "<d:multistatus xmlns:d='DAV:'>\n";
  static const char footer[] = "</d:multistatus>";
  const char *depth = mg_get_header(&conn->mg_conn, "Depth"),
        *list_dir = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
3142

3143
  conn->mg_conn.status_code = 207;
Sergey Lyubka's avatar
Sergey Lyubka committed
3144
  ns_send(conn->ns_conn, header, sizeof(header) - 1);
3145

3146 3147
  // Print properties for the requested resource itself
  print_props(conn, conn->mg_conn.uri, stp);
3148

3149 3150 3151 3152 3153
  // If it is a directory, print directory entries too if Depth is not 0
  if (S_ISDIR(stp->st_mode) && !mg_strcasecmp(list_dir, "yes") &&
      (depth == NULL || strcmp(depth, "0") != 0)) {
    struct dir_entry *arr = NULL;
    int i, num_entries = scan_directory(conn, path, &arr);
3154

3155 3156 3157
    for (i = 0; i < num_entries; i++) {
      char buf[MAX_PATH_SIZE], buf2[sizeof(buf) * 3];
      struct dir_entry *de = &arr[i];
3158

3159 3160 3161 3162
      mg_snprintf(buf, sizeof(buf), "%s%s", de->conn->mg_conn.uri,
                  de->file_name);
      mg_url_encode(buf, buf2, sizeof(buf2) - 1);
      print_props(conn, buf, &de->st);
3163 3164 3165
    }
  }

Sergey Lyubka's avatar
Sergey Lyubka committed
3166
  ns_send(conn->ns_conn, footer, sizeof(footer) - 1);
3167
  close_local_endpoint(conn);
3168 3169
}

3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183
static void handle_mkcol(struct connection *conn, const char *path) {
  int status_code = 500;

  if (conn->mg_conn.content_len > 0) {
    status_code = 415;
  } else if (!mkdir(path, 0755)) {
    status_code = 201;
  } else if (errno == EEXIST) {
    status_code = 405;
  } else if (errno == EACCES) {
    status_code = 403;
  } else if (errno == ENOENT) {
    status_code = 409;
  }
3184
  send_http_error(conn, status_code, NULL);
3185 3186
}

3187 3188 3189 3190 3191
static int remove_directory(const char *dir) {
  char path[MAX_PATH_SIZE];
  struct dirent *dp;
  file_stat_t st;
  DIR *dirp;
3192

3193
  if ((dirp = opendir(dir)) == NULL) return 0;
3194

3195 3196 3197 3198 3199 3200 3201 3202 3203
  while ((dp = readdir(dirp)) != NULL) {
    if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) continue;
    mg_snprintf(path, sizeof(path), "%s%c%s", dir, '/', dp->d_name);
    stat(path, &st);
    if (S_ISDIR(st.st_mode)) {
      remove_directory(path);
    } else {
      remove(path);
    }
3204
  }
3205 3206
  closedir(dirp);
  rmdir(dir);
3207

3208 3209 3210 3211 3212 3213
  return 1;
}

static void handle_delete(struct connection *conn, const char *path) {
  file_stat_t st;

3214
  if (stat(path, &st) != 0) {
3215
    send_http_error(conn, 404, NULL);
3216 3217
  } else if (S_ISDIR(st.st_mode)) {
    remove_directory(path);
3218
    send_http_error(conn, 204, NULL);
3219
  } else if (!remove(path) == 0) {
3220
    send_http_error(conn, 204, NULL);
3221
  } else {
3222
    send_http_error(conn, 423, NULL);
3223
  }
3224
}
3225

3226 3227 3228 3229 3230 3231 3232
// For a given PUT path, create all intermediate subdirectories
// for given path. Return 0 if the path itself is a directory,
// or -1 on error, 1 if OK.
static int put_dir(const char *path) {
  char buf[MAX_PATH_SIZE];
  const char *s, *p;
  file_stat_t st;
3233

Sergey Lyubka's avatar
Sergey Lyubka committed
3234 3235 3236 3237 3238 3239 3240
  // Create intermediate directories if they do not exist
  for (s = p = path + 1; (p = strchr(s, '/')) != NULL; s = ++p) {
    if (p - path >= (int) sizeof(buf)) return -1; // Buffer overflow
    memcpy(buf, path, p - path);
    buf[p - path] = '\0';
    if (stat(buf, &st) != 0 && mkdir(buf, 0755) != 0) return -1;
    if (p[1] == '\0') return 0;  // Path is a directory itself
3241
  }
3242

Sergey Lyubka's avatar
Sergey Lyubka committed
3243
  return 1;
3244 3245
}

3246 3247 3248
static void handle_put(struct connection *conn, const char *path) {
  file_stat_t st;
  const char *range, *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
3249 3250 3251
  int64_t r1, r2;
  int rc;

3252
  conn->mg_conn.status_code = !stat(path, &st) ? 200 : 201;
3253
  if ((rc = put_dir(path)) == 0) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3254 3255 3256
    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\n\r\n",
              conn->mg_conn.status_code);
    close_local_endpoint(conn);
3257
  } else if (rc == -1) {
3258
    send_http_error(conn, 500, "put_dir: %s", strerror(errno));
3259
  } else if (cl_hdr == NULL) {
3260
    send_http_error(conn, 411, NULL);
3261 3262 3263 3264 3265
#ifdef _WIN32
    //On Windows, open() is a macro with 2 params
  } else if ((conn->endpoint.fd =
              open(path, O_RDWR | O_CREAT | O_TRUNC)) < 0) {
#else
3266 3267
  } else if ((conn->endpoint.fd =
              open(path, O_RDWR | O_CREAT | O_TRUNC, 0644)) < 0) {
3268
#endif
3269
    send_http_error(conn, 500, "open(%s): %s", path, strerror(errno));
3270
  } else {
Sergey Lyubka's avatar
Sergey Lyubka committed
3271
    DBG(("PUT [%s] %d", path, conn->ns_conn->recv_iobuf.len));
3272
    conn->endpoint_type = EP_PUT;
Sergey Lyubka's avatar
Sergey Lyubka committed
3273
    ns_set_close_on_exec(conn->endpoint.fd);
3274 3275
    range = mg_get_header(&conn->mg_conn, "Content-Range");
    conn->cl = to64(cl_hdr);
3276 3277
    r1 = r2 = 0;
    if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
3278 3279 3280
      conn->mg_conn.status_code = 206;
      lseek(conn->endpoint.fd, r1, SEEK_SET);
      conn->cl = r2 > r1 ? r2 - r1 + 1: conn->cl - r1;
3281
    }
Sergey Lyubka's avatar
Sergey Lyubka committed
3282 3283
    mg_printf(&conn->mg_conn, "HTTP/1.1 %d OK\r\nContent-Length: 0\r\n\r\n",
              conn->mg_conn.status_code);
3284 3285 3286
  }
}

3287
static void forward_put_data(struct connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3288
  struct iobuf *io = &conn->ns_conn->recv_iobuf;
3289 3290
  int n = write(conn->endpoint.fd, io->buf, io->len);
  if (n > 0) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3291
    iobuf_remove(io, n);
3292 3293 3294
    conn->cl -= n;
    if (conn->cl <= 0) {
      close_local_endpoint(conn);
3295 3296
    }
  }
3297
}
3298
#endif //  MONGOOSE_NO_DAV
3299

3300 3301 3302
static void send_options(struct connection *conn) {
  static const char reply[] = "HTTP/1.1 200 OK\r\nAllow: GET, POST, HEAD, "
    "CONNECT, PUT, DELETE, OPTIONS, PROPFIND, MKCOL\r\nDAV: 1\r\n\r\n";
Sergey Lyubka's avatar
Sergey Lyubka committed
3303 3304
  ns_send(conn->ns_conn, reply, sizeof(reply) - 1);
  conn->ns_conn->flags |= NSF_FINISHED_SENDING_DATA;
3305 3306
}

3307
#ifndef MONGOOSE_NO_AUTH
3308
void mg_send_digest_auth_request(struct mg_connection *c) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3309
  struct connection *conn = MG_CONN_2_CONN(c);
3310 3311
  c->status_code = 401;
  mg_printf(c,
Sergey Lyubka's avatar
Sergey Lyubka committed
3312 3313 3314 3315 3316
            "HTTP/1.1 401 Unauthorized\r\n"
            "WWW-Authenticate: Digest qop=\"auth\", "
            "realm=\"%s\", nonce=\"%lu\"\r\n\r\n",
            conn->server->config_options[AUTH_DOMAIN],
            (unsigned long) time(NULL));
3317
  close_local_endpoint(conn);
3318 3319 3320 3321 3322 3323 3324 3325 3326
}

// Use the global passwords file, if specified by auth_gpass option,
// or search for .htpasswd in the requested directory.
static FILE *open_auth_file(struct connection *conn, const char *path) {
  char name[MAX_PATH_SIZE];
  const char *p, *gpass = conn->server->config_options[GLOBAL_AUTH_FILE];
  file_stat_t st;
  FILE *fp = NULL;
3327

3328 3329 3330 3331 3332 3333
  if (gpass != NULL) {
    // Use global passwords file
    fp = fopen(gpass, "r");
  } else if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
    mg_snprintf(name, sizeof(name), "%s%c%s", path, '/', PASSWORDS_FILE_NAME);
    fp = fopen(name, "r");
3334
  } else {
3335 3336 3337 3338 3339
    // Try to find .htpasswd in requested directory.
    if ((p = strrchr(path, '/')) == NULL) p = path;
    mg_snprintf(name, sizeof(name), "%.*s%c%s",
                (int) (p - path), path, '/', PASSWORDS_FILE_NAME);
    fp = fopen(name, "r");
3340 3341
  }

3342 3343
  return fp;
}
3344

3345
#if !defined(HAVE_MD5) && !defined(MONGOOSE_NO_AUTH)
3346 3347 3348 3349 3350
typedef struct MD5Context {
  uint32_t buf[4];
  uint32_t bits[2];
  unsigned char in[64];
} MD5_CTX;
3351

3352 3353
static void byteReverse(unsigned char *buf, unsigned longs) {
  uint32_t t;
3354

3355 3356 3357 3358 3359 3360 3361 3362
  // Forrest: MD5 expect LITTLE_ENDIAN, swap if BIG_ENDIAN
  if (is_big_endian()) {
    do {
      t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
        ((unsigned) buf[1] << 8 | buf[0]);
      * (uint32_t *) buf = t;
      buf += 4;
    } while (--longs);
3363 3364 3365
  }
}

3366 3367 3368 3369
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
3370

3371 3372
#define MD5STEP(f, w, x, y, z, data, s) \
  ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
3373

3374 3375 3376 3377 3378 3379 3380
// Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
// initialization constants.
static void MD5Init(MD5_CTX *ctx) {
  ctx->buf[0] = 0x67452301;
  ctx->buf[1] = 0xefcdab89;
  ctx->buf[2] = 0x98badcfe;
  ctx->buf[3] = 0x10325476;
3381

3382 3383
  ctx->bits[0] = 0;
  ctx->bits[1] = 0;
3384 3385
}

3386 3387
static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) {
  register uint32_t a, b, c, d;
3388

3389 3390 3391 3392
  a = buf[0];
  b = buf[1];
  c = buf[2];
  d = buf[3];
3393

3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409
  MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
  MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
  MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
  MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
  MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
  MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
  MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
  MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
  MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
  MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
  MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
  MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
  MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
  MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
  MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
  MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
3410

3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426
  MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
  MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
  MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
  MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
  MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
  MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
  MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
  MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
  MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
  MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
  MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
  MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
  MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
  MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
  MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
  MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
3427

3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443
  MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
  MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
  MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
  MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
  MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
  MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
  MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
  MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
  MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
  MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
  MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
  MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
  MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
  MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
  MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
  MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
3444

3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465
  MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
  MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
  MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
  MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
  MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
  MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
  MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
  MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
  MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
  MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
  MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
  MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
  MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
  MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
  MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
  MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);

  buf[0] += a;
  buf[1] += b;
  buf[2] += c;
  buf[3] += d;
3466 3467
}

3468 3469
static void MD5Update(MD5_CTX *ctx, unsigned char const *buf, unsigned len) {
  uint32_t t;
3470

3471 3472 3473 3474
  t = ctx->bits[0];
  if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
    ctx->bits[1]++;
  ctx->bits[1] += len >> 29;
3475

3476
  t = (t >> 3) & 0x3f;
3477

3478 3479
  if (t) {
    unsigned char *p = (unsigned char *) ctx->in + t;
3480

3481 3482 3483 3484
    t = 64 - t;
    if (len < t) {
      memcpy(p, buf, len);
      return;
3485
    }
3486 3487 3488 3489 3490
    memcpy(p, buf, t);
    byteReverse(ctx->in, 16);
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
    buf += t;
    len -= t;
3491 3492
  }

3493 3494 3495 3496 3497 3498
  while (len >= 64) {
    memcpy(ctx->in, buf, 64);
    byteReverse(ctx->in, 16);
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
    buf += 64;
    len -= 64;
3499 3500
  }

3501
  memcpy(ctx->in, buf, len);
3502 3503
}

3504 3505 3506 3507
static void MD5Final(unsigned char digest[16], MD5_CTX *ctx) {
  unsigned count;
  unsigned char *p;
  uint32_t *a;
3508

3509
  count = (ctx->bits[0] >> 3) & 0x3F;
3510

3511 3512 3513 3514 3515 3516 3517 3518
  p = ctx->in + count;
  *p++ = 0x80;
  count = 64 - 1 - count;
  if (count < 8) {
    memset(p, 0, count);
    byteReverse(ctx->in, 16);
    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
    memset(ctx->in, 0, 56);
3519
  } else {
3520
    memset(p, 0, count - 8);
3521
  }
3522
  byteReverse(ctx->in, 14);
3523

3524 3525 3526
  a = (uint32_t *)ctx->in;
  a[14] = ctx->bits[0];
  a[15] = ctx->bits[1];
3527

3528 3529 3530 3531
  MD5Transform(ctx->buf, (uint32_t *) ctx->in);
  byteReverse((unsigned char *) ctx->buf, 4);
  memcpy(digest, ctx->buf, 16);
  memset((char *) ctx, 0, sizeof(*ctx));
3532
}
3533
#endif // !HAVE_MD5
3534 3535 3536



3537 3538 3539 3540
// Stringify binary data. Output buffer must be twice as big as input,
// because each byte takes 2 bytes in string representation
static void bin2str(char *to, const unsigned char *p, size_t len) {
  static const char *hex = "0123456789abcdef";
3541

3542 3543 3544
  for (; len--; p++) {
    *to++ = hex[p[0] >> 4];
    *to++ = hex[p[0] & 0x0f];
3545
  }
3546
  *to = '\0';
3547 3548
}

3549 3550 3551 3552 3553 3554
// Return stringified MD5 hash for list of strings. Buffer must be 33 bytes.
char *mg_md5(char buf[33], ...) {
  unsigned char hash[16];
  const char *p;
  va_list ap;
  MD5_CTX ctx;
3555

3556
  MD5Init(&ctx);
3557

3558 3559 3560
  va_start(ap, buf);
  while ((p = va_arg(ap, const char *)) != NULL) {
    MD5Update(&ctx, (const unsigned char *) p, (unsigned) strlen(p));
3561
  }
3562
  va_end(ap);
3563

3564 3565 3566
  MD5Final(hash, &ctx);
  bin2str(buf, hash, sizeof(hash));
  return buf;
3567 3568
}

3569 3570 3571 3572 3573
// Check the user's password, return 1 if OK
static int check_password(const char *method, const char *ha1, const char *uri,
                          const char *nonce, const char *nc, const char *cnonce,
                          const char *qop, const char *response) {
  char ha2[32 + 1], expected_response[32 + 1];
3574

3575 3576
#if 0
  // Check for authentication timeout
Sergey Lyubka's avatar
Sergey Lyubka committed
3577
  if ((unsigned long) time(NULL) - (unsigned long) to64(nonce) > 3600 * 2) {
3578 3579
    return 0;
  }
3580 3581
#endif

3582 3583 3584
  mg_md5(ha2, method, ":", uri, NULL);
  mg_md5(expected_response, ha1, ":", nonce, ":", nc,
      ":", cnonce, ":", qop, ":", ha2, NULL);
3585

3586
  return mg_strcasecmp(response, expected_response) == 0 ? MG_TRUE : MG_FALSE;
3587 3588 3589
}


3590
// Authorize against the opened passwords file. Return 1 if authorized.
3591
int mg_authorize_digest(struct mg_connection *c, FILE *fp) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3592
  struct connection *conn = MG_CONN_2_CONN(c);
Sergey Lyubka's avatar
Sergey Lyubka committed
3593
  const char *hdr;
3594 3595 3596
  char line[256], f_user[256], ha1[256], f_domain[256], user[100], nonce[100],
       uri[MAX_REQUEST_SIZE], cnonce[100], resp[100], qop[100], nc[100];

Sergey Lyubka's avatar
Sergey Lyubka committed
3597 3598 3599
  if (c == NULL || fp == NULL) return 0;
  if ((hdr = mg_get_header(c, "Authorization")) == NULL ||
      mg_strncasecmp(hdr, "Digest ", 7) != 0) return 0;
3600 3601 3602 3603 3604 3605 3606
  if (!mg_parse_header(hdr, "username", user, sizeof(user))) return 0;
  if (!mg_parse_header(hdr, "cnonce", cnonce, sizeof(cnonce))) return 0;
  if (!mg_parse_header(hdr, "response", resp, sizeof(resp))) return 0;
  if (!mg_parse_header(hdr, "uri", uri, sizeof(uri))) return 0;
  if (!mg_parse_header(hdr, "qop", qop, sizeof(qop))) return 0;
  if (!mg_parse_header(hdr, "nc", nc, sizeof(nc))) return 0;
  if (!mg_parse_header(hdr, "nonce", nonce, sizeof(nonce))) return 0;
3607

3608 3609 3610 3611 3612
  while (fgets(line, sizeof(line), fp) != NULL) {
    if (sscanf(line, "%[^:]:%[^:]:%s", f_user, f_domain, ha1) == 3 &&
        !strcmp(user, f_user) &&
        // NOTE(lsm): due to a bug in MSIE, we do not compare URIs
        !strcmp(conn->server->config_options[AUTH_DOMAIN], f_domain))
3613
      return check_password(c->request_method, ha1, uri,
3614
                            nonce, nc, cnonce, qop, resp);
3615
  }
3616
  return MG_FALSE;
3617 3618 3619
}


3620 3621 3622
// Return 1 if request is authorised, 0 otherwise.
static int is_authorized(struct connection *conn, const char *path) {
  FILE *fp;
3623
  int authorized = MG_TRUE;
3624 3625

  if ((fp = open_auth_file(conn, path)) != NULL) {
3626
    authorized = mg_authorize_digest(&conn->mg_conn, fp);
3627
    fclose(fp);
3628
  }
3629

3630
  return authorized;
3631 3632
}

3633 3634 3635
static int is_authorized_for_dav(struct connection *conn) {
  const char *auth_file = conn->server->config_options[DAV_AUTH_FILE];
  FILE *fp;
3636
  int authorized = MG_FALSE;
3637 3638

  if (auth_file != NULL && (fp = fopen(auth_file, "r")) != NULL) {
3639
    authorized = mg_authorize_digest(&conn->mg_conn, fp);
3640 3641
    fclose(fp);
  }
3642

3643
  return authorized;
3644 3645
}

3646
static int is_dav_mutation(const struct connection *conn) {
3647 3648 3649
  const char *s = conn->mg_conn.request_method;
  return s && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") ||
               !strcmp(s, "MKCOL"));
3650
}
3651
#endif // MONGOOSE_NO_AUTH
3652

Sergey Lyubka's avatar
Sergey Lyubka committed
3653 3654
static int parse_header(const char *str, int str_len, const char *var_name,
                        char *buf, size_t buf_size) {
3655
  int ch = ' ', len = 0, n = strlen(var_name);
Sergey Lyubka's avatar
Sergey Lyubka committed
3656
  const char *p, *end = str + str_len, *s = NULL;
3657

3658
  if (buf != NULL && buf_size > 0) buf[0] = '\0';
3659

3660
  // Find where variable starts
3661
  for (s = str; s != NULL && s + n < end; s++) {
3662
    if ((s == str || s[-1] == ' ' || s[-1] == ',') && s[n] == '=' &&
Sergey Lyubka's avatar
Sergey Lyubka committed
3663
        !memcmp(s, var_name, n)) break;
3664 3665
  }

Sergey Lyubka's avatar
Sergey Lyubka committed
3666
  if (s != NULL && &s[n + 1] < end) {
3667 3668 3669
    s += n + 1;
    if (*s == '"' || *s == '\'') ch = *s++;
    p = s;
3670
    while (p < end && p[0] != ch && p[0] != ',' && len < (int) buf_size) {
3671 3672 3673 3674 3675 3676 3677
      if (p[0] == '\\' && p[1] == ch) p++;
      buf[len++] = *p++;
    }
    if (len >= (int) buf_size || (ch != ' ' && *p != ch)) {
      len = 0;
    } else {
      if (len > 0 && s[len - 1] == ',') len--;
Sergey Lyubka's avatar
Sergey Lyubka committed
3678
      if (len > 0 && s[len - 1] == ';') len--;
3679 3680 3681
      buf[len] = '\0';
    }
  }
3682

3683
  return len;
3684 3685
}

3686
int mg_parse_header(const char *s, const char *var_name, char *buf,
Sergey Lyubka's avatar
Sergey Lyubka committed
3687
                    size_t buf_size) {
3688
  return parse_header(s, s == NULL ? 0 : strlen(s), var_name, buf, buf_size);
Sergey Lyubka's avatar
Sergey Lyubka committed
3689 3690
}

3691
#ifdef MONGOOSE_USE_LUA
Sergey Lyubka's avatar
Sergey Lyubka committed
3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708
#ifdef _WIN32
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
                  int offset) {
  HANDLE fh = (HANDLE) _get_osfhandle(fd);
  HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
  void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
  CloseHandle(mh);
  return p;
}
#define munmap(x, y)  UnmapViewOfFile(x)
#define MAP_FAILED NULL
#define MAP_PRIVATE 0
#define PROT_READ 0
#else
#include <sys/mman.h>
#endif

3709
void reg_string(struct lua_State *L, const char *name, const char *val) {
3710 3711 3712 3713 3714
  lua_pushstring(L, name);
  lua_pushstring(L, val);
  lua_rawset(L, -3);
}

3715
void reg_int(struct lua_State *L, const char *name, int val) {
3716 3717 3718 3719 3720
  lua_pushstring(L, name);
  lua_pushinteger(L, val);
  lua_rawset(L, -3);
}

3721
void reg_function(struct lua_State *L, const char *name,
3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732
                         lua_CFunction func, struct mg_connection *conn) {
  lua_pushstring(L, name);
  lua_pushlightuserdata(L, conn);
  lua_pushcclosure(L, func, 1);
  lua_rawset(L, -3);
}

static int lua_write(lua_State *L) {
  int i, num_args;
  const char *str;
  size_t size;
3733 3734
  struct mg_connection *conn = (struct mg_connection *)
    lua_touserdata(L, lua_upvalueindex(1));
3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746

  num_args = lua_gettop(L);
  for (i = 1; i <= num_args; i++) {
    if (lua_isstring(L, i)) {
      str = lua_tolstring(L, i, &size);
      mg_write(conn, str, size);
    }
  }

  return 0;
}

3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784
static int lsp_sock_close(lua_State *L) {
  if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
    lua_getfield(L, -1, "sock");
    closesocket((sock_t) lua_tonumber(L, -1));
  } else {
    return luaL_error(L, "invalid :close() call");
  }
  return 1;
}

static int lsp_sock_recv(lua_State *L) {
  char buf[2000];
  int n;

  if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
    lua_getfield(L, -1, "sock");
    n = recv((sock_t) lua_tonumber(L, -1), buf, sizeof(buf), 0);
    if (n <= 0) {
      lua_pushnil(L);
    } else {
      lua_pushlstring(L, buf, n);
    }
  } else {
    return luaL_error(L, "invalid :close() call");
  }
  return 1;
}

static int lsp_sock_send(lua_State *L) {
  const char *buf;
  size_t len, sent = 0;
  int n, sock;

  if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
    buf = lua_tolstring(L, -1, &len);
    lua_getfield(L, -2, "sock");
    sock = (int) lua_tonumber(L, -1);
    while (sent < len) {
3785
      if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) break;
3786 3787
      sent += n;
    }
3788
    lua_pushnumber(L, sent);
3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808
  } else {
    return luaL_error(L, "invalid :close() call");
  }
  return 1;
}

static const struct luaL_Reg luasocket_methods[] = {
  {"close", lsp_sock_close},
  {"send", lsp_sock_send},
  {"recv", lsp_sock_recv},
  {NULL, NULL}
};

static sock_t conn2(const char *host, int port) {
  struct sockaddr_in sin;
  struct hostent *he = NULL;
  sock_t sock = INVALID_SOCKET;

  if (host != NULL &&
      (he = gethostbyname(host)) != NULL &&
3809
    (sock = socket(AF_INET, SOCK_STREAM, 0)) != INVALID_SOCKET) {
3810
    ns_set_close_on_exec(sock);
3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841
    sin.sin_family = AF_INET;
    sin.sin_port = htons((uint16_t) port);
    sin.sin_addr = * (struct in_addr *) he->h_addr_list[0];
    if (connect(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
      closesocket(sock);
      sock = INVALID_SOCKET;
    }
  }
  return sock;
}

static int lsp_connect(lua_State *L) {
  sock_t sock;

  if (lua_isstring(L, -2) && lua_isnumber(L, -1)) {
    sock = conn2(lua_tostring(L, -2), (int) lua_tonumber(L, -1));
    if (sock == INVALID_SOCKET) {
      lua_pushnil(L);
    } else {
      lua_newtable(L);
      reg_int(L, "sock", sock);
      reg_string(L, "host", lua_tostring(L, -4));
      luaL_getmetatable(L, "luasocket");
      lua_setmetatable(L, -2);
    }
  } else {
    return luaL_error(L, "connect(host,port): invalid parameter given.");
  }
  return 1;
}

3842 3843 3844 3845 3846 3847
static void prepare_lua_environment(struct mg_connection *ri, lua_State *L) {
  extern void luaL_openlibs(lua_State *);
  int i;

  luaL_openlibs(L);

3848
  luaL_newmetatable(L, "luasocket");
3849
  lua_newtable(L);
3850
  luaL_newlib(L, luasocket_methods);
3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869
  lua_rawset(L, -3);
  lua_pop(L, 1);
  lua_register(L, "connect", lsp_connect);

  if (ri == NULL) return;

  // Register mg module
  lua_newtable(L);
  reg_function(L, "write", lua_write, ri);

  // Export request_info
  lua_pushstring(L, "request_info");
  lua_newtable(L);
  reg_string(L, "request_method", ri->request_method);
  reg_string(L, "uri", ri->uri);
  reg_string(L, "http_version", ri->http_version);
  reg_string(L, "query_string", ri->query_string);
  reg_string(L, "remote_ip", ri->remote_ip);
  reg_int(L, "remote_port", ri->remote_port);
Sergey Lyubka's avatar
Sergey Lyubka committed
3870 3871
  reg_string(L, "local_ip", ri->local_ip);
  reg_int(L, "local_port", ri->local_port);
3872
  lua_pushstring(L, "content");
Sergey Lyubka's avatar
Sergey Lyubka committed
3873
  lua_pushlstring(L, ri->content == NULL ? "" : ri->content, ri->content_len);
3874
  lua_rawset(L, -3);
3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886
  reg_int(L, "num_headers", ri->num_headers);
  lua_pushstring(L, "http_headers");
  lua_newtable(L);
  for (i = 0; i < ri->num_headers; i++) {
    reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
  }
  lua_rawset(L, -3);
  lua_rawset(L, -3);

  lua_setglobal(L, "mg");

  // Register default mg.onerror function
3887 3888
  (void) luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua "
                       "error:\\n', debug.traceback(e, 1)) end");
3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899
}

static int lua_error_handler(lua_State *L) {
  const char *error_msg =  lua_isstring(L, -1) ?  lua_tostring(L, -1) : "?\n";

  lua_getglobal(L, "mg");
  if (!lua_isnil(L, -1)) {
    lua_getfield(L, -1, "write");   // call mg.write()
    lua_pushstring(L, error_msg);
    lua_pushliteral(L, "\n");
    lua_call(L, 2, 0);
3900
    (void) luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
3901 3902
  } else {
    printf("Lua error: [%s]\n", error_msg);
3903
    (void) luaL_dostring(L, "print(debug.traceback(), '\\n')");
3904 3905 3906 3907 3908 3909
  }
  // TODO(lsm): leave the stack balanced

  return 0;
}

Sergey Lyubka's avatar
Sergey Lyubka committed
3910 3911 3912 3913 3914 3915 3916 3917
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);
3918
          if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), "") == 0) {
Sergey Lyubka's avatar
Sergey Lyubka committed
3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929
            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);
}
3930

Sergey Lyubka's avatar
Sergey Lyubka committed
3931 3932
static void handle_lsp_request(struct connection *conn, const char *path,
                               file_stat_t *st) {
3933
  void *p = MAP_FAILED;
Sergey Lyubka's avatar
Sergey Lyubka committed
3934 3935 3936 3937 3938 3939 3940
  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) {
3941
    send_http_error(conn, 500, "mmap(%s): %s", path, strerror(errno));
Sergey Lyubka's avatar
Sergey Lyubka committed
3942 3943
  } else {
    // We're not sending HTTP headers here, Lua page must do it.
3944
    prepare_lua_environment(&conn->mg_conn, L);
Sergey Lyubka's avatar
Sergey Lyubka committed
3945 3946
    conn->mg_conn.connection_param = L;
    call_user(conn, MG_LUA);
3947
    lua_pushcclosure(L, &lua_error_handler, 0);
3948
    lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
3949
    lsp(conn, p, (int) st->st_size, L);
Sergey Lyubka's avatar
Sergey Lyubka committed
3950
    close_local_endpoint(conn);
3951
  }
Sergey Lyubka's avatar
Sergey Lyubka committed
3952 3953

  if (L != NULL) lua_close(L);
3954
  if (p != MAP_FAILED) munmap(p, st->st_size);
Sergey Lyubka's avatar
Sergey Lyubka committed
3955
  if (fp != NULL) fclose(fp);
3956
}
3957
#endif // MONGOOSE_USE_LUA
3958

3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003 4004
#ifndef MONGOOSE_NO_SSI
static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);

static void send_file_data(struct mg_connection *conn, FILE *fp) {
  char buf[IOBUF_SIZE];
  int n;
  while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
    mg_write(conn, buf, n);
  }
}

static void do_ssi_include(struct mg_connection *conn, const char *ssi,
                           char *tag, int include_level) {
  char file_name[IOBUF_SIZE], path[MAX_PATH_SIZE], *p;
  char **opts = (MG_CONN_2_CONN(conn))->server->config_options;
  FILE *fp;

  // sscanf() is safe here, since send_ssi_file() also uses buffer
  // of size MG_BUF_LEN to get the tag. So strlen(tag) is always < MG_BUF_LEN.
  if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
    // File name is relative to the webserver root
    mg_snprintf(path, sizeof(path), "%s%c%s",
                opts[DOCUMENT_ROOT], '/', file_name);
  } else if (sscanf(tag, " abspath=\"%[^\"]\"", file_name) == 1) {
    // File name is relative to the webserver working directory
    // or it is absolute system path
    mg_snprintf(path, sizeof(path), "%s", file_name);
  } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1 ||
             sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
    // File name is relative to the currect document
    mg_snprintf(path, sizeof(path), "%s", ssi);
    if ((p = strrchr(path, '/')) != NULL) {
      p[1] = '\0';
    }
    mg_snprintf(path + strlen(path), sizeof(path) - strlen(path), "%s",
                file_name);
  } else {
    mg_printf(conn, "Bad SSI #include: [%s]", tag);
    return;
  }

  if ((fp = fopen(path, "rb")) == NULL) {
    mg_printf(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
              tag, path, strerror(errno));
  } else {
    ns_set_close_on_exec(fileno(fp));
4005 4006
    if (mg_match_prefix(opts[SSI_PATTERN], strlen(opts[SSI_PATTERN]),
        path) > 0) {
4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 4034 4035 4036 4037 4038 4039 4040 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115
      send_ssi_file(conn, path, fp, include_level + 1);
    } else {
      send_file_data(conn, fp);
    }
    fclose(fp);
  }
}

#ifndef MONGOOSE_NO_POPEN
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
  char cmd[IOBUF_SIZE];
  FILE *fp;

  if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
    mg_printf(conn, "Bad SSI #exec: [%s]", tag);
  } else if ((fp = popen(cmd, "r")) == NULL) {
    mg_printf(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(errno));
  } else {
    send_file_data(conn, fp);
    pclose(fp);
  }
}
#endif // !MONGOOSE_NO_POPEN

static void send_ssi_file(struct mg_connection *conn, const char *path,
                          FILE *fp, int include_level) {
  char buf[IOBUF_SIZE];
  int ch, offset, len, in_ssi_tag;

  if (include_level > 10) {
    mg_printf(conn, "SSI #include level is too deep (%s)", path);
    return;
  }

  in_ssi_tag = len = offset = 0;
  while ((ch = fgetc(fp)) != EOF) {
    if (in_ssi_tag && ch == '>') {
      in_ssi_tag = 0;
      buf[len++] = (char) ch;
      buf[len] = '\0';
      assert(len <= (int) sizeof(buf));
      if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
        // Not an SSI tag, pass it
        (void) mg_write(conn, buf, (size_t) len);
      } else {
        if (!memcmp(buf + 5, "include", 7)) {
          do_ssi_include(conn, path, buf + 12, include_level);
#if !defined(MONGOOSE_NO_POPEN)
        } else if (!memcmp(buf + 5, "exec", 4)) {
          do_ssi_exec(conn, buf + 9);
#endif // !NO_POPEN
        } else {
          mg_printf(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
        }
      }
      len = 0;
    } else if (in_ssi_tag) {
      if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
        // Not an SSI tag
        in_ssi_tag = 0;
      } else if (len == (int) sizeof(buf) - 2) {
        mg_printf(conn, "%s: SSI tag is too large", path);
        len = 0;
      }
      buf[len++] = ch & 0xff;
    } else if (ch == '<') {
      in_ssi_tag = 1;
      if (len > 0) {
        mg_write(conn, buf, (size_t) len);
      }
      len = 0;
      buf[len++] = ch & 0xff;
    } else {
      buf[len++] = ch & 0xff;
      if (len == (int) sizeof(buf)) {
        mg_write(conn, buf, (size_t) len);
        len = 0;
      }
    }
  }

  // Send the rest of buffered data
  if (len > 0) {
    mg_write(conn, buf, (size_t) len);
  }
}

static void handle_ssi_request(struct connection *conn, const char *path) {
  FILE *fp;
  struct vec mime_vec;

  if ((fp = fopen(path, "rb")) == NULL) {
    send_http_error(conn, 500, "fopen(%s): %s", path, strerror(errno));
  } else {
    ns_set_close_on_exec(fileno(fp));
    get_mime_type(conn->server, path, &mime_vec);
    conn->mg_conn.status_code = 200;
    mg_printf(&conn->mg_conn,
              "HTTP/1.1 %d OK\r\n"
              "Content-Type: %.*s\r\n"
              "Connection: close\r\n\r\n",
              conn->mg_conn.status_code, (int) mime_vec.len, mime_vec.ptr);
    send_ssi_file(&conn->mg_conn, path, fp, 0);
    fclose(fp);
    close_local_endpoint(conn);
  }
}
#endif

4116
static void open_local_endpoint(struct connection *conn, int skip_user) {
4117
#ifndef MONGOOSE_NO_FILESYSTEM
4118
  static const char lua_pat[] = LUA_SCRIPT_PATTERN;
4119
  file_stat_t st;
Sergey Lyubka's avatar
Sergey Lyubka committed
4120
  char path[MAX_PATH_SIZE];
4121
  int exists = 0, is_directory = 0;
4122
#ifndef MONGOOSE_NO_CGI
4123
  const char *cgi_pat = conn->server->config_options[CGI_PATTERN];
4124 4125 4126 4127
#else
  const char *cgi_pat = DEFAULT_CGI_PATTERN;
#endif
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
4128
  const char *dir_lst = conn->server->config_options[ENABLE_DIRECTORY_LISTING];
4129 4130 4131
#else
  const char *dir_lst = "yes";
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
4132
#endif
4133

4134 4135 4136
  // If EP_USER was set in a prev call, reset it
  conn->endpoint_type = EP_NONE;

4137
#ifndef MONGOOSE_NO_AUTH
4138
  if (conn->server->event_handler && call_user(conn, MG_AUTH) == MG_FALSE) {
4139 4140 4141
    mg_send_digest_auth_request(&conn->mg_conn);
    return;
  }
4142
#endif
4143

4144
  // Call URI handler if one is registered for this URI
4145
  if (skip_user == 0 && conn->server->event_handler != NULL) {
4146
    conn->endpoint_type = EP_USER;
Sergey Lyubka's avatar
Sergey Lyubka committed
4147
#if MONGOOSE_POST_SIZE_LIMIT > 1
4148 4149 4150
    {
      const char *cl = mg_get_header(&conn->mg_conn, "Content-Length");
      if (!strcmp(conn->mg_conn.request_method, "POST") &&
Sergey Lyubka's avatar
Sergey Lyubka committed
4151
          (cl == NULL || to64(cl) > MONGOOSE_POST_SIZE_LIMIT)) {
4152
        send_http_error(conn, 500, "POST size > %zu",
Sergey Lyubka's avatar
Sergey Lyubka committed
4153
                        (size_t) MONGOOSE_POST_SIZE_LIMIT);
4154 4155 4156
      }
    }
#endif
4157
    return;
4158 4159
  }

4160
#ifdef MONGOOSE_NO_FILESYSTEM
4161 4162 4163 4164 4165
  if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
    send_options(conn);
  } else {
    send_http_error(conn, 404, NULL);
  }
Sergey Lyubka's avatar
Sergey Lyubka committed
4166
#else
4167 4168
  exists = convert_uri_to_file_name(conn, path, sizeof(path), &st);
  is_directory = S_ISDIR(st.st_mode);
4169

4170 4171 4172
  if (!strcmp(conn->mg_conn.request_method, "OPTIONS")) {
    send_options(conn);
  } else if (conn->server->config_options[DOCUMENT_ROOT] == NULL) {
4173
    send_http_error(conn, 404, NULL);
4174
#ifndef MONGOOSE_NO_AUTH
4175 4176 4177 4178
  } else if ((!is_dav_mutation(conn) && !is_authorized(conn, path)) ||
             (is_dav_mutation(conn) && !is_authorized_for_dav(conn))) {
    mg_send_digest_auth_request(&conn->mg_conn);
    close_local_endpoint(conn);
4179
#endif
4180
#ifndef MONGOOSE_NO_DAV
4181 4182 4183 4184 4185 4186 4187 4188
  } 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);
4189
#endif
4190
  } else if (!exists || must_hide_file(conn, path)) {
4191
    send_http_error(conn, 404, NULL);
4192 4193
  } else if (is_directory &&
             conn->mg_conn.uri[strlen(conn->mg_conn.uri) - 1] != '/') {
Sergey Lyubka's avatar
Sergey Lyubka committed
4194 4195 4196 4197
    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);
4198 4199
  } else if (is_directory && !find_index_file(conn, path, sizeof(path), &st)) {
    if (!mg_strcasecmp(dir_lst, "yes")) {
4200
#ifndef MONGOOSE_NO_DIRECTORY_LISTING
4201 4202
      send_directory_listing(conn, path);
#else
4203
      send_http_error(conn, 501, NULL);
4204 4205
#endif
    } else {
4206
      send_http_error(conn, 403, NULL);
4207
    }
4208
  } else if (mg_match_prefix(lua_pat, sizeof(lua_pat) - 1, path) > 0) {
4209
#ifdef MONGOOSE_USE_LUA
Sergey Lyubka's avatar
Sergey Lyubka committed
4210
    handle_lsp_request(conn, path, &st);
4211
#else
4212
    send_http_error(conn, 501, NULL);
4213
#endif
4214
  } else if (mg_match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
4215
#if !defined(MONGOOSE_NO_CGI)
4216
    open_cgi_endpoint(conn, path);
4217
#else
4218
    send_http_error(conn, 501, NULL);
4219
#endif // !MONGOOSE_NO_CGI
4220
#ifndef MONGOOSE_NO_SSI
4221 4222 4223
  } else if (mg_match_prefix(conn->server->config_options[SSI_PATTERN],
                             strlen(conn->server->config_options[SSI_PATTERN]),
                             path) > 0) {
4224 4225
    handle_ssi_request(conn, path);
#endif
4226
  } else if (is_not_modified(conn, &st)) {
4227
    send_http_error(conn, 304, NULL);
4228 4229 4230 4231 4232
  } 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 {
4233
    send_http_error(conn, 404, NULL);
4234
  }
4235
#endif  // MONGOOSE_NO_FILESYSTEM
4236
}
4237

4238 4239 4240
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");
4241

4242
  if (expect_hdr != NULL && !mg_strcasecmp(expect_hdr, "100-continue")) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4243
    ns_send(conn->ns_conn, expect_response, sizeof(expect_response) - 1);
4244
  }
4245
}
4246

4247 4248 4249 4250 4251
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');
}
4252

Sergey Lyubka's avatar
Sergey Lyubka committed
4253 4254
static void try_parse(struct connection *conn) {
  struct iobuf *io = &conn->ns_conn->recv_iobuf;
4255 4256 4257 4258 4259 4260 4261 4262

  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);
4263
    DBG(("%p [%.*s]", conn, conn->request_len, conn->request));
Sergey Lyubka's avatar
Sergey Lyubka committed
4264
    iobuf_remove(io, conn->request_len);
4265 4266
    conn->request_len = parse_http_message(conn->request, conn->request_len,
                                           &conn->mg_conn);
4267 4268 4269 4270 4271
    if (conn->request_len > 0) {
      const char *cl_hdr = mg_get_header(&conn->mg_conn, "Content-Length");
      conn->cl = cl_hdr == NULL ? 0 : to64(cl_hdr);
      conn->mg_conn.content_len = (long int) conn->cl;
    }
4272
  }
4273
}
4274

4275
static void process_request(struct connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4276
  struct iobuf *io = &conn->ns_conn->recv_iobuf;
4277

Sergey Lyubka's avatar
Sergey Lyubka committed
4278 4279 4280
  try_parse(conn);
  DBG(("%p %d %d %d [%.*s]", conn, conn->request_len, io->len,
       conn->ns_conn->flags, io->len, io->buf));
4281 4282
  if (conn->request_len < 0 ||
      (conn->request_len > 0 && !is_valid_uri(conn->mg_conn.uri))) {
4283
    send_http_error(conn, 400, NULL);
4284
  } else if (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE) {
4285
    send_http_error(conn, 413, NULL);
4286 4287 4288
  } else if (conn->request_len > 0 &&
             strcmp(conn->mg_conn.http_version, "1.0") != 0 &&
             strcmp(conn->mg_conn.http_version, "1.1") != 0) {
4289
    send_http_error(conn, 505, NULL);
4290
  } else if (conn->request_len > 0 && conn->endpoint_type == EP_NONE) {
4291
#ifndef MONGOOSE_NO_WEBSOCKET
4292 4293 4294
    send_websocket_handshake_if_requested(&conn->mg_conn);
#endif
    send_continue_if_expected(conn);
4295
    open_local_endpoint(conn, 0);
4296 4297
  }

4298
#ifndef MONGOOSE_NO_CGI
4299 4300 4301 4302 4303
  if (conn->endpoint_type == EP_CGI && io->len > 0) {
    forward_post_data(conn);
  }
#endif
  if (conn->endpoint_type == EP_USER) {
4304
    call_request_handler_if_data_is_buffered(conn);
4305
  }
4306
#ifndef MONGOOSE_NO_DAV
4307 4308
  if (conn->endpoint_type == EP_PUT && io->len > 0) {
    forward_put_data(conn);
4309
  }
4310
#endif
4311 4312
}

4313 4314
static void call_http_client_handler(struct connection *conn) {
  //conn->mg_conn.status_code = code;
4315
  // For responses without Content-Lengh, use the whole buffer
4316
  if (conn->cl == 0) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4317
    conn->mg_conn.content_len = conn->ns_conn->recv_iobuf.len;
4318
  }
Sergey Lyubka's avatar
Sergey Lyubka committed
4319
  conn->mg_conn.content = conn->ns_conn->recv_iobuf.buf;
4320
  if (call_user(conn, MG_REPLY) == MG_FALSE) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4321
    conn->ns_conn->flags |= NSF_CLOSE_IMMEDIATELY;
4322
  }
Sergey Lyubka's avatar
Sergey Lyubka committed
4323 4324
  iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
  conn->mg_conn.status_code = 0;
4325 4326 4327 4328 4329 4330
  conn->cl = conn->num_bytes_sent = conn->request_len = 0;
  free(conn->request);
  conn->request = NULL;
}

static void process_response(struct connection *conn) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4331
  struct iobuf *io = &conn->ns_conn->recv_iobuf;
4332

Sergey Lyubka's avatar
Sergey Lyubka committed
4333
  try_parse(conn);
4334 4335 4336 4337
  DBG(("%p %d %d [%.*s]", conn, conn->request_len, io->len,
       io->len > 40 ? 40 : io->len, io->buf));
  if (conn->request_len < 0 ||
      (conn->request_len == 0 && io->len > MAX_REQUEST_SIZE)) {
4338 4339 4340
    call_http_client_handler(conn);
  } else if (io->len >= conn->cl) {
    call_http_client_handler(conn);
4341 4342 4343
  }
}

4344 4345
struct mg_connection *mg_connect(struct mg_server *server, const char *host,
                                 int port, int use_ssl) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4346 4347
  struct ns_connection *nsconn;
  struct connection *conn;
Sergey Lyubka's avatar
Sergey Lyubka committed
4348

4349
  nsconn = ns_connect(&server->ns_server, host, port, use_ssl, NULL);
Sergey Lyubka's avatar
Sergey Lyubka committed
4350
  if (nsconn == NULL) return 0;
Sergey Lyubka's avatar
Sergey Lyubka committed
4351

Sergey Lyubka's avatar
Sergey Lyubka committed
4352 4353
  if ((conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
    nsconn->flags |= NSF_CLOSE_IMMEDIATELY;
Sergey Lyubka's avatar
Sergey Lyubka committed
4354 4355 4356
    return 0;
  }

Sergey Lyubka's avatar
Sergey Lyubka committed
4357 4358 4359 4360
  // Interlink two structs
  conn->ns_conn = nsconn;
  nsconn->connection_data = conn;

Sergey Lyubka's avatar
Sergey Lyubka committed
4361
  conn->server = server;
Sergey Lyubka's avatar
Sergey Lyubka committed
4362
  conn->endpoint_type = EP_CLIENT;
4363
  //conn->handler = handler;
Sergey Lyubka's avatar
Sergey Lyubka committed
4364 4365
  conn->mg_conn.server_param = server->ns_server.server_data;
  conn->ns_conn->flags = NSF_CONNECTING;
Sergey Lyubka's avatar
Sergey Lyubka committed
4366

4367
  return &conn->mg_conn;
Sergey Lyubka's avatar
Sergey Lyubka committed
4368 4369
}

4370
#ifndef MONGOOSE_NO_LOGGING
4371 4372 4373
static void log_header(const struct mg_connection *conn, const char *header,
                       FILE *fp) {
  const char *header_value;
4374

4375 4376 4377 4378
  if ((header_value = mg_get_header(conn, header)) == NULL) {
    (void) fprintf(fp, "%s", " -");
  } else {
    (void) fprintf(fp, " \"%s\"", header_value);
4379
  }
4380
}
4381

4382 4383 4384
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
4385
  char date[64], user[100];
4386
  time_t now;
4387

4388
  if (fp == NULL) return;
4389 4390
  now = time(NULL);
  strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", localtime(&now));
4391

4392 4393 4394
  flockfile(fp);
  mg_parse_header(mg_get_header(&conn->mg_conn, "Authorization"), "username",
                  user, sizeof(user));
4395
  fprintf(fp, "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT,
Sergey Lyubka's avatar
Sergey Lyubka committed
4396
          c->remote_ip, user[0] == '\0' ? "-" : user, date,
4397
          c->request_method ? c->request_method : "-",
4398 4399 4400
          c->uri ? c->uri : "-", c->query_string ? "?" : "",
          c->query_string ? c->query_string : "",
          c->http_version, c->status_code, conn->num_bytes_sent);
4401 4402 4403 4404
  log_header(c, "Referer", fp);
  log_header(c, "User-Agent", fp);
  fputc('\n', fp);
  fflush(fp);
4405

4406 4407
  funlockfile(fp);
  fclose(fp);
4408 4409 4410
}
#endif

4411
static void close_local_endpoint(struct connection *conn) {
4412
  struct mg_connection *c = &conn->mg_conn;
4413 4414
  // Must be done before free()
  int keep_alive = should_keep_alive(&conn->mg_conn) &&
4415
    (conn->endpoint_type == EP_FILE || conn->endpoint_type == EP_USER);
Sergey Lyubka's avatar
Sergey Lyubka committed
4416 4417
  DBG(("%p %d %d %d", conn, conn->endpoint_type, keep_alive,
       conn->ns_conn->flags));
4418

4419
  switch (conn->endpoint_type) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4420 4421 4422 4423 4424 4425 4426 4427 4428 4429
    case EP_PUT:
    case EP_FILE:
      close(conn->endpoint.fd);
      break;
    case EP_CGI:
      if (conn->endpoint.cgi_conn != NULL) {
        conn->endpoint.cgi_conn->flags |= NSF_CLOSE_IMMEDIATELY;
        conn->endpoint.cgi_conn->connection_data = NULL;
      }
      break;
4430 4431
    default: break;
  }
4432

4433
#ifndef MONGOOSE_NO_LOGGING
4434 4435
  if (c->status_code > 0 && conn->endpoint_type != EP_CLIENT &&
      c->status_code != 400) {
4436 4437 4438
    log_access(conn, conn->server->config_options[ACCESS_LOG_FILE]);
  }
#endif
4439

4440
  // Gobble possible POST data sent to the URI handler
Sergey Lyubka's avatar
Sergey Lyubka committed
4441
  iobuf_remove(&conn->ns_conn->recv_iobuf, conn->mg_conn.content_len);
4442
  conn->endpoint_type = EP_NONE;
Sergey Lyubka's avatar
Sergey Lyubka committed
4443
  conn->cl = conn->num_bytes_sent = conn->request_len = 0;
4444 4445 4446
  conn->ns_conn->flags &= ~(NSF_FINISHED_SENDING_DATA |
                            NSF_BUFFER_BUT_DONT_SEND | NSF_CLOSE_IMMEDIATELY |
                            MG_HEADERS_SENT | MG_LONG_RUNNING);
4447 4448
  c->request_method = c->uri = c->http_version = c->query_string = NULL;
  c->num_headers = c->status_code = c->is_websocket = c->content_len = 0;
Sergey Lyubka's avatar
Sergey Lyubka committed
4449 4450
  free(conn->request); conn->request = NULL;
  free(conn->path_info); conn->path_info = NULL;
4451

4452 4453
  if (keep_alive) {
    process_request(conn);  // Can call us recursively if pipelining is used
4454
  } else {
Sergey Lyubka's avatar
Sergey Lyubka committed
4455 4456
    conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len == 0 ?
      NSF_CLOSE_IMMEDIATELY : NSF_FINISHED_SENDING_DATA;
4457 4458 4459
  }
}

4460 4461
static void transfer_file_data(struct connection *conn) {
  char buf[IOBUF_SIZE];
Sergey Lyubka's avatar
Sergey Lyubka committed
4462 4463
  int n = read(conn->endpoint.fd, buf, conn->cl < (int64_t) sizeof(buf) ?
               (int) conn->cl : (int) sizeof(buf));
4464

Sergey Lyubka's avatar
Sergey Lyubka committed
4465
  if (n <= 0) {
4466 4467 4468
    close_local_endpoint(conn);
  } else if (n > 0) {
    conn->cl -= n;
Sergey Lyubka's avatar
Sergey Lyubka committed
4469
    ns_send(conn->ns_conn, buf, n);
4470 4471
    if (conn->cl <= 0) {
      close_local_endpoint(conn);
4472 4473 4474 4475
    }
  }
}

Sergey Lyubka's avatar
Sergey Lyubka committed
4476 4477
int mg_poll_server(struct mg_server *server, int milliseconds) {
  return ns_server_poll(&server->ns_server, milliseconds);
4478 4479
}

4480 4481
void mg_destroy_server(struct mg_server **server) {
  if (server != NULL && *server != NULL) {
4482
    struct mg_server *s = *server;
Sergey Lyubka's avatar
Sergey Lyubka committed
4483 4484 4485
    int i;

    ns_server_free(&s->ns_server);
4486 4487
    for (i = 0; i < (int) ARRAY_SIZE(s->config_options); i++) {
      free(s->config_options[i]);  // It is OK to free(NULL)
4488
    }
4489
    free(s);
4490
    *server = NULL;
4491 4492 4493
  }
}

Sergey Lyubka's avatar
Sergey Lyubka committed
4494 4495 4496 4497
struct mg_iterator {
  mg_handler_t cb;
  void *param;
};
4498
union variant { mg_handler_t cb; void *p; };
Sergey Lyubka's avatar
Sergey Lyubka committed
4499 4500 4501

static void iter(struct ns_connection *nsconn, enum ns_event ev, void *param) {
  if (ev == NS_POLL) {
4502
    union variant *variant = (union variant *) param;
Sergey Lyubka's avatar
Sergey Lyubka committed
4503
    struct connection *c = (struct connection *) nsconn->connection_data;
4504
    variant->cb(&c->mg_conn, MG_POLL);
Sergey Lyubka's avatar
Sergey Lyubka committed
4505 4506 4507
  }
}

4508
// Apply function to all active connections.
4509 4510 4511
void mg_iterate_over_connections(struct mg_server *server, mg_handler_t cb) {
  union variant variant = { cb };
  ns_iterate(&server->ns_server, iter, &variant);
4512 4513
}

4514 4515 4516 4517 4518
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;
4519

4520 4521 4522 4523 4524 4525 4526 4527 4528 4529
  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';
4530

4531 4532 4533 4534
    // 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)) {
4535

4536 4537
        // Point p to variable value
        p += name_len + 1;
4538

4539 4540 4541 4542 4543 4544
        // 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);
4545

4546 4547
        // Decode variable into destination buffer
        len = mg_url_decode(p, (size_t)(s - p), dst, dst_len, 1);
4548

4549 4550 4551 4552 4553 4554 4555 4556
        // Redirect error code from -1 to -2 (destination buffer too small).
        if (len == -1) {
          len = -2;
        }
        break;
      }
    }
  }
4557

4558 4559
  return len;
}
4560

4561 4562 4563 4564 4565 4566
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);
4567
  }
4568 4569
  return len;
}
4570

Sergey Lyubka's avatar
Sergey Lyubka committed
4571 4572 4573 4574 4575 4576 4577 4578 4579 4580 4581 4582 4583 4584 4585 4586 4587 4588 4589 4590 4591 4592 4593 4594 4595 4596 4597 4598 4599 4600 4601 4602 4603 4604 4605 4606 4607 4608 4609 4610 4611 4612 4613 4614 4615
static int get_line_len(const char *buf, int buf_len) {
  int len = 0;
  while (len < buf_len && buf[len] != '\n') len++;
  return buf[len] == '\n' ? len + 1: -1;
}

int mg_parse_multipart(const char *buf, int buf_len,
                       char *var_name, int var_name_len,
                       char *file_name, int file_name_len,
                       const char **data, int *data_len) {
  static const char cd[] = "Content-Disposition: ";
  //struct mg_connection c;
  int hl, bl, n, ll, pos, cdl = sizeof(cd) - 1;
  //char *p;

  if (buf == NULL || buf_len <= 0) return 0;
  if ((hl = get_request_len(buf, buf_len)) <= 0) return 0;
  if (buf[0] != '-' || buf[1] != '-' || buf[2] == '\n') return 0;

  // Get boundary length
  bl = get_line_len(buf, buf_len);

  // Loop through headers, fetch variable name and file name
  var_name[0] = file_name[0] = '\0';
  for (n = bl; (ll = get_line_len(buf + n, hl - n)) > 0; n += ll) {
    if (mg_strncasecmp(cd, buf + n, cdl) == 0) {
      parse_header(buf + n + cdl, ll - (cdl + 2), "name",
                   var_name, var_name_len);
      parse_header(buf + n + cdl, ll - (cdl + 2), "filename",
                   file_name, file_name_len);
    }
  }

  // Scan body, search for terminating boundary
  for (pos = hl; pos + (bl - 2) < buf_len; pos++) {
    if (buf[pos] == '-' && !memcmp(buf, &buf[pos], bl - 2)) {
      if (data_len != NULL) *data_len = (pos - 2) - hl;
      if (data != NULL) *data = buf + hl;
      return pos;
    }
  }

  return 0;
}

4616 4617
const char **mg_get_valid_option_names(void) {
  return static_config_options;
4618 4619
}

4620
static int get_option_index(const char *name) {
4621
  int i;
4622 4623 4624 4625

  for (i = 0; static_config_options[i * 2] != NULL; i++) {
    if (strcmp(static_config_options[i * 2], name) == 0) {
      return i;
4626
    }
4627 4628 4629 4630 4631 4632 4633
  }
  return -1;
}

static void set_default_option_values(char **opts) {
  const char *value, **all_opts = mg_get_valid_option_names();
  int i;
4634

4635 4636 4637 4638
  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);
4639 4640 4641 4642
    }
  }
}

4643 4644 4645 4646 4647 4648 4649
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";
4650
  } else {
4651 4652 4653 4654
    if (server->config_options[ind] != NULL) {
      free(server->config_options[ind]);
    }
    server->config_options[ind] = mg_strdup(value);
4655
    DBG(("%s [%s]", name, value));
4656 4657

    if (ind == LISTENING_PORT) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4658 4659
      int port = ns_bind(&server->ns_server, value);
      if (port < 0) {
4660
        error_msg = "Cannot bind to port";
4661 4662
      } else {
        sockaddr_to_string(server->local_ip, sizeof(server->local_ip),
Sergey Lyubka's avatar
Sergey Lyubka committed
4663
                           &server->ns_server.listening_sa);
4664 4665
        if (!strcmp(value, "0")) {
          char buf[10];
Sergey Lyubka's avatar
Sergey Lyubka committed
4666
          mg_snprintf(buf, sizeof(buf), "%d", port);
4667 4668 4669
          free(server->config_options[ind]);
          server->config_options[ind] = mg_strdup(buf);
        }
4670
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
4671
#ifndef _WIN32
4672
    } else if (ind == RUN_AS_USER) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4673 4674 4675 4676 4677 4678 4679 4680
      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
4681
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
4682
#ifdef NS_ENABLE_SSL
4683
    } else if (ind == SSL_CERTIFICATE) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4684 4685 4686 4687 4688 4689
      int res = ns_set_ssl_cert(&server->ns_server, value);
      if (res == -2) {
        error_msg = "Cannot load PEM";
      } else if (res == -3) {
        error_msg = "SSL not enabled";
      } else if (res == -1) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4690 4691
        error_msg = "SSL_CTX_new() failed";
      }
Sergey Lyubka's avatar
Sergey Lyubka committed
4692
#endif
4693 4694 4695
    }
  }

4696 4697 4698
  return error_msg;
}

Sergey Lyubka's avatar
Sergey Lyubka committed
4699 4700 4701 4702 4703 4704 4705 4706 4707 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731
static void on_accept(struct ns_connection *nc, union socket_address *sa) {
  struct mg_server *server = (struct mg_server *) nc->server;
  struct connection *conn;

  if (!check_acl(server->config_options[ACCESS_CONTROL_LIST],
                 ntohl(* (uint32_t *) &sa->sin.sin_addr)) ||
      (conn = (struct connection *) calloc(1, sizeof(*conn))) == NULL) {
    nc->flags |= NSF_CLOSE_IMMEDIATELY;
  } else {
    conn->server = (struct mg_server *) nc->server;
    sockaddr_to_string(conn->mg_conn.remote_ip,
                       sizeof(conn->mg_conn.remote_ip), sa);
    conn->mg_conn.remote_port = ntohs(sa->sin.sin_port);
    conn->mg_conn.server_param = nc->server->server_data;
    conn->mg_conn.local_ip = server->local_ip;
    conn->mg_conn.local_port =
      ntohs(server->ns_server.listening_sa.sin.sin_port);

    // Circularly link two connection structures
    nc->connection_data = conn;
    conn->ns_conn = nc;
  }
}

static void mg_ev_handler(struct ns_connection *nc, enum ns_event ev, void *p) {
  struct connection *conn = (struct connection *) nc->connection_data;

  switch (ev) {
    case NS_ACCEPT:
      on_accept(nc, (union socket_address *) p);
      break;

    case NS_CONNECT:
Sergey Lyubka's avatar
Sergey Lyubka committed
4732 4733 4734 4735
      conn->mg_conn.status_code = * (int *) p;
      if (conn->mg_conn.status_code != 0 ||
          call_user(conn, MG_CONNECT) == MG_FALSE) {
        nc->flags |= NSF_CLOSE_IMMEDIATELY;
Sergey Lyubka's avatar
Sergey Lyubka committed
4736 4737 4738 4739 4740 4741
      }
      break;

    case NS_RECV:
      if (nc->flags & NSF_ACCEPTED) {
        process_request(conn);
4742
#ifndef MONGOOSE_NO_CGI
Sergey Lyubka's avatar
Sergey Lyubka committed
4743 4744
      } else if (nc->flags & MG_CGI_CONN) {
        on_cgi_data(nc);
4745
#endif
Sergey Lyubka's avatar
Sergey Lyubka committed
4746 4747 4748 4749 4750 4751 4752 4753 4754 4755 4756
      } else {
        process_response(conn);
      }
      break;

    case NS_SEND:
      break;

    case NS_CLOSE:
      nc->connection_data = NULL;
      if ((nc->flags & MG_CGI_CONN) && conn && conn->ns_conn) {
4757
        conn->ns_conn->flags &= ~NSF_BUFFER_BUT_DONT_SEND;
Sergey Lyubka's avatar
Sergey Lyubka committed
4758 4759 4760 4761 4762 4763 4764
        conn->ns_conn->flags |= conn->ns_conn->send_iobuf.len > 0 ?
          NSF_FINISHED_SENDING_DATA : NSF_CLOSE_IMMEDIATELY;
        conn->endpoint.cgi_conn = NULL;
      } else if (conn != NULL) {
        DBG(("%p %d closing", conn, conn->endpoint_type));

        if (conn->endpoint_type == EP_CLIENT && nc->recv_iobuf.len > 0) {
4765
          call_http_client_handler(conn);
Sergey Lyubka's avatar
Sergey Lyubka committed
4766 4767
        }

4768
        call_user(conn, MG_CLOSE);
Sergey Lyubka's avatar
Sergey Lyubka committed
4769 4770 4771 4772 4773 4774
        close_local_endpoint(conn);
        free(conn);
      }
      break;

    case NS_POLL:
Sergey Lyubka's avatar
Sergey Lyubka committed
4775 4776 4777 4778
      if (call_user(conn, MG_POLL) == MG_TRUE) {
        nc->flags |= NSF_FINISHED_SENDING_DATA;
      }

Sergey Lyubka's avatar
Sergey Lyubka committed
4779 4780 4781 4782 4783 4784 4785 4786
      if (conn != NULL && conn->endpoint_type == EP_FILE) {
        transfer_file_data(conn);
      }

      // Expire idle connections
      {
        time_t current_time = * (time_t *) p;

4787
        if (conn != NULL && conn->mg_conn.is_websocket) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4788 4789 4790 4791
          ping_idle_websocket_connection(conn, current_time);
        }

        if (nc->last_io_time + MONGOOSE_IDLE_TIMEOUT_SECONDS < current_time) {
4792
          mg_ev_handler(nc, NS_CLOSE, NULL);
Sergey Lyubka's avatar
Sergey Lyubka committed
4793 4794 4795 4796 4797 4798 4799 4800 4801 4802
          nc->flags |= NSF_CLOSE_IMMEDIATELY;
        }
      }
      break;

    default:
      break;
  }
}

Sergey Lyubka's avatar
Sergey Lyubka committed
4803 4804 4805 4806
void mg_wakeup_server(struct mg_server *server) {
  ns_server_wakeup(&server->ns_server);
}

4807
void mg_set_listening_socket(struct mg_server *server, int sock) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4808 4809
  if (server->ns_server.listening_sock != INVALID_SOCKET) {
    closesocket(server->ns_server.listening_sock);
4810
  }
Sergey Lyubka's avatar
Sergey Lyubka committed
4811
  server->ns_server.listening_sock = (sock_t) sock;
4812 4813 4814
}

int mg_get_listening_socket(struct mg_server *server) {
Sergey Lyubka's avatar
Sergey Lyubka committed
4815
  return server->ns_server.listening_sock;
4816 4817
}

4818 4819 4820 4821 4822 4823
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];
}

4824
struct mg_server *mg_create_server(void *server_data, mg_handler_t handler) {
4825
  struct mg_server *server = (struct mg_server *) calloc(1, sizeof(*server));
Sergey Lyubka's avatar
Sergey Lyubka committed
4826
  ns_server_init(&server->ns_server, server_data, mg_ev_handler);
4827
  set_default_option_values(server->config_options);
4828
  server->event_handler = handler;
4829
  return server;
4830
}