/*
 * serverX.c - a simple web server, modified as an "echo server"
 *                                  for the termfusion perl module
 * 
 * author:  Max-Gerd Retzlaff <m.retzlaff@gmx.net>,
 *                            <mgr@hannover.ccc.de>
 * date:    $Date: 2002/7/26 00:34:23 CEST $
 * version: 5.echo.1
 *
 * Copyright (c) 2002 by Max-Gerd Retzlaff <m.retzlaff@gmx.net>,
 *                                         <mgr@hannover.ccc.de>
 *
 * This application is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111 USA.  
 */

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

#include <stdarg.h>

#include <curses.h>

#define LISTEN_QUEUE 10
#define MAXBUF 2048

#define DL_LOWEST 1
#define DL_LOW 25
#define DL_MEDIUM 50
#define DL_HIGH 75
#define DL_HIGHEST 99
#define DL_MAX 100
/* #define DEBUGLEVEL DL_HIGH */
int debuglevel = DL_HIGH;

int myport = 2342;

void sigchld_handler( int s ) {
  while( wait(NULL) > 0 );
}

int log( int min_dl, int max_dl, int requestno, char msgformat[], ... ) {
  va_list msg;

  /*  if ( ( DEBUGLEVEL < min_dl ) || ( DEBUGLEVEL >= max_dl ) ) { */
  if ( ( debuglevel < min_dl ) || ( debuglevel >= max_dl ) ) {
    return(1);
  }

  va_start( msg, msgformat );
  fprintf( stderr, "server: request #%d: ", requestno );
  vfprintf( stderr, msgformat, msg );
  va_end(msg);
  fflush(NULL);

  return(0);
}

int mysend( int new_sockfd, int requestno, char msg[] ) {
  int sendret;
  sendret = send( new_sockfd, msg, strlen(msg), 0 );
  if ( sendret == -1 ) {
    perror("send");
    return(1);
  }
  log( DL_HIGHEST, DL_MAX, requestno, "send info: msglength: %d, sent %d\n",
       strlen(msg), sendret );
  return(0);
}

int init_socket() {
  struct sockaddr_in my_addr;
  int yes=1;
  int sockfd;
  struct sigaction sa;

  sa.sa_handler = sigchld_handler;
  sigemptyset( &sa.sa_mask );
  sa.sa_flags = SA_RESTART;
  if ( sigaction( SIGCHLD, &sa, NULL ) == -1 ) {
    perror("sigaction");
    exit(1);
  }
  
  if ( ( sockfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
    perror("socket");
    exit(1);
  }

  if ( setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int) )
       == -1 ) {
    perror("setsockopt");
    exit(1);
  }

  /* set server */
  my_addr.sin_family = AF_INET;
  my_addr.sin_port = htons(myport);
  my_addr.sin_addr.s_addr = INADDR_ANY;
  memset( &(my_addr.sin_zero), '\0', 8 );

  if ( bind( sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr) )
       == -1 ) {
    perror("bind");
    exit(1);
  }

  if ( listen( sockfd, LISTEN_QUEUE ) == -1 ) {
    perror("listen");
    exit(1);
  }
 
  log( DL_LOW, DL_MAX, 0, "I'm started and feeling fine. Waiting for requests...\n" );

  return(sockfd);
}

int react_upon_request( int new_sockfd, char request[MAXBUF], int requestno ) {
  char string[MAXBUF];

  if ( strncasecmp( request, "get termsize", 12 ) == 0 ) {

    log( DL_LOW, DL_MAX, requestno, "client requests \"get termsize\"\n" );
    setterm( (char *)0 );
    sprintf( string, "cols: %d, rows: %d\n", COLS, LINES );
    mysend( new_sockfd, requestno, string );

  } else {
    log( DL_LOW, DL_MAX, requestno, "can't parse the request\n" );
    printf( "%s", request );
    fflush(NULL);
  }

  return(0);
}

int mainloop( int sockfd ) {
  struct sockaddr_in their_addr;
  int new_sockfd;
  int sin_size, numbytes;
  long int requestno=0; // requestno will flow over...
  char buf[MAXBUF];

  while(1) {

    sin_size = sizeof( struct sockaddr_in );
    if ( ( new_sockfd = accept( sockfd, (struct sockaddr *)&their_addr,
			    &sin_size ) ) == -1 ) {
      perror("accept");
      continue;
    }
    requestno++;

    log( DL_LOW, DL_MAX, requestno, "connection from %s\n", inet_ntoa( their_addr.sin_addr ) );

    if ( !fork() ) {

      close(sockfd); /* forked child does not listen */
      
      while ( (numbytes = recv( new_sockfd, buf, MAXBUF-1, 0 ) ) != 0 ) {
	if ( numbytes == -1 ) {
	  perror("recv");
	} else {
	  buf[numbytes] = '\0';

	  log( DL_MEDIUM, DL_MAX, requestno, "client sent: \n%s\n", buf );
	  log( DL_LOW, DL_MEDIUM, requestno, "client sent a request\n", buf );
	  log( DL_HIGHEST, DL_MAX, requestno, "client request info: len is %d, numbytes is %d\n", strlen(buf), numbytes );

	  react_upon_request( new_sockfd, buf, requestno );
	}
      }
      
      log( DL_LOW, DL_MAX, requestno, "connection closed.\n", "" );
      close(new_sockfd);
      return(0); /* end of child */
    }
    close(new_sockfd);
  }

  return(0);
}

/* int main(void) { */
int main( int argc, char *argv[] ) {
  int sockfd;

  if ( argc > 1 ) {
    if ( sscanf( argv[1], "%d", &myport ) != 1 ) {
      log( DL_LOW, DL_MAX, -1, "ERROR: Invalid port number, using default port %d.\n", myport );
    }
  }

  if ( argc > 2 ) {
    if ( sscanf( argv[2], "%d", &debuglevel ) != 1 ) {
      log( DL_LOW, DL_MAX, -1, "ERROR: Invalid debug level, using default debug level %d.\n", debuglevel );
    }
  }
 
  sockfd = init_socket();
  mainloop( sockfd ); 
  close( sockfd );

  return(0);
}
