/*
 * libwebsockets-test-server - libwebsockets test implementation
 *
 * Copyright (C) 2011-2016 Andy Green <andy@warmcat.com>
 *
 * This file is made available under the Creative Commons CC0 1.0
 * Universal Public Domain Dedication.
 *
 * The person who associated a work with this deed has dedicated
 * the work to the public domain by waiving all of his or her rights
 * to the work worldwide under copyright law, including all related
 * and neighboring rights, to the extent allowed by law. You can copy,
 * modify, distribute and perform the work, even for commercial purposes,
 * all without asking permission.
 *
 * The test apps are intended to be adapted for use in your code, which
 * may be proprietary.  So unlike the library itself, they are licensed
 * Public Domain.
 */
#include "test-server.h"

int close_testing;
int max_poll_elements;
int debug_level = 7;
volatile int force_exit = 0;
struct lws_context *context;
struct lws_plat_file_ops fops_plat;

/* http server gets files from this path */
#define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server"
char *resource_path = LOCAL_RESOURCE_PATH;

#if defined(LWS_OPENSSL_SUPPORT) && defined(LWS_HAVE_SSL_CTX_set1_param)
char crl_path[1024] = "";
#endif

/* singlethreaded version --> no locks */

void test_server_lock(int care)
{
}
void test_server_unlock(int care)
{
}

/*
 * This demo server shows how to use libwebsockets for one or more
 * websocket protocols in the same server
 *
 * It defines the following websocket protocols:
 *
 *  dumb-increment-protocol:  once the socket is opened, an incrementing
 *        ascii string is sent down it every 50ms.
 *        If you send "reset\n" on the websocket, then
 *        the incrementing number is reset to 0.
 *
 *  lws-mirror-protocol: copies any received packet to every connection also
 *        using this protocol, including the sender
 */

enum demo_protocols {
  /* always first */
  PROTOCOL_HTTP = 0,

  PROTOCOL_DUMB_INCREMENT,
  PROTOCOL_LWS_MIRROR,

  /* always last */
  DEMO_PROTOCOL_COUNT
};

/* list of supported protocols and callbacks */

static struct lws_protocols protocols[] = {
  /* first protocol must always be HTTP handler */

  {
    "http-only",    /* name */
    callback_http,    /* callback */
    sizeof (struct per_session_data__http),  /* per_session_data_size */
    0,      /* max frame size / rx buffer */
  },
  {
    "dumb-increment-protocol",
    callback_dumb_increment,
    sizeof(struct per_session_data__dumb_increment),
    10,
  },
  {
    "lws-mirror-protocol",
    callback_lws_mirror,
    sizeof(struct per_session_data__lws_mirror),
    128,
  },
  {
    "lws-status",
    callback_lws_status,
    sizeof(struct per_session_data__lws_status),
    128,
  },
  { NULL, NULL, 0, 0 } /* terminator */
};

static const struct lws_extension exts[] = {
  {
    "permessage-deflate",
    lws_extension_callback_pm_deflate,
    "permessage-deflate; client_no_context_takeover; client_max_window_bits"
  },
  {
    "deflate-frame",
    lws_extension_callback_pm_deflate,
    "deflate_frame"
  },
  { NULL, NULL, NULL /* terminator */ }
};

/* this shows how to override the lws file operations.  You don't need
 * to do any of this unless you have a reason (eg, want to serve
 * compressed files without decompressing the whole archive)
 */
static lws_fop_fd_t
test_server_fops_open(const struct lws_plat_file_ops *fops,
          const char *vfs_path, const char *vpath,
          lws_fop_flags_t *flags)
{
  lws_fop_fd_t n;

  /* call through to original platform implementation */
  n = fops_plat.open(fops, vfs_path, vpath, flags);

  lwsl_notice("%s: opening %s, ret %p\n", __func__, vfs_path, n);

  return n;
}

void signal_cb(evutil_socket_t sock_fd, short events, void *ctx)
{
  lwsl_notice("Signal caught, exiting...\n");
  force_exit = 1;
  if (events & EV_SIGNAL) {
    struct event_base *event_base_loop = event_get_base((struct event *) ctx);
    event_base_loopbreak(event_base_loop);
  }
}

static void
ev_timeout_cb (evutil_socket_t sock_fd, short events, void *ctx)
{
  lws_callback_on_writable_all_protocol(context,
          &protocols[PROTOCOL_DUMB_INCREMENT]);
}

static struct option options[] = {
  { "help",  no_argument,    NULL, 'h' },
  { "debug",  required_argument,  NULL, 'd' },
  { "port",  required_argument,  NULL, 'p' },
  { "ssl",  no_argument,    NULL, 's' },
  { "allow-non-ssl",  no_argument,  NULL, 'a' },
  { "interface",  required_argument,  NULL, 'i' },
  { "closetest",  no_argument,    NULL, 'c' },
  { "libevent",  no_argument,    NULL, 'e' },
#ifndef LWS_NO_DAEMONIZE
  { "daemonize",   no_argument,    NULL, 'D' },
#endif
  { "resource_path", required_argument,  NULL, 'r' },
  { NULL, 0, 0, 0 }
};

int main(int argc, char **argv)
{
  int sigs[] = { SIGINT, SIGKILL, SIGTERM, SIGSEGV, SIGFPE };
  struct event *signals[ARRAY_SIZE(sigs)];
  struct event_base *event_base_loop = event_base_new();
  struct lws_context_creation_info info;
  char interface_name[128] = "";
  const char *iface = NULL;
  struct event *timeout_watcher;
  char cert_path[1024];
  char key_path[1024];
  int use_ssl = 0;
  int opts = 0;
  int n = 0;
#ifndef _WIN32
  int syslog_options = LOG_PID | LOG_PERROR;
#endif
#ifndef LWS_NO_DAEMONIZE
  int daemonize = 0;
#endif

  /*
   * take care to zero down the info struct, he contains random garbaage
   * from the stack otherwise
   */
  memset(&info, 0, sizeof info);
  info.port = 7681;

  while (n >= 0) {
    n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL);
    if (n < 0)
      continue;
    switch (n) {
    case 'e':
      opts |= LWS_SERVER_OPTION_LIBEVENT;
      break;
#ifndef LWS_NO_DAEMONIZE
    case 'D':
      daemonize = 1;
      #ifndef _WIN32
      syslog_options &= ~LOG_PERROR;
      #endif
      break;
#endif
    case 'd':
      debug_level = atoi(optarg);
      break;
    case 's':
      use_ssl = 1;
      break;
    case 'a':
      opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT;
      break;
    case 'p':
      info.port = atoi(optarg);
      break;
    case 'i':
      strncpy(interface_name, optarg, sizeof interface_name);
      interface_name[(sizeof interface_name) - 1] = '\0';
      iface = interface_name;
      break;
    case 'c':
      close_testing = 1;
      fprintf(stderr, " Close testing mode -- closes on "
             "client after 50 dumb increments"
             "and suppresses lws_mirror spam\n");
      break;
    case 'r':
      resource_path = optarg;
      printf("Setting resource path to \"%s\"\n", resource_path);
      break;
    case 'h':
      fprintf(stderr, "Usage: test-server "
          "[--port=<p>] [--ssl] "
          "[-d <log bitfield>] "
          "[--resource_path <path>]\n");
      exit(1);
    }
  }

#if !defined(LWS_NO_DAEMONIZE) && !defined(WIN32)
  /*
   * normally lock path would be /var/lock/lwsts or similar, to
   * simplify getting started without having to take care about
   * permissions or running as root, set to /tmp/.lwsts-lock
   */
  if (daemonize && lws_daemonize("/tmp/.lwsts-lock")) {
    fprintf(stderr, "Failed to daemonize\n");
    return 1;
  }
#endif

  for (n = 0; n < ARRAY_SIZE(sigs); n++) {
    signals[n] = evsignal_new(event_base_loop, sigs[n], signal_cb, event_self_cbarg());
    evsignal_add(signals[n], NULL);
  }

#ifndef _WIN32
  /* we will only try to log things according to our debug_level */
  setlogmask(LOG_UPTO (LOG_DEBUG));
  openlog("lwsts", syslog_options, LOG_DAEMON);
#endif

  /* tell the library what debug level to emit and to send it to syslog */
  lws_set_log_level(debug_level, lwsl_emit_syslog);

  lwsl_notice("libwebsockets test server libevent - license LGPL2.1+SLE\n");
  lwsl_notice("(C) Copyright 2010-2016 Andy Green <andy@warmcat.com>\n");

  printf("Using resource path \"%s\"\n", resource_path);

  info.iface = iface;
  info.protocols = protocols;
  info.extensions = exts;

  info.ssl_cert_filepath = NULL;
  info.ssl_private_key_filepath = NULL;

  if (use_ssl) {
    if (strlen(resource_path) > sizeof(cert_path) - 32) {
      lwsl_err("resource path too long\n");
      return -1;
    }
    sprintf(cert_path, "%s/libwebsockets-test-server.pem",
                resource_path);
    if (strlen(resource_path) > sizeof(key_path) - 32) {
      lwsl_err("resource path too long\n");
      return -1;
    }
    sprintf(key_path, "%s/libwebsockets-test-server.key.pem",
                resource_path);

    info.ssl_cert_filepath = cert_path;
    info.ssl_private_key_filepath = key_path;

    opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
  }
  info.gid = -1;
  info.uid = -1;
  info.max_http_header_pool = 1;
  info.options = opts | LWS_SERVER_OPTION_LIBEVENT;

  context = lws_create_context(&info);
  if (context == NULL) {
    lwsl_err("libwebsocket init failed\n");
    return -1;
  }

  /*
   * this shows how to override the lws file operations.  You don't need
   * to do any of this unless you have a reason (eg, want to serve
   * compressed files without decompressing the whole archive)
   */
  /* stash original platform fops */
  fops_plat = *(lws_get_fops(context));
  /* override the active fops */
  lws_get_fops(context)->open = test_server_fops_open;

  // Don't use the default Signal Event Watcher & Handler
  lws_event_sigint_cfg(context, 0, NULL);
  // Initialize the LWS with libevent loop
  lws_event_initloop(context, event_base_loop, 0);

  timeout_watcher = evtimer_new(event_base_loop, ev_timeout_cb, NULL);
  struct timeval tv = {0, 50000};
  evtimer_add(timeout_watcher, &tv);
  event_base_dispatch(event_base_loop);

  lws_context_destroy(context);
  lwsl_notice("libwebsockets-test-server exited cleanly\n");

#ifndef _WIN32
  closelog();
#endif

  return 0;
}