/*
 *  $Id: stuff.c,v 1.7 1999/11/18 00:27:20 jhawk Exp $
 *
 *  various helper functions
 *  (part of diswww, a Discuss->WWW gateway)
 */

#ifndef lint
static char rcsid_main[] = "$Id: stuff.c,v 1.7 1999/11/18 00:27:20 jhawk Exp $";
#endif

#include "proto.h"  /* the copyright is included in this file. */
#include "extern.h"
#include <sys/types.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"

/*
 *  create a blocking server socket
 *     port -- port number for the socket
 *  returns: file descriptor for the socket, or -1 in case of failure.
 */
int open_socket(int port)
{
  struct sockaddr_in sock;
  int s;
  const int on = 1;

  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);

  setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

  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
 *     str -- string to copy
 *  returns: a copy of the string
 *  If there isn't enough space to allocate, the child process exits.
 */
char *sstrdup(char *str)
{
  char *ret;

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

/*
 *  find the hostname of the client.
 *     addr -- socket structure
 *  returns: 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 = sstrdup(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;
}

/*
 *  set up for running as a 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();
}

/*
 *  parse the request path (ie, the pathname from the URL)
 *  into meeting host, meeting path and transaction number.
 *     path --    request path
 *     host --    hostname (return)
 *     meeting -- meeting (return)
 *     tr_num --  transaction number, or 0 if unspecified (return)
 *     lower --   lower limit from query, or 0 if no query, or -1 if left out
 *     upper --   upper limit from query, or 0 if no query, or -1 if left out,
 *                or -2 if there is no "-" in query
 *  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 = sstrdup(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;
    }
  }
}

/*
 *  read in the access permission file and generate the permission list.
 *     access -- filename for the permission file
 *  The format of the permission file is described in the documentation.
 */
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 = sstrdup(file);
      nr->type = strtok(NULL, " \t\n");
      nr->type = sstrdup(nr->type ? nr->type : T_html);

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

  fclose(afile);
  return root;
}

/*
 *  variables used by setproctitle
 */
extern char w_buf[];
char **Argv;
char *LastArgv;

/*
 *  set process title for ps listings
 *     fmt -- printf-style format for the title
 *     ... -- printf-style arguments
 */
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++ = ' ';
}

/*
 *  Save start and end of argv+env.vars for setproctitle.
 *     argc, argv, envp -- arguments passed to main()
 */
void envsetup(int argc, char **argv, char **envp)
{
  int i;

  Argv = argv;

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