<commit_before>
/*
 * httpAdapter.c
 *
 * © Copyright IBM Corp. 2005, 2007
 *
 * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
 * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
 * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
 *
 * You can obtain a current copy of the Eclipse Public License from
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * Author:        <NAME> <<EMAIL>>
 *
 * Description:
 *
 * httpAdapter implementation.
 *
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <dlfcn.h>

#include <sys/wait.h>
#include <sys/socket.h>
#include <netdb.h>

#include "cmpidt.h"
#include "msgqueue.h"
#include "utilft.h"
#include "trace.h"
#include "cimXmlRequest.h"
#include "support.h"

#include <pthread.h>
#include <semaphore.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifdef HAVE_SYS_FSUID_H
#include <sys/fsuid.h>
#endif

#include "httpComm.h"
#include "sfcVersion.h"
#include "control.h"

#ifdef HAVE_UDS
#include <grp.h>
#endif

/* should go into cimRequest.h */
#define REQ_STDHTTP 0
#define REQ_REST 1

unsigned long   exFlags = 0;
static char    *name;
static int      debug;
static int      doBa;
#ifdef HAVE_UDS
static int      doUdsAuth;
#endif
static int      doFork = 0;
int             noChunking = 0;
int             sfcbSSLMode = 0;        /* used as a global sslMode */
int             httpLocalOnly = 0;      /* 1 = only listen on loopback
                                         * interface */
static int      hMax;
static int      httpProcIdX;
static int      stopAccepting = 0;
static int      running = 0;
static long     keepaliveTimeout = 15;
static long     keepaliveMaxRequest = 10;
static long     numRequest;
struct timeval  httpSelectTimeout = { 5, 0 };   /* 5 sec. timeout for
                                                 * select() before read() */

#if defined USE_SSL
static SSL_CTX *ctx;
static X509    *x509 = NULL;
#define CC_VERIFY_IGNORE  0
#define CC_VERIFY_ACCEPT  1
#define CC_VERIFY_REQUIRE 2
int             ccVerifyMode = CC_VERIFY_IGNORE;
static int      get_cert(int, X509_STORE_CTX *);
static int      ccValidate(X509 *, char **, int);
#endif

static key_t    httpProcSemKey;
static key_t    httpWorkSemKey;
static int      httpProcSem;
static int      httpWorkSem;

extern char    *decode64(char *data);
extern void     libraryName(const char *dir, const char *location,
                            char *fullName, int buf_size);
extern void    *loadLibib(const char *libname);
extern int      getControlChars(char *id, char **val);
extern void    *loadLibib(const char *libname);

extern RespSegments genFirstChunkResponses(BinRequestContext *,
                                           BinResponseHdr **, int, int);
extern RespSegments genLastChunkResponses(BinRequestContext *,
                                          BinResponseHdr **, int);
extern RespSegments genChunkResponses(BinRequestContext *,
                                      BinResponseHdr **, int);
extern RespSegments genFirstChunkErrorResponse(BinRequestContext * binCtx,
                                               int rc, char *msg);
extern char    *getErrTrailer(int id, int rc, char *m);
extern void     dumpTiming(int pid);
extern char    *configfile;
extern int      inet_aton(const char *cp, struct in_addr *inp);

static unsigned int sessionId;
extern char    *opsName[];

typedef int     (*Authenticate) (char *principal, char *pwd);

typedef struct _buffer {
  char           *data,
                 *content;
  int             length,
                  size,
                  ptr;
  unsigned int    content_length;
  int             trailers;
  char           *httpHdr,
                 *authorization,
                 *content_type,
                 *host,
                 *useragent;
  char           *principal;
  char           *protocol;
#if defined USE_SSL
  X509           *certificate;
#endif
} Buffer;

#ifdef HAVE_IPV6
#define USE_INET6
#endif

#define SET_HDR_CP(member, val)   member = val + strspn(val, " \t"); \

void
initHttpProcCtl(int p)
{
  httpProcSemKey = ftok(SFCB_BINARY, 'H');
  httpWorkSemKey = ftok(SFCB_BINARY, 'W');
  union semun     sun;
  int             i;

  mlogf(M_INFO, M_SHOW, "--- Max Http procs: %d\n", p);
  if ((httpProcSem = semget(httpProcSemKey, 1, 0600)) != -1)
    semctl(httpProcSem, 0, IPC_RMID, sun);

  if ((httpProcSem =
       semget(httpProcSemKey, 1 + p, IPC_CREAT | IPC_EXCL | 0600)) == -1) {
    char           *emsg = strerror(errno);
    mlogf(M_ERROR, M_SHOW,
          "\n--- Http Proc semaphore create key: 0x%x failed: %s\n",
          httpProcSemKey, emsg);
    mlogf(M_ERROR, M_SHOW,
          "     use \"ipcrm -S 0x%x\" to remove semaphore\n\n",
          httpProcSemKey);
    abort();
  }
  sun.val = p;
  semctl(httpProcSem, 0, SETVAL, sun);

  sun.val = 0;
  for (i = 1; i <= p; i++)
    semctl(httpProcSem, i, SETVAL, sun);

  if ((httpWorkSem = semget(httpWorkSemKey, 1, 0600)) != -1)
    semctl(httpWorkSem, 0, IPC_RMID, sun);

  if ((httpWorkSem =
       semget(httpWorkSemKey, 1, IPC_CREAT | IPC_EXCL | 0600)) == -1) {
    char           *emsg = strerror(errno);
    mlogf(M_ERROR, M_SHOW,
          "\n--- Http ProcWork semaphore create key: 0x%x failed: %s\n",
          httpWorkSemKey, emsg);
    mlogf(M_ERROR, M_SHOW,
          "     use \"ipcrm -S 0x%x\" to remove semaphore\n\n",
          httpProcSemKey);
    abort();
  }
  sun.val = 1;
  semctl(httpWorkSem, 0, SETVAL, sun);
}

int
remProcCtl()
{
  semctl(httpProcSem, 0, IPC_RMID, 0);
  semctl(httpWorkSem, 0, IPC_RMID, 0);
  return 0;
}

int
baValidate(char *cred, char **principal)
{
  char           *auth,
                 *pw = NULL;
  int             i,
                  err = 0;
  static void    *authLib = NULL;
  static Authenticate authenticate = NULL;
  char            dlName[512];
  int             ret = 0;

  if (strncasecmp(cred, "basic ", 6))
    return 0;
  auth = decode64(cred + 6);
  for (i = 0; i < strlen(auth); i++)
    if (auth[i] == ':') {
      auth[i] = 0;
      pw = &auth[i + 1];
      break;
    }

  if (err == 0 && authLib == NULL) {
    char           *ln;
    err = 1;
    if (getControlChars("basicAuthlib", &ln) == 0) {
      libraryName(NULL, ln, dlName, 512);
      if ((authLib = dlopen(dlName, RTLD_LAZY))) {
        authenticate = dlsym(authLib, "_sfcBasicAuthenticate");
        if (authenticate)
          err = 0;
      }
    }
    if (err)
      mlogf(M_ERROR, M_SHOW, "--- Authentication exit %s not found\n",
            dlName);
  }

  if (err) {
    ret = 1;
  } else {
    *principal = strdup(auth);
    if (authenticate(auth, pw))
      ret = 1;
  }

  free(auth);

  return ret;
}

static void
handleSigChld(int sig)
{
  const int       oerrno = errno;
  pid_t           pid;
  int             status;

  for (;;) {
    pid = wait4(0, &status, WNOHANG, NULL);
    if ((int) pid == 0)
      break;
    if ((int) pid < 0) {
      if (errno == EINTR || errno == EAGAIN) {
        // fprintf(stderr, "pid: %d continue \n", pid);
        continue;
      }
      if (errno != ECHILD)
        perror("child wait");
      break;
    } else {
      running--;
      // fprintf(stderr, "%s: SIGCHLD signal %d - %s(%d)\n", name, pid,
      // __FILE__, __LINE__);
    }
  }
  errno = oerrno;
}

static void
stopProc(void *p)
{
  // printf("--- %s draining %d\n",processName,running);
  for (;;) {
    if (running == 0) {
      mlogf(M_INFO, M_SHOW, "--- %s terminating %d\n", processName,
            getpid());
      exit(0);
    }
    sleep(1);
  }
}

static void
handleSigUsr1(int sig)
{
  pthread_t       t;
  pthread_attr_t  tattr;

  if (stopAccepting == 0) {
    stopAccepting = 1;
    pthread_attr_init(&tattr);
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
    pthread_create(&t, &tattr, (void *(*)(void *)) stopProc, NULL);
  }
}

static void
freeBuffer(Buffer * b)
{
  Buffer          emptyBuf =
      { NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL
  };
  if (b->data)
    free(b->data);
  if (b->content)
    free(b->content);
  if (b->principal)
    free(b->principal);
  *b = emptyBuf;
}

static char    *
getNextHdr(Buffer * b)
{
  int             i;
  char            c;

  for (i = b->ptr; b->ptr < b->length; ++b->ptr) {
    c = b->data[b->ptr];
    if (c == '\n' || c == '\r') {
      b->data[b->ptr] = 0;
      ++b->ptr;
      if (c == '\r' && b->ptr < b->length && b->data[b->ptr] == '\n') {
        b->data[b->ptr] = 0;
        ++b->ptr;
      }
      return &(b->data[i]);
    }
  }
  return NULL;
}

static void
add2buffer(Buffer * b, char *str, size_t len)
{
  if (b->size == 0) {
    b->size = len + 500;
    b->length = 0;
    b->data = (char *) malloc(b->size);
  } else if (b->length + len >= b->size) {
    b->size = b->length + len + 500;
    b->data = (char *) realloc((void *) b->data, b->size);
  }
  memmove(&((b->data)[b->length]), str, len);
  b->length += len;
  (b->data)[b->length] = 0;
}

static void
genError(CommHndl conn_fd, Buffer * b, int status, char *title, char *more)
{
  char            head[1000];
  char            server[] = "Server: sfcHttpd\r\n";
  char            clength[] = "Content-Length: 0\r\n";
  char            cclose[] = "Connection: close\r\n";
  char            end[] = "\r\n";

  _SFCB_ENTER(TRACE_HTTPDAEMON, "genError");

  snprintf(head, sizeof(head), "%s %d %s\r\n", b->protocol, status, title);
  // fprintf(stderr,"+++ genError: %s\n",head);
  _SFCB_TRACE(1, ("--- genError response: %s", head));
  commWrite(conn_fd, head, strlen(head));
  if (more) {
    commWrite(conn_fd, more, strlen(more));
  }

  commWrite(conn_fd, server, strlen(server));
  commWrite(conn_fd, clength, strlen(clength));
  if (keepaliveTimeout == 0 || numRequest >= keepaliveMaxRequest) {
    _SFCB_TRACE(1, ("--- closing after error\n"));
    commWrite(conn_fd, cclose, strlen(cclose));
  }
  commWrite(conn_fd, end, strlen(end));
  commFlush(conn_fd);
}

static int
readData(CommHndl conn_fd, char *into, int length)
{
  int             c = 0,
      r,
      isReady;
  fd_set          httpfds;
  FD_ZERO(&httpfds);
  FD_SET(conn_fd.socket, &httpfds);

  while (c < length) {
    isReady =
        select(conn_fd.socket + 1, &httpfds, NULL, NULL,
               &httpSelectTimeout);
    if (isReady == 0) {
      c = -1;
      break;
    }
    r = commRead(conn_fd, into + c, length - c);
    if (r < 0) {
      if (errno == EINTR || errno == EAGAIN) {
        continue;
      } else {
        mlogf(M_INFO, M_SHOW, "--- readData(): read() error %s\n",
              strerror(errno));
        c = -2;
        break;
      }
    }
    /*
     * r==0 is a success condition for read(), but the loop should
     * complete prior to this 
     */
    else if (r == 0) {
      mlogf(M_INFO, M_SHOW, "--- commRead hit EOF sooner than expected\n");
      c = -2;
      break;
    }
    c += r;
  }
  return c;
}

static int
getPayload(CommHndl conn_fd, Buffer * b)
{
  unsigned int    c = b->length - b->ptr;
  int             rc = 0;

  if (c > b->content_length) {
    mlogf(M_INFO, M_SHOW,
          "--- HTTP Content-Length is lying; rejecting, %d %d\n", c, b->content_length);
    return -1;
  }

  b->content = (char *) malloc(b->content_length + 8);
  if (c)
    memcpy(b->content, (b->data) + b->ptr, c);

  rc = readData(conn_fd, (b->content) + c, b->content_length - c);
  *((b->content) + b->content_length) = 0;
  return rc;
}

void
dumpResponse(RespSegments * rs)
{
  int             i;
  if (rs) {
    for (i = 0; i < 7; i++) {
      if (rs->segments[i].txt) {
        if (rs->segments[i].mode == 2) {
          UtilStringBuffer *sb = (UtilStringBuffer *) rs->segments[i].txt;
          printf("%s", sb->ft->getCharPtr(sb));
        } else
          printf("%s", rs->segments[i].txt);
      }
    }
    printf("<\n");
  }
}

static void
writeResponse(CommHndl conn_fd, RespSegments rs)
{

  static char     head[] = { "HTTP/1.1 200 OK\r\n" };
  static char     cont[] =
      { "Content-Type: application/xml; charset=\"utf-8\"\r\n" };
  static char     cach[] = { "Cache-Control: no-cache\r\n" };
  static char     op[] = { "CIMOperation: MethodResponse\r\n" };
  static char     cclose[] = "Connection: close\r\n";
  static char     end[] = { "\r\n" };
  char            str[256];
  int             len,
                  i,
                  ls[8];

  _SFCB_ENTER(TRACE_HTTPDAEMON, "writeResponse");

  for (len = 0, i = 0; i < 7; i++) {
    if (rs.segments[i].txt) {
      if (rs.segments[i].mode == 2) {
        UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
        if (sb == NULL)
          ls[i] = 0;
        else
          len += ls[i] = sb->ft->getSize(sb);
      } else
        len += ls[i] = strlen(rs.segments[i].txt);
    }
  }

  commWrite(conn_fd, head, strlen(head));
  commWrite(conn_fd, cont, strlen(cont));
  sprintf(str, "Content-Length: %d\r\n", len);
  commWrite(conn_fd, str, strlen(str));
  commWrite(conn_fd, cach, strlen(cach));
  commWrite(conn_fd, op, strlen(op));
  if (keepaliveTimeout == 0 || numRequest >= keepaliveMaxRequest) {
    commWrite(conn_fd, cclose, strlen(cclose));
  }
  commWrite(conn_fd, end, strlen(end));

  for (len = 0, i = 0; i < 7; i++) {
    if (rs.segments[i].txt) {
      if (rs.segments[i].mode == 2) {
        UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
        if (sb) {
          commWrite(conn_fd, (void *) sb->ft->getCharPtr(sb), ls[i]);
          sb->ft->release(sb);
        }
      } else {
        commWrite(conn_fd, rs.segments[i].txt, ls[i]);
        if (rs.segments[i].mode == 1)
          free(rs.segments[i].txt);
      }
    }
  }

  commFlush(conn_fd);

  _SFCB_EXIT();
}

static void
writeChunkHeaders(BinRequestContext * ctx)
{
  static char     head[] = { "HTTP/1.1 200 OK\r\n" };
  static char     cont[] =
      { "Content-Type: application/xml; charset=\"utf-8\"\r\n" };
  static char     cach[] = { "Cache-Control: no-cache\r\n" };
  static char     op[] = { "CIMOperation: MethodResponse\r\n" };
  static char     tenc[] = { "Transfer-encoding: chunked\r\n" };
  static char     trls[] =
      { "Trailer: CIMError, CIMStatusCode, CIMStatusCodeDescription\r\n" };
  static char     cclose[] = "Connection: close\r\n";

  _SFCB_ENTER(TRACE_HTTPDAEMON, "writeChunkHeaders");

  commWrite(*(ctx->commHndl), head, strlen(head));
  commWrite(*(ctx->commHndl), cont, strlen(cont));
  commWrite(*(ctx->commHndl), cach, strlen(cach));
  commWrite(*(ctx->commHndl), op, strlen(op));
  commWrite(*(ctx->commHndl), tenc, strlen(tenc));
  commWrite(*(ctx->commHndl), trls, strlen(trls));
  if (keepaliveTimeout == 0 || numRequest >= keepaliveMaxRequest) {
    commWrite(*(ctx->commHndl), cclose, strlen(cclose));
  }
  commFlush(*(ctx->commHndl));

  _SFCB_EXIT();
}

static void
writeChunkResponse(BinRequestContext * ctx, BinResponseHdr * rh)
{
  int             i,
                  len,
                  ls[8];
  char            str[256];
  RespSegments    rs;
  _SFCB_ENTER(TRACE_HTTPDAEMON, "writeChunkResponse");
  switch (ctx->chunkedMode) {
  case 1:
    _SFCB_TRACE(1, ("---  writeChunkResponse case 1"));
    if (rh->rc != 1) {
      RespSegments    rs;
      rs = genFirstChunkErrorResponse(ctx, rh->rc - 1, NULL);
      writeResponse(*ctx->commHndl, rs);
      _SFCB_EXIT();
    }
    writeChunkHeaders(ctx);
    /*
     * if (rh->rc!=1) { _SFCB_TRACE(1,("--- writeChunkResponse case 1
     * error")); rh->moreChunks=0; break; } 
     */
    rs = genFirstChunkResponses(ctx, &rh, rh->count, rh->moreChunks);
    ctx->chunkedMode = 2;
    break;
  case 2:
    _SFCB_TRACE(1, ("---  writeChunkResponse case 2"));
    if (rh->rc != 1) {
      _SFCB_TRACE(1, ("---  writeChunkResponse case 2 error"));
      rh->moreChunks = 0;
      break;
    }
    if (rh->moreChunks || ctx->pDone < ctx->pCount)
      rs = genChunkResponses(ctx, &rh, rh->count);
    else {
      rs = genLastChunkResponses(ctx, &rh, rh->count);
    }
    break;
  }

  if (rh->rc == 1) {

    for (len = 0, i = 0; i < 7; i++) {
      if (rs.segments[i].txt) {
        if (rs.segments[i].mode == 2) {
          UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
          if (sb == NULL)
            ls[i] = 0;
          else
            len += ls[i] = sb->ft->getSize(sb);
        } else
          len += ls[i] = strlen(rs.segments[i].txt);
      }
    }
    /*
     * make sure we do not have a 0 len , this would 
     * indicate the end of the chunk data. 
     */
    if (len != 0) {
      sprintf(str, "\r\n%x\r\n", len);
      commWrite(*(ctx->commHndl), str, strlen(str));
      _SFCB_TRACE(1, ("---  writeChunkResponse chunk amount %x ", len));
    }

    for (len = 0, i = 0; i < 7; i++) {
      if (rs.segments[i].txt) {
        if (rs.segments[i].mode == 2) {
          UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
          if (sb) {
            commWrite(*(ctx->commHndl), (void *) sb->ft->getCharPtr(sb),
                      ls[i]);
            sb->ft->release(sb);
          }
        } else {
          commWrite(*(ctx->commHndl), rs.segments[i].txt, ls[i]);
          if (rs.segments[i].mode == 1)
            free(rs.segments[i].txt);
        }

      }
    }
  }

  if (rh->moreChunks == 0 && ctx->pDone >= ctx->pCount) {
    char           *eStr = "\r\n0\r\n";
    char            status[512];
    char           *desc = NULL;

    _SFCB_TRACE(1, ("---  writing trailers"));

    sprintf(status, "CIMStatusCode: %d\r\n", (int) (rh->rc - 1));
    if (rh->rc != 1)
      desc = getErrTrailer(73, rh->rc - 1, NULL);

    commWrite(*(ctx->commHndl), eStr, strlen(eStr));
    commWrite(*(ctx->commHndl), status, strlen(status));
    if (desc) {
      commWrite(*(ctx->commHndl), desc, strlen(desc));
      free(desc);
    }
    eStr = "\r\n";
    commWrite(*(ctx->commHndl), eStr, strlen(eStr));
  }
  commFlush(*(ctx->commHndl));
  _SFCB_EXIT();
}

static ChunkFunctions httpChunkFunctions = {
  writeChunkResponse,
};

#define hdrBufsize 5000
#define hdrLimmit 5000

static int
getHdrs(CommHndl conn_fd, Buffer * b)
{
  int             first = 1,
      total = 0,
      isReady;
  fd_set          httpfds;
  int             state = 0;

  FD_ZERO(&httpfds);
  FD_SET(conn_fd.socket, &httpfds);

  for (;;) {
    isReady =
        select(conn_fd.socket + 1, &httpfds, NULL, NULL,
               &httpSelectTimeout);
    if (isReady == 0)
      return 3;

    char            buf[hdrBufsize];
    int             r = commRead(conn_fd, buf, sizeof(buf));

    if (r < 0) {
      if (errno == EINTR || errno == EAGAIN) {
        continue;
      } else {
        mlogf(M_INFO, M_SHOW, "--- getHdrs: read() error %s\n",
              strerror(errno));
        state = 3;
        break;
      }
    }
    if (r == 0) {
      if (b->size == 0)
        break;
      if (strstr(b->data, "\r\n\r\n") == NULL &&
          strstr(b->data, "\n\n") == NULL) {
        mlogf(M_ERROR, M_SHOW, "-#- HTTP header ended prematurely\n");
        state = 3;
        break;
      }
    }

    add2buffer(b, buf, r);
    total += r;

    /* DO THIS IN THE ReqHandler
     * on first run through, ensure that this is a POST req. if cimxml req
     */
//     if (first)
//       fprintf(stderr, "buf is '%s', cmd is '%s'\n", buf, cmd);
//     if (r && first) {
//       if (strncasecmp(buf, cmd, strlen(cmd)) != 0) {
//         /*
//          * not what we expected - still continue to read to not confuse
//          * the client 
//          */
//         state = 1;
//       }
//       first = 0;
//     }

    /*
     * success condition: end of header 
     */
    if (strstr(b->data, "\r\n\r\n") != NULL ||
        strstr(b->data, "\n\n") != NULL) {
      break;
    }

    if (total >= hdrLimmit) {
      mlogf(M_ERROR, M_SHOW, "-#- Possible DOS attempt detected\n");
      state = 2;
      break;
    }
  }
  return state;
}

int
pauseCodec(char *name)
{
  int             rc = 0;
  char           *n;

  if (noHttpPause)
    return 0;
  if (httpPauseStr == NULL) {
    if (httpPauseStr) {
      char           *p;
      p = httpPauseStr = strdup(httpPauseStr);
      while (*p) {
        *p = tolower(*p);
        p++;
      }
    }
  }
  if (httpPauseStr) {
    char           *p;
    int             l = strlen(name);
    p = n = strdup(name);
    while (*p) {
      *p = tolower(*p);
      p++;
    }
    if ((p = strstr(httpPauseStr, n)) != NULL) {
      if ((p == httpPauseStr || *(p - 1) == ',')
          && (p[l] == ',' || p[l] == 0))
        rc = 1;
    }
    free(n);
    return rc;
  }
  noHttpPause = 1;
  return 0;
}

/* CimRs resource types */
#define RES_NS                    1
#define RES_NS_Collection         2
#define RES_Class                 3
#define RES_ClassCollection       4
#define RES_ClassAssocCollection  5
#define RES_ClassRefCollection    6
#define RES_ClassMethCollection   7
#define RES_ClassMeth             8
#define RES_Inst                  9
#define RES_InstCollection       10
#define RES_InstAssocCollection  11
#define RES_InstRefCollection    12
#define RES_InstMeth             13
#define RES_InstMethCollection   14
#define RES_Quals                15
#define RES_QuallsCollection     16

int parseCimRsQueryParams(char* p) {

  return 0;

}


typedef struct _cimrsreq {
  int scope;
#define SCOPE_NAMESPACE        1
#define SCOPE_NS_COLL          2
#define SCOPE_CLASS            3
#define SCOPE_CL_COLL          4
#define SCOPE_CL_METH          5
#define SCOPE_CL_METH_COLL     6
#define SCOPE_CL_ASSOC_COLL    7
#define SCOPE_CL_REF_COLL      8
#define SCOPE_INSTANCE         9
#define SCOPE_INST_COLL       10
#define SCOPE_INST_METH       11
#define SCOPE_INST_METH_COLL  12
#define SCOPE_INST_ASSOC_COLL 13
#define SCOPE_INST_REF_COLL   14
  char* path; /* strdup'd */
  char* ns;
  char* cn;
  char* meth; /* for class meth or inst meth */
  char* keyList;
  /* query stuff */
} CimRsReq;

/* decode */
static char* percentDecode(char* s) {
  return s;
}

/* for parsing constant ending fragments, such as: 
   /cimrs/namespaces
   /cimrs/namespaces/{ns}/classes?istopclass
   /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}/methods
 */
static int checkEndingFragment(char* fragment, char* name, int nameLen) {
  /* /cimrs/namespaces/{ns}/classes/{cn}/associators */
  if (fragment[nameLen] == 0) {
    fprintf(stderr, " request for %s\n", name);
    return 0;
  }
  /* something like /cimrs/namespaces/{ns}/classes/{cn}/associatorswithcrapattheend */
  else
    return -1;
}

static int parseMethodFragment(CimRsReq* req, char* fragment, const int coll, const int name) {

  char* meth;

  meth = strpbrk(fragment, "/");
  if (meth == NULL) {
    req->scope = coll;
    return checkEndingFragment(fragment, "methods", 7);
  }
  else {
    req->scope = name;
    req->meth = ++meth;
    return 0;
  }
}

static int parseInstanceFragment(CimRsReq* req, char* fragment) {

  char* tmp;
  char* keyList = strpbrk(fragment, "/");
  
  if (keyList == NULL) {
    req->scope = SCOPE_INST_COLL;
    return checkEndingFragment(fragment, "instances", 9);
  }
  else {
    keyList++; /* TODO: key list syntax validation? */
    tmp = strpbrk(keyList, "/");
    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist} */
    if (tmp == NULL) {
      req->scope = SCOPE_INSTANCE;
      req->keyList = percentDecode(keyList);
      return 0;
    }

    *tmp = 0;
    tmp++;    
    req->keyList = percentDecode(keyList);

    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}/associators */
    if (strncasecmp(tmp, "associators", 11) == 0) {
      req->scope = SCOPE_INST_ASSOC_COLL;
      return checkEndingFragment(tmp, "associators", 11);
    }
    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}/references */
    else if (strncasecmp(tmp, "references", 10) == 0) {
      req->scope = SCOPE_INST_REF_COLL;
      return checkEndingFragment(tmp, "references", 10);
    }
    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}methods */
    else if (strncasecmp(tmp, "methods", 7) == 0) {
      return parseMethodFragment(req, tmp, SCOPE_INST_METH_COLL, SCOPE_INST_METH);
    }
  }

  return -1;
}

int parseCimRsPath(char* p, CimRsReq* req) {

  char* path;
  char* tmp;
  char* ns;
  char* cn;
  char* query;

  req->path = strdup(p);
  path = req->path;
  tmp = strpbrk(++path, "/");

  /* "/cimrs/namespaces" should be the start of every req path */
  if (strncasecmp(++tmp, "namespaces", 10) != 0) {
    return -1;
  }

  /* get the query string if there is one */
  query = strpbrk(path, "?"); /* do we only need to check this for GET? */
  if (query != NULL) {
    *query = 0;
    parseCimRsQueryParams(++query);
    fprintf(stderr, " has query\n");
  }

  ns = strpbrk(tmp, "/");

  /* we got "/cimrs/namespaces" or "/cimrs/namespaceswithcrapafterit */
  if (ns == NULL) {
    req->scope = SCOPE_NS_COLL;
    return checkEndingFragment(tmp, "namespaces", 10); 
  }

  tmp = strpbrk(++ns, "/");

  if (tmp == NULL) {
    /* we got "/cimrs/namespaces/{ns} */
    fprintf(stderr, " request for namespace '%s'\n", ns);
    req->scope = SCOPE_NAMESPACE;
    return 0;
  }

  *tmp = 0; /* null-out the '/' so that we get just the namespace in ns */

  req->ns = percentDecode(ns);

  /* should be /cimrs/namespaces/{ns}/classes */
  /* TODO: could be "qualifiers" as well */
  if (strncasecmp(++tmp, "classes", 7) != 0) {
    return -1;
  }

  cn = strpbrk(tmp, "/");

  if (cn == NULL) {
    req->scope = SCOPE_CL_COLL;
    return checkEndingFragment(tmp, "classes", 7);
  }
  
  tmp = strpbrk(++cn, "/");

  if (tmp == NULL) {
    /* /cimrs/namespaces/{ns}/classes/{cn} */
    req->scope = SCOPE_CLASS;
    return 0;
  }

  tmp = strpbrk(cn, "/");
  *tmp = 0;

  req->cn = cn;

  tmp++;

  /* /cimrs/namespaces/{ns}/classes/{cn}/instances */
  if (strncasecmp(tmp, "instances", 9) == 0) {
    return parseInstanceFragment(req, tmp);
  }

  /* /cimrs/namespaces/{ns}/classes/{cn}/associators */
  else if (strncasecmp(tmp, "associators", 11) == 0) {
    req->scope = SCOPE_CL_ASSOC_COLL;
    return checkEndingFragment(tmp, "associators", 11);
  }
  /* /cimrs/namespaces/{ns}/classes/{cn}/references */
  else if (strncasecmp(tmp, "references", 10) == 0) {
    req->scope = SCOPE_CL_REF_COLL;
    return checkEndingFragment(tmp, "references", 10);
  }
  /* /cimrs/namespaces/{ns}/classes/{cn}/methods */
  else if (strncasecmp(tmp, "methods", 7) == 0) {
    return parseMethodFragment(req, tmp, SCOPE_CL_METH_COLL, SCOPE_CL_METH);
  }

  return -1;
}


static int
doHttpRequest(CommHndl conn_fd)
{
  char           *cp;
  Buffer          inBuf = { NULL, NULL, 0, 0, 0, 0, 0, 0 };
  RespSegments    response;
  static RespSegments nullResponse = { NULL, 0, 0, NULL, {{0, NULL}} };
  unsigned long   len;
  int             hl,
                  rc,
                  uset = 0;
  char           *hdr,
                 *path;
  int             discardInput = 0;
  MsgSegment      msgs[2];
  struct rusage   us,
                  ue;
  struct timeval  sv,
                  ev;
  CimXmlRequestContext ctx;
  int             breakloop;
  _SFCB_ENTER(TRACE_HTTPDAEMON, "doHttpRequest");

  if (pauseCodec("http"))
    for (breakloop = 0; breakloop == 0;) {
      fprintf(stdout, "-#- Pausing for codec http %d\n", currentProc);
      sleep(5);
    }

  inBuf.authorization = "";
  inBuf.protocol = "HTTP/1.1";
  inBuf.content_type = NULL;
  inBuf.content_length = UINT_MAX;
  inBuf.host = NULL;
  inBuf.useragent = "";
  int             badReq = 0;

  /* get everything */
  rc = getHdrs(conn_fd, &inBuf);

  if (rc == 1) { /* 1 indicates bad HTTP verb.  may no longer be needed */
    genError(conn_fd, &inBuf, 501, "Not Implemented", NULL);
    /*
     * we continue to parse headers and empty the socket to be graceful
     * with the client 
     */
    discardInput = 1;
  } else if (rc == 2) {
    genError(conn_fd, &inBuf, 400, "Bad Request", NULL);
    discardInput = 2;
    _SFCB_TRACE(1, ("--- potential DOS attempt discovered."));
  } else if (rc == 3) {
    genError(conn_fd, &inBuf, 400, "Bad Request", NULL);
    _SFCB_TRACE(1, ("--- exiting after request timeout."));
    commClose(conn_fd);
    exit(1);
  }

  if (inBuf.size == 0) {
    /*
     * no buffer data - end of file - quit 
     */
    _SFCB_TRACE(1, ("--- HTTP connection EOF, quit %d ", currentProc));
    commClose(conn_fd);
    exit(1);
  }

  /* get the protocol and path */
  inBuf.httpHdr = getNextHdr(&inBuf);
  fprintf(stderr, "httpHdr = %s\n", inBuf.httpHdr);
  char* tmp;
  for (badReq = 1;;) {
    if (inBuf.httpHdr == NULL)
      break;
    tmp = inBuf.httpHdr;

    path = strpbrk(tmp, " \t\r\n");
    if (path == NULL)
      break;
    *path++ = 0; /* get rid of the leading space */
    _SFCB_TRACE(1, ("--- Header: %s", inBuf.httpHdr));
    path += strspn(path, " \t\r\n");
    inBuf.protocol = strpbrk(path, " \t\r\n");

    if (inBuf.protocol == NULL)
      break;
    *inBuf.protocol++ = 0;

    //    fprintf(stderr, "path is '%s'\n", path);
    ctx.path = path;
    ctx.httpOp = tmp;

//     CimRsReq req = {0, NULL, NULL, NULL, NULL};
//     int prc = parseCimRsPath(path, &req);
//     fprintf(stderr, "parseCimPath returned %d\n", prc);
//     if (prc == 0) {
//       fprintf(stderr, "scope: %d\n", req.scope);
//       if (req.ns) fprintf(stderr, "%s", req.ns);
//       if (req.cn) fprintf(stderr, ":%s", req.cn);
//       if (req.keyList) fprintf(stderr, ".%s", req.keyList);
//       if (req.meth) fprintf(stderr, " %s", req.meth);
//       fprintf(stderr, "\n");
//     }

    badReq = 0;
  }

  if (badReq) {
    genError(conn_fd, &inBuf, 400, "Bad Request", NULL);
    _SFCB_TRACE(1, ("--- exiting after malformed header."));
    commClose(conn_fd);
    exit(1);
  }

  /* parse rest of headers */
  while ((hdr = getNextHdr(&inBuf)) != NULL) {
    _SFCB_TRACE(1, ("--- Header: %s", hdr));
    if (hdr[0] == 0)<commit_msg>testing parsing REST requests<commit_after>
/*
 * httpAdapter.c
 *
 * © Copyright IBM Corp. 2005, 2007
 *
 * THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
 * ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
 * CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
 *
 * You can obtain a current copy of the Eclipse Public License from
 * http://www.opensource.org/licenses/eclipse-1.0.php
 *
 * Author:        <NAME> <<EMAIL>>
 *
 * Description:
 *
 * httpAdapter implementation.
 *
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <dlfcn.h>

#include <sys/wait.h>
#include <sys/socket.h>
#include <netdb.h>

#include "cmpidt.h"
#include "msgqueue.h"
#include "utilft.h"
#include "trace.h"
#include "cimXmlRequest.h"
#include "support.h"

#include <pthread.h>
#include <semaphore.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#ifdef HAVE_SYS_FSUID_H
#include <sys/fsuid.h>
#endif

#include "httpComm.h"
#include "sfcVersion.h"
#include "control.h"

#ifdef HAVE_UDS
#include <grp.h>
#endif

/* should go into cimRequest.h */
#define REQ_STDHTTP 0
#define REQ_REST 1

unsigned long   exFlags = 0;
static char    *name;
static int      debug;
static int      doBa;
#ifdef HAVE_UDS
static int      doUdsAuth;
#endif
static int      doFork = 0;
int             noChunking = 0;
int             sfcbSSLMode = 0;        /* used as a global sslMode */
int             httpLocalOnly = 0;      /* 1 = only listen on loopback
                                         * interface */
static int      hMax;
static int      httpProcIdX;
static int      stopAccepting = 0;
static int      running = 0;
static long     keepaliveTimeout = 15;
static long     keepaliveMaxRequest = 10;
static long     numRequest;
struct timeval  httpSelectTimeout = { 5, 0 };   /* 5 sec. timeout for
                                                 * select() before read() */

#if defined USE_SSL
static SSL_CTX *ctx;
static X509    *x509 = NULL;
#define CC_VERIFY_IGNORE  0
#define CC_VERIFY_ACCEPT  1
#define CC_VERIFY_REQUIRE 2
int             ccVerifyMode = CC_VERIFY_IGNORE;
static int      get_cert(int, X509_STORE_CTX *);
static int      ccValidate(X509 *, char **, int);
#endif

static key_t    httpProcSemKey;
static key_t    httpWorkSemKey;
static int      httpProcSem;
static int      httpWorkSem;

extern char    *decode64(char *data);
extern void     libraryName(const char *dir, const char *location,
                            char *fullName, int buf_size);
extern void    *loadLibib(const char *libname);
extern int      getControlChars(char *id, char **val);
extern void    *loadLibib(const char *libname);

extern RespSegments genFirstChunkResponses(BinRequestContext *,
                                           BinResponseHdr **, int, int);
extern RespSegments genLastChunkResponses(BinRequestContext *,
                                          BinResponseHdr **, int);
extern RespSegments genChunkResponses(BinRequestContext *,
                                      BinResponseHdr **, int);
extern RespSegments genFirstChunkErrorResponse(BinRequestContext * binCtx,
                                               int rc, char *msg);
extern char    *getErrTrailer(int id, int rc, char *m);
extern void     dumpTiming(int pid);
extern char    *configfile;
extern int      inet_aton(const char *cp, struct in_addr *inp);

static unsigned int sessionId;
extern char    *opsName[];

typedef int     (*Authenticate) (char *principal, char *pwd);

typedef struct _buffer {
  char           *data,
                 *content;
  int             length,
                  size,
                  ptr;
  unsigned int    content_length;
  int             trailers;
  char           *httpHdr,
                 *authorization,
                 *content_type,
                 *host,
                 *useragent;
  char           *principal;
  char           *protocol;
#if defined USE_SSL
  X509           *certificate;
#endif
} Buffer;

#ifdef HAVE_IPV6
#define USE_INET6
#endif

#define SET_HDR_CP(member, val)   member = val + strspn(val, " \t"); \

void
initHttpProcCtl(int p)
{
  httpProcSemKey = ftok(SFCB_BINARY, 'H');
  httpWorkSemKey = ftok(SFCB_BINARY, 'W');
  union semun     sun;
  int             i;

  mlogf(M_INFO, M_SHOW, "--- Max Http procs: %d\n", p);
  if ((httpProcSem = semget(httpProcSemKey, 1, 0600)) != -1)
    semctl(httpProcSem, 0, IPC_RMID, sun);

  if ((httpProcSem =
       semget(httpProcSemKey, 1 + p, IPC_CREAT | IPC_EXCL | 0600)) == -1) {
    char           *emsg = strerror(errno);
    mlogf(M_ERROR, M_SHOW,
          "\n--- Http Proc semaphore create key: 0x%x failed: %s\n",
          httpProcSemKey, emsg);
    mlogf(M_ERROR, M_SHOW,
          "     use \"ipcrm -S 0x%x\" to remove semaphore\n\n",
          httpProcSemKey);
    abort();
  }
  sun.val = p;
  semctl(httpProcSem, 0, SETVAL, sun);

  sun.val = 0;
  for (i = 1; i <= p; i++)
    semctl(httpProcSem, i, SETVAL, sun);

  if ((httpWorkSem = semget(httpWorkSemKey, 1, 0600)) != -1)
    semctl(httpWorkSem, 0, IPC_RMID, sun);

  if ((httpWorkSem =
       semget(httpWorkSemKey, 1, IPC_CREAT | IPC_EXCL | 0600)) == -1) {
    char           *emsg = strerror(errno);
    mlogf(M_ERROR, M_SHOW,
          "\n--- Http ProcWork semaphore create key: 0x%x failed: %s\n",
          httpWorkSemKey, emsg);
    mlogf(M_ERROR, M_SHOW,
          "     use \"ipcrm -S 0x%x\" to remove semaphore\n\n",
          httpProcSemKey);
    abort();
  }
  sun.val = 1;
  semctl(httpWorkSem, 0, SETVAL, sun);
}

int
remProcCtl()
{
  semctl(httpProcSem, 0, IPC_RMID, 0);
  semctl(httpWorkSem, 0, IPC_RMID, 0);
  return 0;
}

int
baValidate(char *cred, char **principal)
{
  char           *auth,
                 *pw = NULL;
  int             i,
                  err = 0;
  static void    *authLib = NULL;
  static Authenticate authenticate = NULL;
  char            dlName[512];
  int             ret = 0;

  if (strncasecmp(cred, "basic ", 6))
    return 0;
  auth = decode64(cred + 6);
  for (i = 0; i < strlen(auth); i++)
    if (auth[i] == ':') {
      auth[i] = 0;
      pw = &auth[i + 1];
      break;
    }

  if (err == 0 && authLib == NULL) {
    char           *ln;
    err = 1;
    if (getControlChars("basicAuthlib", &ln) == 0) {
      libraryName(NULL, ln, dlName, 512);
      if ((authLib = dlopen(dlName, RTLD_LAZY))) {
        authenticate = dlsym(authLib, "_sfcBasicAuthenticate");
        if (authenticate)
          err = 0;
      }
    }
    if (err)
      mlogf(M_ERROR, M_SHOW, "--- Authentication exit %s not found\n",
            dlName);
  }

  if (err) {
    ret = 1;
  } else {
    *principal = strdup(auth);
    if (authenticate(auth, pw))
      ret = 1;
  }

  free(auth);

  return ret;
}

static void
handleSigChld(int sig)
{
  const int       oerrno = errno;
  pid_t           pid;
  int             status;

  for (;;) {
    pid = wait4(0, &status, WNOHANG, NULL);
    if ((int) pid == 0)
      break;
    if ((int) pid < 0) {
      if (errno == EINTR || errno == EAGAIN) {
        // fprintf(stderr, "pid: %d continue \n", pid);
        continue;
      }
      if (errno != ECHILD)
        perror("child wait");
      break;
    } else {
      running--;
      // fprintf(stderr, "%s: SIGCHLD signal %d - %s(%d)\n", name, pid,
      // __FILE__, __LINE__);
    }
  }
  errno = oerrno;
}

static void
stopProc(void *p)
{
  // printf("--- %s draining %d\n",processName,running);
  for (;;) {
    if (running == 0) {
      mlogf(M_INFO, M_SHOW, "--- %s terminating %d\n", processName,
            getpid());
      exit(0);
    }
    sleep(1);
  }
}

static void
handleSigUsr1(int sig)
{
  pthread_t       t;
  pthread_attr_t  tattr;

  if (stopAccepting == 0) {
    stopAccepting = 1;
    pthread_attr_init(&tattr);
    pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
    pthread_create(&t, &tattr, (void *(*)(void *)) stopProc, NULL);
  }
}

static void
freeBuffer(Buffer * b)
{
  Buffer          emptyBuf =
      { NULL, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL
  };
  if (b->data)
    free(b->data);
  if (b->content)
    free(b->content);
  if (b->principal)
    free(b->principal);
  *b = emptyBuf;
}

static char    *
getNextHdr(Buffer * b)
{
  int             i;
  char            c;

  for (i = b->ptr; b->ptr < b->length; ++b->ptr) {
    c = b->data[b->ptr];
    if (c == '\n' || c == '\r') {
      b->data[b->ptr] = 0;
      ++b->ptr;
      if (c == '\r' && b->ptr < b->length && b->data[b->ptr] == '\n') {
        b->data[b->ptr] = 0;
        ++b->ptr;
      }
      return &(b->data[i]);
    }
  }
  return NULL;
}

static void
add2buffer(Buffer * b, char *str, size_t len)
{
  if (b->size == 0) {
    b->size = len + 500;
    b->length = 0;
    b->data = (char *) malloc(b->size);
  } else if (b->length + len >= b->size) {
    b->size = b->length + len + 500;
    b->data = (char *) realloc((void *) b->data, b->size);
  }
  memmove(&((b->data)[b->length]), str, len);
  b->length += len;
  (b->data)[b->length] = 0;
}

static void
genError(CommHndl conn_fd, Buffer * b, int status, char *title, char *more)
{
  char            head[1000];
  char            server[] = "Server: sfcHttpd\r\n";
  char            clength[] = "Content-Length: 0\r\n";
  char            cclose[] = "Connection: close\r\n";
  char            end[] = "\r\n";

  _SFCB_ENTER(TRACE_HTTPDAEMON, "genError");

  snprintf(head, sizeof(head), "%s %d %s\r\n", b->protocol, status, title);
  // fprintf(stderr,"+++ genError: %s\n",head);
  _SFCB_TRACE(1, ("--- genError response: %s", head));
  commWrite(conn_fd, head, strlen(head));
  if (more) {
    commWrite(conn_fd, more, strlen(more));
  }

  commWrite(conn_fd, server, strlen(server));
  commWrite(conn_fd, clength, strlen(clength));
  if (keepaliveTimeout == 0 || numRequest >= keepaliveMaxRequest) {
    _SFCB_TRACE(1, ("--- closing after error\n"));
    commWrite(conn_fd, cclose, strlen(cclose));
  }
  commWrite(conn_fd, end, strlen(end));
  commFlush(conn_fd);
}

static int
readData(CommHndl conn_fd, char *into, int length)
{
  int             c = 0,
      r,
      isReady;
  fd_set          httpfds;
  FD_ZERO(&httpfds);
  FD_SET(conn_fd.socket, &httpfds);

  while (c < length) {
    isReady =
        select(conn_fd.socket + 1, &httpfds, NULL, NULL,
               &httpSelectTimeout);
    if (isReady == 0) {
      c = -1;
      break;
    }
    r = commRead(conn_fd, into + c, length - c);
    if (r < 0) {
      if (errno == EINTR || errno == EAGAIN) {
        continue;
      } else {
        mlogf(M_INFO, M_SHOW, "--- readData(): read() error %s\n",
              strerror(errno));
        c = -2;
        break;
      }
    }
    /*
     * r==0 is a success condition for read(), but the loop should
     * complete prior to this 
     */
    else if (r == 0) {
      mlogf(M_INFO, M_SHOW, "--- commRead hit EOF sooner than expected\n");
      c = -2;
      break;
    }
    c += r;
  }
  return c;
}

static int
getPayload(CommHndl conn_fd, Buffer * b)
{
  unsigned int    c = b->length - b->ptr;
  int             rc = 0;

  if (c > b->content_length) {
    mlogf(M_INFO, M_SHOW,
          "--- HTTP Content-Length is lying; rejecting, %d %d\n", c, b->content_length);
    return -1;
  }

  b->content = (char *) malloc(b->content_length + 8);
  if (c)
    memcpy(b->content, (b->data) + b->ptr, c);

  rc = readData(conn_fd, (b->content) + c, b->content_length - c);
  *((b->content) + b->content_length) = 0;
  return rc;
}

void
dumpResponse(RespSegments * rs)
{
  int             i;
  if (rs) {
    for (i = 0; i < 7; i++) {
      if (rs->segments[i].txt) {
        if (rs->segments[i].mode == 2) {
          UtilStringBuffer *sb = (UtilStringBuffer *) rs->segments[i].txt;
          printf("%s", sb->ft->getCharPtr(sb));
        } else
          printf("%s", rs->segments[i].txt);
      }
    }
    printf("<\n");
  }
}

static void
writeResponse(CommHndl conn_fd, RespSegments rs)
{

  static char     head[] = { "HTTP/1.1 200 OK\r\n" };
  static char     cont[] =
      { "Content-Type: application/xml; charset=\"utf-8\"\r\n" };
  static char     cach[] = { "Cache-Control: no-cache\r\n" };
  static char     op[] = { "CIMOperation: MethodResponse\r\n" };
  static char     cclose[] = "Connection: close\r\n";
  static char     end[] = { "\r\n" };
  char            str[256];
  int             len,
                  i,
                  ls[8];

  _SFCB_ENTER(TRACE_HTTPDAEMON, "writeResponse");

  for (len = 0, i = 0; i < 7; i++) {
    if (rs.segments[i].txt) {
      if (rs.segments[i].mode == 2) {
        UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
        if (sb == NULL)
          ls[i] = 0;
        else
          len += ls[i] = sb->ft->getSize(sb);
      } else
        len += ls[i] = strlen(rs.segments[i].txt);
    }
  }

  commWrite(conn_fd, head, strlen(head));
  commWrite(conn_fd, cont, strlen(cont));
  sprintf(str, "Content-Length: %d\r\n", len);
  commWrite(conn_fd, str, strlen(str));
  commWrite(conn_fd, cach, strlen(cach));
  commWrite(conn_fd, op, strlen(op));
  if (keepaliveTimeout == 0 || numRequest >= keepaliveMaxRequest) {
    commWrite(conn_fd, cclose, strlen(cclose));
  }
  commWrite(conn_fd, end, strlen(end));

  for (len = 0, i = 0; i < 7; i++) {
    if (rs.segments[i].txt) {
      if (rs.segments[i].mode == 2) {
        UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
        if (sb) {
          commWrite(conn_fd, (void *) sb->ft->getCharPtr(sb), ls[i]);
          sb->ft->release(sb);
        }
      } else {
        commWrite(conn_fd, rs.segments[i].txt, ls[i]);
        if (rs.segments[i].mode == 1)
          free(rs.segments[i].txt);
      }
    }
  }

  commFlush(conn_fd);

  _SFCB_EXIT();
}

static void
writeChunkHeaders(BinRequestContext * ctx)
{
  static char     head[] = { "HTTP/1.1 200 OK\r\n" };
  static char     cont[] =
      { "Content-Type: application/xml; charset=\"utf-8\"\r\n" };
  static char     cach[] = { "Cache-Control: no-cache\r\n" };
  static char     op[] = { "CIMOperation: MethodResponse\r\n" };
  static char     tenc[] = { "Transfer-encoding: chunked\r\n" };
  static char     trls[] =
      { "Trailer: CIMError, CIMStatusCode, CIMStatusCodeDescription\r\n" };
  static char     cclose[] = "Connection: close\r\n";

  _SFCB_ENTER(TRACE_HTTPDAEMON, "writeChunkHeaders");

  commWrite(*(ctx->commHndl), head, strlen(head));
  commWrite(*(ctx->commHndl), cont, strlen(cont));
  commWrite(*(ctx->commHndl), cach, strlen(cach));
  commWrite(*(ctx->commHndl), op, strlen(op));
  commWrite(*(ctx->commHndl), tenc, strlen(tenc));
  commWrite(*(ctx->commHndl), trls, strlen(trls));
  if (keepaliveTimeout == 0 || numRequest >= keepaliveMaxRequest) {
    commWrite(*(ctx->commHndl), cclose, strlen(cclose));
  }
  commFlush(*(ctx->commHndl));

  _SFCB_EXIT();
}

static void
writeChunkResponse(BinRequestContext * ctx, BinResponseHdr * rh)
{
  int             i,
                  len,
                  ls[8];
  char            str[256];
  RespSegments    rs;
  _SFCB_ENTER(TRACE_HTTPDAEMON, "writeChunkResponse");
  switch (ctx->chunkedMode) {
  case 1:
    _SFCB_TRACE(1, ("---  writeChunkResponse case 1"));
    if (rh->rc != 1) {
      RespSegments    rs;
      rs = genFirstChunkErrorResponse(ctx, rh->rc - 1, NULL);
      writeResponse(*ctx->commHndl, rs);
      _SFCB_EXIT();
    }
    writeChunkHeaders(ctx);
    /*
     * if (rh->rc!=1) { _SFCB_TRACE(1,("--- writeChunkResponse case 1
     * error")); rh->moreChunks=0; break; } 
     */
    rs = genFirstChunkResponses(ctx, &rh, rh->count, rh->moreChunks);
    ctx->chunkedMode = 2;
    break;
  case 2:
    _SFCB_TRACE(1, ("---  writeChunkResponse case 2"));
    if (rh->rc != 1) {
      _SFCB_TRACE(1, ("---  writeChunkResponse case 2 error"));
      rh->moreChunks = 0;
      break;
    }
    if (rh->moreChunks || ctx->pDone < ctx->pCount)
      rs = genChunkResponses(ctx, &rh, rh->count);
    else {
      rs = genLastChunkResponses(ctx, &rh, rh->count);
    }
    break;
  }

  if (rh->rc == 1) {

    for (len = 0, i = 0; i < 7; i++) {
      if (rs.segments[i].txt) {
        if (rs.segments[i].mode == 2) {
          UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
          if (sb == NULL)
            ls[i] = 0;
          else
            len += ls[i] = sb->ft->getSize(sb);
        } else
          len += ls[i] = strlen(rs.segments[i].txt);
      }
    }
    /*
     * make sure we do not have a 0 len , this would 
     * indicate the end of the chunk data. 
     */
    if (len != 0) {
      sprintf(str, "\r\n%x\r\n", len);
      commWrite(*(ctx->commHndl), str, strlen(str));
      _SFCB_TRACE(1, ("---  writeChunkResponse chunk amount %x ", len));
    }

    for (len = 0, i = 0; i < 7; i++) {
      if (rs.segments[i].txt) {
        if (rs.segments[i].mode == 2) {
          UtilStringBuffer *sb = (UtilStringBuffer *) rs.segments[i].txt;
          if (sb) {
            commWrite(*(ctx->commHndl), (void *) sb->ft->getCharPtr(sb),
                      ls[i]);
            sb->ft->release(sb);
          }
        } else {
          commWrite(*(ctx->commHndl), rs.segments[i].txt, ls[i]);
          if (rs.segments[i].mode == 1)
            free(rs.segments[i].txt);
        }

      }
    }
  }

  if (rh->moreChunks == 0 && ctx->pDone >= ctx->pCount) {
    char           *eStr = "\r\n0\r\n";
    char            status[512];
    char           *desc = NULL;

    _SFCB_TRACE(1, ("---  writing trailers"));

    sprintf(status, "CIMStatusCode: %d\r\n", (int) (rh->rc - 1));
    if (rh->rc != 1)
      desc = getErrTrailer(73, rh->rc - 1, NULL);

    commWrite(*(ctx->commHndl), eStr, strlen(eStr));
    commWrite(*(ctx->commHndl), status, strlen(status));
    if (desc) {
      commWrite(*(ctx->commHndl), desc, strlen(desc));
      free(desc);
    }
    eStr = "\r\n";
    commWrite(*(ctx->commHndl), eStr, strlen(eStr));
  }
  commFlush(*(ctx->commHndl));
  _SFCB_EXIT();
}

static ChunkFunctions httpChunkFunctions = {
  writeChunkResponse,
};

#define hdrBufsize 5000
#define hdrLimmit 5000

static int
getHdrs(CommHndl conn_fd, Buffer * b)
{
  int             first = 1,
      total = 0,
      isReady;
  fd_set          httpfds;
  int             state = 0;

  FD_ZERO(&httpfds);
  FD_SET(conn_fd.socket, &httpfds);

  for (;;) {
    isReady =
        select(conn_fd.socket + 1, &httpfds, NULL, NULL,
               &httpSelectTimeout);
    if (isReady == 0)
      return 3;

    char            buf[hdrBufsize];
    int             r = commRead(conn_fd, buf, sizeof(buf));

    if (r < 0) {
      if (errno == EINTR || errno == EAGAIN) {
        continue;
      } else {
        mlogf(M_INFO, M_SHOW, "--- getHdrs: read() error %s\n",
              strerror(errno));
        state = 3;
        break;
      }
    }
    if (r == 0) {
      if (b->size == 0)
        break;
      if (strstr(b->data, "\r\n\r\n") == NULL &&
          strstr(b->data, "\n\n") == NULL) {
        mlogf(M_ERROR, M_SHOW, "-#- HTTP header ended prematurely\n");
        state = 3;
        break;
      }
    }

    add2buffer(b, buf, r);
    total += r;

    /* DO THIS IN THE ReqHandler
     * on first run through, ensure that this is a POST req. if cimxml req
     */
//     if (first)
//       fprintf(stderr, "buf is '%s', cmd is '%s'\n", buf, cmd);
//     if (r && first) {
//       if (strncasecmp(buf, cmd, strlen(cmd)) != 0) {
//         /*
//          * not what we expected - still continue to read to not confuse
//          * the client 
//          */
//         state = 1;
//       }
//       first = 0;
//     }

    /*
     * success condition: end of header 
     */
    if (strstr(b->data, "\r\n\r\n") != NULL ||
        strstr(b->data, "\n\n") != NULL) {
      break;
    }

    if (total >= hdrLimmit) {
      mlogf(M_ERROR, M_SHOW, "-#- Possible DOS attempt detected\n");
      state = 2;
      break;
    }
  }
  return state;
}

int
pauseCodec(char *name)
{
  int             rc = 0;
  char           *n;

  if (noHttpPause)
    return 0;
  if (httpPauseStr == NULL) {
    if (httpPauseStr) {
      char           *p;
      p = httpPauseStr = strdup(httpPauseStr);
      while (*p) {
        *p = tolower(*p);
        p++;
      }
    }
  }
  if (httpPauseStr) {
    char           *p;
    int             l = strlen(name);
    p = n = strdup(name);
    while (*p) {
      *p = tolower(*p);
      p++;
    }
    if ((p = strstr(httpPauseStr, n)) != NULL) {
      if ((p == httpPauseStr || *(p - 1) == ',')
          && (p[l] == ',' || p[l] == 0))
        rc = 1;
    }
    free(n);
    return rc;
  }
  noHttpPause = 1;
  return 0;
}

/* CimRs resource types */
#define RES_NS                    1
#define RES_NS_Collection         2
#define RES_Class                 3
#define RES_ClassCollection       4
#define RES_ClassAssocCollection  5
#define RES_ClassRefCollection    6
#define RES_ClassMethCollection   7
#define RES_ClassMeth             8
#define RES_Inst                  9
#define RES_InstCollection       10
#define RES_InstAssocCollection  11
#define RES_InstRefCollection    12
#define RES_InstMeth             13
#define RES_InstMethCollection   14
#define RES_Quals                15
#define RES_QuallsCollection     16

int parseCimRsQueryParams(char* p) {

  return 0;

}


typedef struct _cimrsreq {
  int scope;
#define SCOPE_NAMESPACE        1
#define SCOPE_NS_COLL          2
#define SCOPE_CLASS            3
#define SCOPE_CL_COLL          4
#define SCOPE_CL_METH          5
#define SCOPE_CL_METH_COLL     6
#define SCOPE_CL_ASSOC_COLL    7
#define SCOPE_CL_REF_COLL      8
#define SCOPE_INSTANCE         9
#define SCOPE_INST_COLL       10
#define SCOPE_INST_METH       11
#define SCOPE_INST_METH_COLL  12
#define SCOPE_INST_ASSOC_COLL 13
#define SCOPE_INST_REF_COLL   14
  char* path; /* strdup'd */
  char* ns;
  char* cn;
  char* meth; /* for class meth or inst meth */
  char* keyList;
  /* query stuff */
} CimRsReq;

/* decode */
static char* percentDecode(char* s) {
  return s;
}

/* for parsing constant ending fragments, such as: 
   /cimrs/namespaces
   /cimrs/namespaces/{ns}/classes?istopclass
   /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}/methods
 */
static int checkEndingFragment(char* fragment, char* name, int nameLen) {
  /* /cimrs/namespaces/{ns}/classes/{cn}/associators */
  if (fragment[nameLen] == 0) {
    fprintf(stderr, " request for %s\n", name);
    return 0;
  }
  /* something like /cimrs/namespaces/{ns}/classes/{cn}/associatorswithcrapattheend */
  else
    return -1;
}

static int parseMethodFragment(CimRsReq* req, char* fragment, const int coll, const int name) {

  char* meth;

  meth = strpbrk(fragment, "/");
  if (meth == NULL) {
    req->scope = coll;
    return checkEndingFragment(fragment, "methods", 7);
  }
  else {
    req->scope = name;
    req->meth = ++meth;
    return 0;
  }
}

static int parseInstanceFragment(CimRsReq* req, char* fragment) {

  char* tmp;
  char* keyList = strpbrk(fragment, "/");
  
  if (keyList == NULL) {
    req->scope = SCOPE_INST_COLL;
    return checkEndingFragment(fragment, "instances", 9);
  }
  else {
    keyList++; /* TODO: key list syntax validation? */
    tmp = strpbrk(keyList, "/");
    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist} */
    if (tmp == NULL) {
      req->scope = SCOPE_INSTANCE;
      req->keyList = percentDecode(keyList);
      return 0;
    }

    *tmp = 0;
    tmp++;    
    req->keyList = percentDecode(keyList);

    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}/associators */
    if (strncasecmp(tmp, "associators", 11) == 0) {
      req->scope = SCOPE_INST_ASSOC_COLL;
      return checkEndingFragment(tmp, "associators", 11);
    }
    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}/references */
    else if (strncasecmp(tmp, "references", 10) == 0) {
      req->scope = SCOPE_INST_REF_COLL;
      return checkEndingFragment(tmp, "references", 10);
    }
    /* /cimrs/namespaces/{ns}/classes/{cn}/instances/{keylist}methods */
    else if (strncasecmp(tmp, "methods", 7) == 0) {
      return parseMethodFragment(req, tmp, SCOPE_INST_METH_COLL, SCOPE_INST_METH);
    }
  }

  return -1;
}

int parseCimRsPath(char* p, CimRsReq* req) {

  char* path;
  char* tmp;
  char* ns;
  char* cn;
  char* query;

  req->path = strdup(p);
  path = req->path;
  tmp = strpbrk(++path, "/");

  /* "/cimrs/namespaces" should be the start of every req path */
  if (strncasecmp(++tmp, "namespaces", 10) != 0) {
    return -1;
  }

  /* get the query string if there is one */
  query = strpbrk(path, "?"); /* do we only need to check this for GET? */
  if (query != NULL) {
    *query = 0;
    parseCimRsQueryParams(++query);
    fprintf(stderr, " has query\n");
  }

  ns = strpbrk(tmp, "/");

  /* we got "/cimrs/namespaces" or "/cimrs/namespaceswithcrapafterit */
  if (ns == NULL) {
    req->scope = SCOPE_NS_COLL;
    return checkEndingFragment(tmp, "namespaces", 10); 
  }

  tmp = strpbrk(++ns, "/");

  if (tmp == NULL) {
    /* we got "/cimrs/namespaces/{ns} */
    fprintf(stderr, " request for namespace '%s'\n", ns);
    req->scope = SCOPE_NAMESPACE;
    return 0;
  }

  *tmp = 0; /* null-out the '/' so that we get just the namespace in ns */

  req->ns = percentDecode(ns);

  /* should be /cimrs/namespaces/{ns}/classes */
  /* TODO: could be "qualifiers" as well */
  if (strncasecmp(++tmp, "classes", 7) != 0) {
    return -1;
  }

  cn = strpbrk(tmp, "/");

  if (cn == NULL) {
    req->scope = SCOPE_CL_COLL;
    return checkEndingFragment(tmp, "classes", 7);
  }
  
  tmp = strpbrk(++cn, "/");

  if (tmp == NULL) {
    /* /cimrs/namespaces/{ns}/classes/{cn} */
    req->scope = SCOPE_CLASS;
    return 0;
  }

  tmp = strpbrk(cn, "/");
  *tmp = 0;

  req->cn = cn;

  tmp++;

  /* /cimrs/namespaces/{ns}/classes/{cn}/instances */
  if (strncasecmp(tmp, "instances", 9) == 0) {
    return parseInstanceFragment(req, tmp);
  }

  /* /cimrs/namespaces/{ns}/classes/{cn}/associators */
  else if (strncasecmp(tmp, "associators", 11) == 0) {
    req->scope = SCOPE_CL_ASSOC_COLL;
    return checkEndingFragment(tmp, "associators", 11);
  }
  /* /cimrs/namespaces/{ns}/classes/{cn}/references */
  else if (strncasecmp(tmp, "references", 10) == 0) {
    req->scope = SCOPE_CL_REF_COLL;
    return checkEndingFragment(tmp, "references", 10);
  }
  /* /cimrs/namespaces/{ns}/classes/{cn}/methods */
  else if (strncasecmp(tmp, "methods", 7) == 0) {
    return parseMethodFragment(req, tmp, SCOPE_CL_METH_COLL, SCOPE_CL_METH);
  }

  return -1;
}


static int
doHttpRequest(CommHndl conn_fd)
{
  char           *cp;
  Buffer          inBuf = { NULL, NULL, 0, 0, 0, 0, 0, 0 };
  RespSegments    response;
  static RespSegments nullResponse = { NULL, 0, 0, NULL, {{0, NULL}} };
  unsigned long   len;
  int             hl,
                  rc,
                  uset = 0;
  char           *hdr,
                 *path;
  int             discardInput = 0;
  MsgSegment      msgs[2];
  struct rusage   us,
                  ue;
  struct timeval  sv,
                  ev;
  CimXmlRequestContext ctx;
  int             breakloop;
  _SFCB_ENTER(TRACE_HTTPDAEMON, "doHttpRequest");

  if (pauseCodec("http"))
    for (breakloop = 0; breakloop == 0;) {
      fprintf(stdout, "-#- Pausing for codec http %d\n", currentProc);
      sleep(5);
    }

  inBuf.authorization = "";
  inBuf.protocol = "HTTP/1.1";
  inBuf.content_type = NULL;
  inBuf.content_length = UINT_MAX;
  inBuf.host = NULL;
  inBuf.useragent = "";
  int             badReq = 0;

  /* get everything */
  rc = getHdrs(conn_fd, &inBuf);

  if (rc == 1) { /* 1 indicates bad HTTP verb.  may no longer be needed */
    genError(conn_fd, &inBuf, 501, "Not Implemented", NULL);
    /*
     * we continue to parse headers and empty the socket to be graceful
     * with the client 
     */
    discardInput = 1;
  } else if (rc == 2) {
    genError(conn_fd, &inBuf, 400, "Bad Request", NULL);
    discardInput = 2;
    _SFCB_TRACE(1, ("--- potential DOS attempt discovered."));
  } else if (rc == 3) {
    genError(conn_fd, &inBuf, 400, "Bad Request", NULL);
    _SFCB_TRACE(1, ("--- exiting after request timeout."));
    commClose(conn_fd);
    exit(1);
  }

  if (inBuf.size == 0) {
    /*
     * no buffer data - end of file - quit 
     */
    _SFCB_TRACE(1, ("--- HTTP connection EOF, quit %d ", currentProc));
    commClose(conn_fd);
    exit(1);
  }

  /* get the protocol and path */
  inBuf.httpHdr = getNextHdr(&inBuf);
  fprintf(stderr, "httpHdr = %s\n", inBuf.httpHdr);
  char* tmp;
  for (badReq = 1;;) {
    if (inBuf.httpHdr == NULL)
      break;
    tmp = inBuf.httpHdr;

    path = strpbrk(tmp, " \t\r\n");
    if (path == NULL)
      break;
    *path++ = 0; /* get rid of the leading space */
    _SFCB_TRACE(1, ("--- Header: %s", inBuf.httpHdr));
    path += strspn(path, " \t\r\n");
    inBuf.protocol = strpbrk(path, " \t\r\n");

    if (inBuf.protocol == NULL)
      break;
    *inBuf.protocol++ = 0;

    //    fprintf(stderr, "path is '%s'\n", path);
//     ctx.path = path;
//     ctx.httpOp = tmp;

//     CimRsReq req = {0, NULL, NULL, NULL, NULL};
//     int prc = parseCimRsPath(path, &req);
//     fprintf(stderr, "parseCimPath returned %d\n", prc);
//     if (prc == 0) {
//       fprintf(stderr, "scope: %d\n", req.scope);
//       if (req.ns) fprintf(stderr, "%s", req.ns);
//       if (req.cn) fprintf(stderr, ":%s", req.cn);
//       if (req.keyList) fprintf(stderr, ".%s", req.keyList);
//       if (req.meth) fprintf(stderr, " %s", req.meth);
//       fprintf(stderr, "\n");
//     }

    badReq = 0;
  }

  if (badReq) {
    genError(conn_fd, &inBuf, 400, "Bad Request", NULL);
    _SFCB_TRACE(1, ("--- exiting after malformed header."));
    commClose(conn_fd);
    exit(1);
  }

  /* parse rest of headers */
  while ((hdr = getNextHdr(&inBuf)) != NULL) {
    _SFCB_TRACE(1, ("--- Header: %s", hdr));
    if (hdr[0] == 0)
{'fraction_non_alphanumeric': 0.15881280053059194, 'fraction_numerical': 0.013762228486154866, 'mean_word_length': 2.25758885167981, 'pattern_counts': {'":': 2, '<': 91, '<?xml version=': 0, '>': 345, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 6}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '599175', 'n_tokens_mistral': 23024, 'n_tokens_neox': 21962, 'n_words': 5220}