text: #include <sys/types.h>
  #include <sys/time.h>
  #include <sys/stat.h>
  #include <signal.h>
  #include <stdlib.h>
  #include <unistd.h>
  #include <inttypes.h>
  #include <pwd.h>
  #include <grp.h>
  #include <err.h>
  #include <errno.h>
  #include <string.h>
  #include <stdio.h>
  #include <fnmatch.h>
  #include "queue.h"
  #include "simplehttp.h"
  #include "stat.h"
  #include "request.h"
  #include "options.h"
  
  typedef struct cb_entry {
      char *path;
      void (*cb)(struct evhttp_request *, struct evbuffer *, void *);
      void *ctx;
      TAILQ_ENTRY(cb_entry) entries;
  } cb_entry;
  TAILQ_HEAD(, cb_entry) callbacks;
  
  int simplehttp_logging = 0;
  int callback_count = 0;
  uint64_t request_count = 0;
  struct evhttp *httpd;
  struct event pipe_ev;
  extern struct event_base *current_base;
  
  int help_cb(int *value);
  
  static void ignore_cb(int sig, short what, void *arg)
  {
  }
  
  void termination_handler(int signum)
  {
      event_loopbreak();
  }
  
  int get_uid(char *user)
  {
      int retcode;
      struct passwd *pw;
      
      pw = getpwnam(user);
      if (pw == NULL) {
          retcode = -1;
      } else {
          retcode = (unsigned) pw->pw_uid;
      }
      return retcode;
  }
  
  int get_gid(char *group)
  {
      int retcode;
      struct group *grent;
      
      grent = getgrnam(group);
      if (grent == NULL) {
          retcode = -1;
      } else {
          retcode = grent->gr_gid;
      }
      return retcode;
  }
  
  int get_user_gid(char *user)
  {
      int retcode;
      struct passwd *pw;
      
      pw = getpwnam(user);
      if (pw == NULL) {
          retcode = -1;
      } else {
          retcode = pw->pw_gid;
      }
      return retcode;
  }
  
  char **simplehttp_callback_names()
  {
      char **callback_names;
      char *tmp;
      struct cb_entry *entry;
      int i = 0, len;
      
      callback_names = malloc(callback_count * sizeof(*callback_names));
      TAILQ_FOREACH(entry, &callbacks, entries) {
          len = strlen(entry->path);
          callback_names[i] = malloc(len + 1);
          tmp = entry->path;
          if (tmp[0] == '/') {
              tmp++;
              len--;
          }
          if (tmp[len - 1] == '*') {
              len--;
          }
          memcpy(callback_names[i], tmp, len);
          callback_names[i][len] = '\0';
          i++;
      }
      
      return callback_names;
  }
  
  void generic_request_handler(struct evhttp_request *req, void *arg)
  {
      int found_cb = 0, i = 0;
      struct cb_entry *entry;
      struct simplehttp_request *s_req;
      struct evbuffer *evb = evbuffer_new();
      
      // fprintf(stderr, "request for %s from %s\n", req->uri, req->remote_host);
      
      request_count++;
      
      s_req = simplehttp_request_new(req, request_count);
      
      TAILQ_FOREACH(entry, &callbacks, entries) {
          if (fnmatch(entry->path, req->uri, FNM_NOESCAPE) == 0) {
              s_req->index = i;
              (*entry->cb)(req, evb, entry->ctx);
              found_cb = 1;
              break;
          }
          i++;
      }
      
      if (!found_cb) {
          evhttp_send_reply(req, HTTP_NOTFOUND, "", evb);
      }
      
      if (simplehttp_async_check(req) == NULL) {
          simplehttp_request_finish(req, s_req);
      }
      
      evbuffer_free(evb);
  }
  
  void simplehttp_init()
  {
      if (!current_base) {
          event_init();
      }
      TAILQ_INIT(&callbacks);
      TAILQ_INIT(&simplehttp_reqs);
  }
  
  void simplehttp_free()
  {
      struct cb_entry *entry;
      
      while ((entry = TAILQ_FIRST(&callbacks))) {
          TAILQ_REMOVE(&callbacks, entry, entries);
          free(entry->path);
          free(entry);
      }
      evhttp_free(httpd);
      simplehttp_stats_destruct();
  }
  
  void simplehttp_set_cb(const char *path, void (*cb)(struct evhttp_request *, struct evbuffer *, void *), void *ctx)
  {
      struct cb_entry *cbPtr;
      
      cbPtr = (cb_entry *)malloc(sizeof(*cbPtr));
      cbPtr->path = strdup(path);
      cbPtr->cb = cb;
      cbPtr->ctx = ctx;
      TAILQ_INSERT_TAIL(&callbacks, cbPtr, entries);
      
      callback_count++;
      
      printf("registering callback for path \"%s\"\n", path);
  }
  
  void define_simplehttp_options()
  {
      option_define_str("address", OPT_OPTIONAL, "0.0.0.0", NULL, NULL, "address to listen on");
      option_define_int("port", OPT_OPTIONAL, 8080, NULL, NULL, "port to listen on");
      option_define_bool("enable_logging", OPT_OPTIONAL, 0, NULL, NULL, "request logging");
      option_define_bool("daemon", OPT_OPTIONAL, 0, NULL, NULL, "daemonize process");
      option_define_str("root", OPT_OPTIONAL, NULL, NULL, NULL, "chdir and run from this directory");
      option_define_str("user", OPT_OPTIONAL, NULL, NULL, NULL, "run as this user");
      option_define_str("group", OPT_OPTIONAL, NULL, NULL, NULL, "run as this group");
  }
  
  int simplehttp_listen()
  {
      uid_t uid = 0;
      gid_t gid = 0;
      pid_t pid, sid;
      int errno;
      
      char *address = option_get_str("address");
      int port = option_get_int("port");
      
      int daemon = option_get_int("daemon");
      char *root = option_get_str("root");
      char *user = option_get_str("user");
      char *group = option_get_str("group");
      simplehttp_logging = option_get_int("enable_logging");
      
      if (daemon) {
          pid = fork();
          if (pid < 0) {
              return 0;
          } else if (pid > 0) {
              exit(EXIT_SUCCESS);
          }
          
          umask(0);
          sid = setsid();
          if (sid < 0) {
              return 0;
          }
      }
      
      if (user != NULL) {
          uid = get_uid(user);
          gid = get_user_gid(user);
          if (uid < 0) {
              uid = atoi(user);
              user = NULL;
          }
          if (uid == 0) {
              err(1, "invalid user");
          }
      }
      if (group != NULL) {
          gid = get_gid(group);
          if (gid < 0) {
              gid = atoi(group);
              if (gid == 0) {
                  err(1, "invalid group");
              }
          }
      }
      
      if (root != NULL) {
          if (chroot(root) != 0) {
              err(1, strerror(errno));
          }
          if (chdir("/") != 0) {
              err(1, strerror(errno));
          }
      }
      
      if (getuid() == 0) {
          if (user != NULL) {
              if (initgroups(user, (int) gid) != 0) {
                  err(1, "initgroups() failed");
              }
          } else {
              if (setgroups(0, NULL) != 0) {
                  err(1, "setgroups() failed");
              }
          }
          if (gid != getgid() && setgid(gid) != 0) {
              err(1, "setgid() failed");
          }
          if (setuid(uid) != 0) {
              err(1, "setuid() failed");
          }
      }
      
      signal(SIGINT, termination_handler);
      signal(SIGQUIT, termination_handler);
      signal(SIGTERM, termination_handler);
      
      signal_set(&pipe_ev, SIGPIPE, ignore_cb, NULL);
      signal_add(&pipe_ev, NULL);
      
      simplehttp_stats_init();
      
      httpd = evhttp_start(address, port);
      if (!httpd) {
          printf("could not bind to %s:%d\n", address, port);
          return 0;
      }
      
      printf("listening on %s:%d\n", address, port);
      
      evhttp_set_gencb(httpd, generic_request_handler, NULL);
      return 1;
  }
  
  void simplehttp_run()
  {
      event_dispatch();
  }
  
  int simplehttp_main()
  {
      if (!simplehttp_listen()) {
          return 1;
      }
      simplehttp_run();
      simplehttp_free();
      return 0;
  }
  