/*

$Id: stuff.c,v 1.5 94/03/11 23:18:18 bert Exp $

*/

#include "extern.h"
#include "proto.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdio.h>
#include <strings.h>
#include <ctype.h>
#include <math.h>
#include <malloc.h>
#include <errno.h>
#include <stdarg.h>
#include "status.h"

/*
 *  open_socket creates a blocking server socket on the specified
 *  port.  It returns the file descriptor for the (yet unaccepted)
 *  socket, or -1 in case of failure.
 */
int open_socket(int port)
{
  struct sockaddr_in sock;
  int s;

  if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0)
    return -1 ;

  memset(&sock, 0, sizeof(sock));
  sock.sin_family = AF_INET;
  sock.sin_port = htons(port);
  sock.sin_addr.s_addr = htonl(INADDR_ANY);

  if (bind(s, (struct sockaddr*)&sock, sizeof(sock)) < 0)
    return -1;

  if (listen(s, SRV_SOCK_BACKLOG) < 0)
    return -1;

  return s ;
}

/*
 *  duplicate a string by allocating space and copying
 */
char *strdup(char *str)
{
  char *ret;

  if (! str) return NULL;
  ret = (char*)malloc(strlen(str)+1);
  CHECK_MALLOC(ret);
  strcpy(ret,str);
  return ret;
}

/*
 *  This finds the hostname of the client and returns it.
 *  The returned host name is either the canonical machine name from
 *  gethostbyaddr, or the dotted IP address.
 */
char *find_peer_name(struct sockaddr_in *addr)
{
  char *host;
  struct hostent *peer;
  long ip_addr;

  peer = gethostbyaddr(&(addr->sin_addr.s_addr),
		       sizeof(addr->sin_addr.s_addr), AF_INET);

  if (peer && peer->h_name)          /* we got a name */
    host = strdup(peer->h_name);
  else {                             /* we didn't get a name-- use IP #s */
    ip_addr = htonl(addr->sin_addr.s_addr);
    host = (char*)malloc(20);
    CHECK_MALLOC(host);
    sprintf(host, "%d.%d.%d.%d",
	    (int)((ip_addr >> 24) & 0xff),
	    (int)((ip_addr >> 16) & 0xff),
	    (int)((ip_addr >>  8) & 0xff),
	    (int)(ip_addr & 0xff));
  }

  return host;
}

/*
 *  This will set up for running as daemon (w/o tty, etc)
 */
void background(void)
{
  pid_t pid;

  close(STDIN_FILENO);
  close(STDOUT_FILENO);
  close(STDERR_FILENO);
  chdir(SRV_ROOT_DIR);   
  umask(SRV_UMASK);

  if ((pid=fork()) < 0) {
    SYSLOG_ERROR("initial fork failed");
    exit(1) ;
  } else if (pid != 0) {
    exit(0);
  }

  setsid();
}

/*
 *  This will pass the request path (ie, the pathname from the URL)
 *  into meeting host, meeting path and transaction number.
 *  Contents of `path' is preserved.
 */

int parse_path(char *path, char **host, char **meeting,
	       int *tr_num, int *lower, int *upper)
{
  char *p, *q, *s;

  while ((*path) == '/')            /* get rid of leading slashes. */
    path++;

  path = strdup(path);              /* keep a separate copy */

  if (p = strchr(path, '?')) {      /* query? */
    (*p) = '\0';
    if (q = strchr((++p), '-')) {
      (*q) = '\0';
      (*upper) = (*(++q) != '\0') ? atoi(q) : -1;
    } else (*upper) = -2;
    (*lower) = (*p != '\0') ? atoi(p) : -1;
  } else (*lower) = (*upper) = 0;

  if (! (p = strchr(path, '/'))) {  /* no components?  oops. */
    (*host) = (*meeting) = NULL;
    (*tr_num) = 0;
    free(path);
    return -1;
  } else {
    (*p) = '\0';
    (*host) = path;                 /* NOTE: we DO need to deallocate this */
    if (! (q = strrchr((++p), '/'))) {
      (*tr_num) = 0;
    } else {
      (*q) = '\0';
      (*tr_num) = (strcmp((++q),"list") && (*q != '\0')) ? atoi(q) : -1;
    }
    if (strchr(p, '/')) {
      (*meeting) = (char*)malloc(2+strlen(p));
      CHECK_MALLOC((*meeting));     /* NOTE: we DO need to deallocate this */
      (**meeting) = '/';
      strcpy((*meeting)+1, p);
      return 0;
    } else {
      (*meeting) = (char*)malloc(20+strlen(p));
      CHECK_MALLOC((*meeting));     /* NOTE: we DO need to deallocate this */
      strncpy((*meeting), "/usr/spool/discuss/", 19);
      strcpy((*meeting)+19, p);
      return 0;
    }
  }
}

/*
 *  This reads in the access permission file and generates the access
 *  permission list.  The permission file will be searched for a match
 *  from end toward start.
 */
struct access *access_list(char* access)
{
  FILE *afile;
  char buf[SRV_BUFSIZE];
  struct access *root = NULL;
  char *file;
  int cnt=0;

  if (! (afile = fopen(access, "r"))) {
    SYSLOG_EWARN("opening access file failed");
    return (struct access*)NULL;
  }

  while (fgets(buf, SRV_BUFSIZE, afile)) {
    cnt++;
    if ((file = strtok(buf, " \t\n"))) {
      struct access *nr;

      nr = (struct access*)malloc(sizeof(struct access));
      CHECK_MALLOC(nr);

      nr->filename = strdup(file);
      nr->type = strtok(NULL, " \t\n");
      nr->type = strdup(nr->type ? nr->type : T_html);

      nr->next = root;
      root = nr;
    }
  }

  fclose(afile);
  return root;
}

/*
 *  setproctitle stuff
 */
extern char w_buf[];
char **Argv;
char *LastArgv;

/*
 *  set process title for ps listings
 *  (stolen from sendmail8)
 */
void setproctitle(char *fmt, ...)
{
	char *p;
	int i;
	va_list ap;

	va_start(ap, fmt);
	vsprintf(w_buf, fmt, ap);
	va_end(ap);

	i = strlen(w_buf);

	if (i > LastArgv - Argv[0] - 2)
	{
		i = LastArgv - Argv[0] - 2;
		w_buf[i] = '\0';
	}
	(void) strcpy(Argv[0], w_buf);
	p = &Argv[0][i];
	while (p < LastArgv)
		*p++ = ' ';
}

/*
 *  Set things up so setproctitle can deal.
 */
void envsetup(int argc, char **argv, char **envp)
{
  int i;

  for (i = 0; envp[i] != NULL; i++);

  /*
   *  Save start and extent of argv for setproctitle.
   */

  Argv = argv;
  if (i > 0)
    LastArgv = envp[i - 1] + strlen(envp[i - 1]);
  else
    LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
}
