#include <netinet/in.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>

const char id[] = "$Id: filtserver.html,v 1.1 2001/05/22 07:58:08 paudley Exp $";
#define VERSION "1.0"
#define MAXLINE 512
#define PASSWORD "zookes"
#define SA struct sockaddr

//#define debug(x)
#define debug(x) x

#undef MAX
#define MAX(a,b)  ((a) > (b) ? (a) : (b))

/* include readline */

static ssize_t my_read(int fd, char *ptr)
{
  static int	read_cnt = 0;
  static char	*read_ptr;
  static char	read_buf[MAXLINE];
  
  if (read_cnt <= 0) {
  again:
    if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
      if (errno == EINTR)
	goto again;
      return(-1);
    } else if (read_cnt == 0)
      return(0);
    read_ptr = read_buf;
  }
  
  read_cnt--;
  *ptr = *read_ptr++;
  return(1);
}

ssize_t readline(int fd, void *vptr, size_t maxlen)
{
  int		n, rc;
  char	c, *ptr;
  
  ptr = vptr;
  for (n = 1; n < maxlen; n++) {
    if ( (rc = my_read(fd, &c)) == 1) {
      *ptr++ = c;
      if (c == '\n')
	break;	/* newline is stored, like fgets() */
    } else if (rc == 0) {
      if (n == 1)
	return(0);	/* EOF, no data read */
      else
	break;		/* EOF, some data was read */
    } else
      return(-1);		/* error, errno set by read() */
  }
  
  *ptr = 0;	/* null terminate like fgets() */
  return(n);
}

/* end readline */

/* include writen */

// Write "n" bytes to a descriptor.
ssize_t writen(int fd, const void *vptr, size_t n)
{
  size_t		nleft;
  ssize_t		nwritten;
  const char	*ptr;
  
  // ignore SIGPIPE
  signal( SIGPIPE, SIG_IGN );

  ptr = vptr;
  nleft = n;
  while (nleft > 0) {
    if ( (nwritten = write(fd, ptr, nleft)) <= 0) {
      if (errno == EINTR)
	nwritten = 0;		/* and call write() again */
      else
	return(-1);			/* error */
    }
    nleft -= nwritten;
    ptr   += nwritten;
  }
  return(n);
}
/* end writen */

int main(int argc, char **argv)
{
  int			i, maxi, maxfd, listenfd, connfd, sockfd;
  int			nready, clients[FD_SETSIZE], x, port;
  char                  auth[FD_SETSIZE];
  ssize_t		n, clilen;
  fd_set		rset, allset;
  char			line[MAXLINE], passwd[64], version[128];
  char*                 tmp;
  struct sockaddr_in	cliaddr, servaddr;

  sprintf(version,"v%s (%s)",VERSION,id);

  if( argc < 2 ) {
    fprintf(stderr,"FiltServer %s\n",version);
    fprintf(stderr,"    syntax: filtserver <port> [ <password> ]\n");
    fprintf(stderr,"     (default passwd is \"%s\"\n", PASSWORD );
    exit(1);
  }
  if( ( port=strtol( argv[1], NULL, 10 ) ) < 1 || port > 65535 ) {
    fprintf(stderr,"Invalid port specification.\n");
    exit(1);
  }
  debug( printf("port: %d\n", port ) );

  if( argc < 3 )
    sprintf( passwd, PASSWORD );
  else 
    sprintf( passwd, argv[2] );

  debug( printf("passwd: %s\n", passwd ) );


  for( i=0; i< FD_SETSIZE; i++ ) { clients[i] = -1; auth[i] = 0; }
  maxi = -1;
  
  listenfd = socket( AF_INET, SOCK_STREAM, 0 );
  
  bzero( &servaddr, sizeof(servaddr) );
  servaddr.sin_family      = AF_INET;
  servaddr.sin_addr.s_addr = htonl( INADDR_ANY );
  servaddr.sin_port        = htons( port );
  
  if( bind(listenfd, (SA *) &servaddr, sizeof(servaddr)) == -1 ) {
    fprintf(stderr,"bind() error: %s\n", strerror(errno));
    exit(2);
  }
  
  listen(listenfd, 5);
  
  maxfd = MAX( listenfd, STDIN_FILENO ); /* initialize */

  FD_ZERO(&allset);
  FD_SET( listenfd, &allset );
  FD_SET( STDIN_FILENO, &allset );
  
  for ( ; ; ) {
    rset = allset;
    nready = select(maxfd+1, &rset, NULL, NULL, NULL);
    
    if( FD_ISSET( listenfd, &rset ) ) {
      // New Client connection
      clilen = sizeof(cliaddr);
      connfd = accept(listenfd,(SA*)&cliaddr,&clilen);
      debug( printf("new client: %s, port %d\n",
	     inet_ntop(AF_INET,&cliaddr.sin_addr,4,NULL),
	     ntohs(cliaddr.sin_port)) );
      for( i=0; i<FD_SETSIZE; i++ )
	if( clients[i] < 0 ) {
	  clients[i] = connfd;
	  break;
	}
      debug( printf( "connect(%d)...\n", i ) );
      // Print the banner...
      sprintf( line, "FiltServer v%s\n - Copyright Patrick Audley (1997)\n\nPlease enter password:\n", version );
      if( writen( connfd, line, strlen(line) ) < 0 ) {
	// Error writing to client..
	debug( printf( "Dropping client for write error.\n" ) );
	close( connfd );
	FD_CLR( connfd, &allset );
	clients[i] = -1;
	auth[i] = 0;
	for( i=0; i<FD_SETSIZE; i++ ) 
	  if( clients[i] > -1 ) maxi = i;
      }
      if( i == FD_SETSIZE ) {
	debug( printf( "Too many clients!\n" ) );
	close( connfd );
      }
      FD_SET( connfd, &allset );
      if( connfd > maxfd )
      	maxfd = connfd;	 /* for select */
      if( i > maxi )  
	maxi = i;  /* max index in client[] array */
      if( --nready <= 0 )
	continue;
    }
    for( i=0; i<=maxi; i++ ) {
      // check all clients for data/disconnects.
      if( ( sockfd = clients[i] ) < 0 )
	continue;
      if( FD_ISSET( sockfd, &rset ) ) {
	// This client has something.
	if( ( n=readline( sockfd, line, MAXLINE ) ) == 0 ) {
	  // connection closed by client.
	  debug( printf( "dropping(%d)...\n", i ) );
	  close( sockfd );
	  FD_CLR( sockfd, &allset );
	  clients[i] = -1;
	  auth[i] = 0;
	  for( i=0; i<FD_SETSIZE; i++ ) 
	    if( clients[i] > -1 ) maxi = i;	    
	} else {
	  debug( printf( "client_read(%d): %s", i, line ) );
	  // process input... look for passwd.
	  if( auth[i] > 0 ) {
	    // Already authenticated...  ignore..
	  } else if( strncmp( passwd, line, strlen(passwd) ) == 0 ) {
	    // Otherwise, compare input to passwd.
	    debug( printf( "auth(%d)...\n",i ) );
	    auth[i] = 1;
	    debug( printf( "write(%d)...\n", i ) );
	    sprintf( line, "Access Granted:  Streaming active.\n------------------------------------------\n" );
	    if( writen( sockfd, line, strlen(line) ) < 0 ) {
	      // Error writing to client..
	      debug( printf( "Dropping client for write error.\n" ) );
	      close( sockfd );
	      FD_CLR( sockfd, &allset );
	      clients[i] = -1;
	      auth[i] = 0;
	      for( i=0; i<FD_SETSIZE; i++ ) 
		if( clients[i] > -1 ) maxi = i;
	    }
	  } else {
	    sprintf( line, "Access Denied.\n" );
	    writen( sockfd, line, strlen(line) );
	    // drop the client.
	    debug( printf( "Dropping client for passwd error.\n" ) );
	    close( sockfd );
	    FD_CLR( sockfd, &allset );
	    clients[i] = -1;
	    auth[i] = 0;
	    for( i=0; i<FD_SETSIZE; i++ ) 
	      if( clients[i] > -1 ) maxi = i;
	  }
	}
	if( --nready <= 0 )
	  break; // no more readable descriptors.
      }
    }
    
    if( FD_ISSET( STDIN_FILENO, &rset ) ) {
      // Something on stdin for us.
      if( ( n=readline( STDIN_FILENO, line, MAXLINE ) ) == 0 ) {
	// oops...  problem...
	debug( printf( "nothing to read from stdin...\n" ) );
      } else {
	debug( printf( "read: %s", line ) );
	// write out line to each client..
	for( i=0; i<=maxi; i++ ) {
	  if( ( sockfd = clients[i] ) < 0 || auth[i] < 1 )
	    continue;
	  debug( printf( "write(%d)...\n", i ) );
	  if( writen( sockfd, line, n ) < 0 ) {
	    // Error writing to client..
	    debug( printf( "Dropping client for write error.\n" ) );
	    close( sockfd );
	    FD_CLR( sockfd, &allset );
	    clients[i] = -1;
	    auth[i] = 0;
	    for( i=0; i<FD_SETSIZE; i++ ) 
	      if( clients[i] > -1 ) maxi = i;
	  }
	}
      }
    }
  }
}

// $Log: filtserver.c,v $
// Revision 1.1  1997/10/22 06:26:12  paudley
// - Changed banner to not scroll off screen.
//
// Revision 1.0.0.1  1997/10/22 06:22:07  paudley
// Inital write
//


syntax highlighted by Code2HTML, v. 0.9