/*
 * main.c
 */
#include "copyright.h"

#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <signal.h>
#include <setjmp.h>
#include <pwd.h>
#include <sys/time.h>
#include <fcntl.h>
#include "defs.h"
#include INC_STRINGS
#include INC_UNISTD
#include INC_SYS_WAIT
#include "struct.h"
#include "data.h"
#include "packets.h"
#include "../patchlevel.h"
#include "../version.h"
#ifdef SENDFLAGS
#include "../cflags.h"
#endif

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

jmp_buf env;
int ignored[MAXPLAYER];	/* change 7/24/91 TC */

int indie = 0;			/* always be indie 8/28/91 TC */
void reaper();

main(argc, argv)
int argc;
char **argv;
{
    int team, s_type, w_queue = 0;
    int pno;
    int usage = 0;
    int err = 0;
    char *name, *ptr;
    struct passwd *pwent;
    int callHost=0;
    time_t starttime;
    int i;
    extern void forceShutdown ();
    char pseudo[PSEUDOSIZE];  /* Was a global - MK 9/30/94 */

    getpath();	/* added 11/6/92 DRG */
    errors = open(Error_File, O_WRONLY | O_APPEND | O_CREAT, 0644);
    dup2(errors,2);
    setbuf(stderr, NULL);
    dup2(errors,1);
    setbuf(stdout, NULL);

    name = *argv++;
    argc--;
    if ((ptr = RINDEX(name,'/')) != NULL)
	name = ptr + 1;
    while (*argv) {
	if (**argv == '-')
	    ++*argv;
	else
	    break;

	argc--;
	ptr = *argv++;
	while (*ptr) {
	    switch (*ptr) {
	    case 'u': 
		usage++; 
		break;
	    case 'i': 
		indie++; 
		break; /* 8/28/91 TC */
            case 'q': w_queue=atoi(*argv); argv++; argc--; break;
	    case 's': 
		xtrekPort=atoi(*argv); 
		callHost=1; 
		argv++; 
		argc--; 
		break;
#ifdef OBSERVERS
	    case 'O':
		Observer = 1;
		clueVerified = 1;
		break;
#endif
	    case 'd':
		host = *argv;
		argc--;
		argv++;
		break;
	    default: 
		ERROR(1,("%s: unknown option '%c'\n", name, *ptr));
		err++;
		break;
	    }
	    ptr++;
	}
    }
    if (usage || err) {
	printUsage(name);
	exit(err);
    }
    /* compatability */
    if (argc > 0)
	host = argv[0];
    srandom((int)getpid() * time((time_t *) 0));
    /* this finds the shared memory information */
    openmem(1);
    readsysdefaults();

#ifdef FEATURE_PACKETS
    readFeatures();
#endif

    me = NULL;			/* UDP fix (?) */
    if (callHost) {
	if (!connectToClient(host,xtrekPort)) {
	    exit(0);
	}
    } else {
	sock=0;	/* Because we were forked by inetd! */
	checkSocket();
	initClientData();	/* "normally" called by connectToClient() */
    }
    starttime=time(NULL);
    while (userVersion==0) {
	/* Waiting for user to send his version number.
	 * We give him ten seconds to do so...
	 */
	if (starttime+10 < time(NULL)) {
	    exit(1);
	}
	socketPause();
	readFromClient();
    }
    if (!checkVersion()) exit(1);

    SIGNAL(SIGALRM, SIG_IGN);
    SIGNAL(SIGTERM, forceShutdown);
    SIGNAL(SIGPIPE, SIG_IGN);

/*    getshipdefaults(); */
    sendMotd();

    pno = findslot(w_queue);
    if (pno < 0) {
	/* trigger client's "Sorry, but you cannot play xtrek now.
	   Try again later." */
	struct badversion_spacket packet;
	packet.type = SP_BADVERSION;
	packet.why = 1;
	sendClientPacket (&packet);
	flushSockBuf ();
	ERROR(2,("ntserv/main.c: Quitting: No slot available\n"));
	/* print some appropriate message */
	exit(1);
    }

    me = &players[pno];

    me->p_process = getpid ();
    me->p_timerdelay = UPDATE_SKIP; /* Default 5 updates/sec */

    myship = &me->p_ship;
    mystats = &me->p_stats;
    lastm = mctl->mc_current;
    SIGNAL(SIGINT, SIG_IGN);
    (void) SIGNAL(SIGCHLD, reaper);


    /* We set these so we won't bother updating him on the location of the
     *  other players in the galaxy which he is not near.  There is no
     *  real harm to doing this, except that he would then get more information
     *  than he deserves.
     * It is kind of a hack, but should be harmless.
     */
    me->p_x= -100000;	
    me->p_y= -100000;
    me->p_team=0;
#ifdef RCD
    me->gen_distress = 0;
#endif
    updateSelf();	/* so he gets info on who he is */
    updateShips();
    updatePlanets();
    flushSockBuf();

    /* Get login name */

    if ((pwent = getpwuid(getuid())) != NULL)
	STRNCPY(login, pwent->pw_name, NAME_LEN);
    else
	STRNCPY(login, "Bozo", NAME_LEN);
    login[NAME_LEN - 1] = '\0';

    strcpy(pseudo, "Guest");

    strcpy(me->p_name, pseudo);
    me->p_team=ALLTEAM;
    getname();
    strcpy(pseudo, me->p_name);

    keeppeace = (me->p_stats.st_flags / ST_KEEPPEACE) & 1;
/*    showgalactic = 1 + (me->p_stats.st_flags / ST_GALFREQUENT) & 1;*/

    /* Set p_hostile to hostile, so if keeppeace is on, the guy starts off
       hating everyone (like a good fighter should) */
    me->p_hostile = (FED|ROM|KLI|ORI);
    me->p_war = me->p_hostile;

    s_type = CRUISER;
    me->p_planets=0;
    me->p_genoplanets=0;
    me->p_armsbomb=0;
    me->p_genoarmsbomb=0;
    /* Set up a reasonable default */
    me->p_whydead=KQUIT;

    STRNCPY(me->p_login, login, NAME_LEN);
    me->p_login[NAME_LEN - 1] = '\0';

    {
	int i;
	for (i = 0; i < MAXPLAYER; i++) ignored[i] = 0;
    }

    STRNCPY(me->p_monitor, host, NAME_LEN);
    me->p_monitor[NAME_LEN - 1] = '\0';

#ifdef FULL_HOSTNAMES
    /* assume this is only place p_monitor is set, and mirror accordingly */
    /* 4/13/92 TC */
    STRNCPY(me->p_full_hostname, host, sizeof(me->p_full_hostname));
    me->p_full_hostname[sizeof(me->p_full_hostname) - 1] = '\0';
#endif

    logEntry(); /* moved down to get login/monitor 2/12/92 TMC */

#ifdef PING     /* 0 might just be legit for a local player */
    me->p_avrt = -1;
    me->p_stdv = -1;
    me->p_pkls_c_s = -1;
    me->p_pkls_s_c = -1;
#endif

    startTkills = me->p_stats.st_tkills;
    startTlosses = me->p_stats.st_tlosses;
    startTarms = me->p_stats.st_tarmsbomb;
    startTplanets = me->p_stats.st_tplanets;
    startTticks = me->p_stats.st_tticks;

    startSBkills = me->p_stats.st_sbkills;
    startSBlosses = me->p_stats.st_sblosses;
    startSBticks = me->p_stats.st_sbticks;

    setjmp(env);		/* Reentry point of game */
	/* give the player the motd and find out which team he wants */
    if (me->p_status != PALIVE) {
	me->p_x= -100000;
	me->p_y= -100000;
	updateSelf();
	updateShips();
	teamPick= -1;
	flushSockBuf();
	getEntry(&team, &s_type);
	repCount=0;		/* Make sure he gets an update immediately */
    }
    if (team == -1) {
	exitGame();
    }
    if(CheckBypass(login,host,Bypass_File)==TRUE)
      bypassed=1;
    else bypassed=0;


    if (indie) team = 4;	/* force to independent 8/28/91 TC */
    redrawall = 1;

    for (i = 0; i <= MAX_CP_PACKETS; i++)
	FD_SET (i, &inputMask);	/* Allow all input now */

    enter(team, 0, pno, s_type, pseudo);
   
#ifndef DEBUG
    for (i = 1; i < NSIG; i++) {
	SIGNAL(i, SIG_IGN);
    }
#endif   

/* +++ 2.6pl2 kantner@hot.caltech.edu 22-NOV-1994 09:46:00.74 */
    /*
    **  The daemon sends ntserv a SIGTERM after the slot is freed during a
    **  ghostbust.  Without this line the SIGTERM does nothing.  The slot is
    **  marked free, but really isn't.  Someone else joins the slot, and a
    **  copilot is formed.
    */
    SIGNAL(SIGTERM, forceShutdown);
/* --- */
    SIGNAL(SIGILL, SIG_DFL);		/* since illegal instruction is
					   so rare this is an useful one to
					   use to make core files for debugging
					   ie.  kill -SIGILL <process #> */
#ifdef OBSERVERS
    if (Observer) {
	me->p_status = POBSERV;     /* put observer in game */
        new_warning(UNDEF,"Lock onto a teammate or planet to see the action.");
        pmessage(me->p_no, MINDIV, addr_mess(me->p_no,MINDIV),
		 "Lock onto a teammate or planet to see the action.");
      }
    else
#endif
    me->p_status = PALIVE;			/* Put player in game */
    me->p_ghostbuster = 0;

    if (checkbanned(login, host) == TRUE) {
      FILE        *logfile;
      time_t curtime;

      pmessage(0, MALL, "GOD->ALL","%s (%s@%s) is banned from the game.",
              me->p_name, me->p_login, host);
      new_warning(UNDEF,"You are banned from the game.");
      me->p_explode=100;
      me->p_status=PEXPLODE;
      me->p_whydead=KQUIT;   /* make him self destruct */
      me->p_whodead=0;
      logfile=fopen(LogFileName, "a");
      if (logfile) {
        curtime=time(NULL);
        fprintf(logfile, "Banned and exiting: %s (%s@%s), (%c), %s",
              me->p_name,  me->p_login, host, shipnos[me->p_no], ctime(&curtime)
);
        fclose(logfile);
      }
    }

    /* Get input until the player quits or dies */
    input();
    return 1;	/* satisfy lint */
}

exitGame()
{
    char addrbuf[20];

    if (me != NULL && me->p_team != ALLTEAM) {
        sprintf(addrbuf, " %c%c->ALL",
	   teamlet[me->p_team], shipnos[me->p_no]);
#ifndef FULL_HOSTNAMES
	/* old-style leaving message 4/13/92 TC */
	pmessage2(0, MALL | MLEAVE, addrbuf, me->p_no,
	    "%s %s (%s) leaving the game (%.16s@%.16s)", 
	    ranks[me->p_stats.st_rank].name,
	    me->p_name,
	    me->p_mapchars, 
	    me->p_login,
	    me->p_monitor);
#else
	/* new-style leaving message 4/13/92 TC */
	pmessage2(0, MALL | MLEAVE, addrbuf, me->p_no, 
	    "%s %s (%s) leaving game (%.16s@%.32s)", 
	    ranks[me->p_stats.st_rank].name,
	    me->p_name,
	    me->p_mapchars, 
	    me->p_login,
	    me->p_full_hostname);
#endif
	me->p_stats.st_flags &= ~ST_CYBORG; /* clear this flag 8/27/91 TC */
	savestats();
	printStats();
    }

    if (me) {
	if(me->p_process != getpid()){
            ERROR(1,( "main/exitGame: process exiting from co-pilot!\n"));
            ERROR(1,( "%d != pid: %d\n", me->p_process, (int)getpid()));
            fflush(stderr);
            exit(0);
        }
	freeslot(me);
    }

    exit(0);
}
    

printUsage(prog)
	char	*prog;
{
    char *text = "\
Usage: %s [-u] [-q n] [-i] [-O] [-s n] [-d a]\n\
    -u       displays program usage\n\
    -q n     specifies queue number to join\n\
    -i       player is to be independant\n\
    -O       player is to be an observer\n\
\n\
Manual Connection\n\
    -s n     call client on socket number 'n'\n\
    -d a     call client on machine address 'a'\n";
    
    ERROR(1,(text,prog));
}

sendMotd()
{
    FILE *motd;
    char buf[100];	/* big enough... */
    char motd_file[255];

    time_t curtime;
    struct tm *tmstruct;
    int hour;

    sprintf(buf, "Welcome to %s, patchlevel %d", serv_version, PATCHLEVEL);
#ifdef SENDFLAGS
    sendMotdLine(buf);
    sendMotdLine("Compiler option flags used for this server:");
    sendMotdLine(cflags);
    sendMotdLine(" ");
#endif
    time(&curtime);
    tmstruct = localtime(&curtime);
    if (!(hour = tmstruct->tm_hour%12)) hour = 12;
    sprintf(buf,"Connection to server established at %d:%02d%s.", hour,
	    tmstruct->tm_min,
	    tmstruct->tm_hour >= 12 ? "pm" : "am");
    if (!time_access()) {
	strcat(buf, "  WE'RE CLOSED.  SEE HOURS BELOW.");
        sendMotdLine(buf);
        sendMotdLine(" ");
    }
    strcpy(motd_file,Motd_Path);
    if (clue) 				/* added 2/6/93 NBT */
	strcat(motd_file,N_MOTD_CLUE);
    else strcat(motd_file,Motd);
 
    /* the following will read a motd */
    if ((motd = fopen(motd_file, "r")) != NULL) {
	while (fgets(buf, sizeof (buf), motd) != NULL) {
	    buf[strlen(buf)-1] = '\0';
	    sendMotdLine(buf);
	}
	(void) fclose(motd);
    }
    if (show_sysdef)
        sendConfigMsg();
}

sendConfigMsg()
{
    register    i, flag;
    char buf[100];      /* big enough... */
    extern char *shiptypes[];

    /* ATM: send current configuration info as well */
    sendMotdLine(STATUS_TOKEN); /* indicate start of config info */

    if (clue)
	sendMotdLine("Clue verification enabled");

    sprintf(buf, "%-30s: ", "Binary Verification");
    switch (binconfirm) {
    case 0:
        strcat(buf, "disabled");
        break;
    case 1:
        strcat(buf, "enabled, RSA only");
        break;
    case 2:
        strcat(buf, "enabled, RSA and reserved.c");
        break;
    }
    sendMotdLine(buf);

    if (tournplayers > 8)
        sprintf(buf, "%-30s: disabled", "Tournament Mode");
    else
        sprintf(buf, "%-30s: %d players / side","Tournament Mode",tournplayers);
    sendMotdLine(buf);

    sprintf(buf, "%-30s: ", "Ships Allowed");
    for (i = 0; i < NUM_TYPES; i++) {
        if (i == ATT) continue;
        if (shipsallowed[i]) {
            strcat(buf, shiptypes[i]);
            strcat(buf, " ");
        }
    }
    sendMotdLine(buf);

    sprintf(buf, "%-30s: %s", "Tractor/Pressor Beams",
        weaponsallowed[WP_TRACTOR] ? "enabled" : "disabled");
    sendMotdLine(buf);

    sprintf(buf, "%-30s: %s", "Plasma Torpedoes",
        weaponsallowed[WP_PLASMA] ? "enabled" : "disabled");
    sendMotdLine(buf);

    if (weaponsallowed[WP_PLASMA]) {
        sprintf(buf, "%-30s: %d", "Kills Required for Plasma", plkills);
        sendMotdLine(buf);
    }

#ifdef nodef
    sprintf(buf, "%-30s: %s", "Scanning Beams",
        weaponsallowed[WP_SCANNER] ? "enabled" : "disabled");
    sendMotdLine(buf);
#endif
    if (shipsallowed[STARBASE]) {
        sprintf(buf, "%-30s: %s (%d)", "Rank Required for SB",
                ranks[sbrank].name, sbrank);
	sendMotdLine(buf);	
    }

    sprintf(buf, "%-30s: ", "Hidden Mode");
    switch (hiddenenemy) {
    case 0:
        strcat(buf, "inactive");
        break;
    case 1:
        strcat(buf, "tournament only");
        break;
    case 2:
        strcat(buf, "active");
        break;
    }
    sendMotdLine(buf);

    flag = 0;           /* see if non-home worlds are allowable entry points */
    for (i = 0; i < MAXPLANETS; i++) {
        if (startplanets[i] && (i % 10)) {
            flag = 1;
            break;
        }
    }
    sprintf(buf, "%-30s: %s", "Multiple Entry Planets", flag ? "yes" : "no");
    sendMotdLine(buf);

    sprintf(buf, "%-30s: %s", "Chaos Mode", chaos ? "enabled" : "disabled");
    sendMotdLine(buf);



#ifdef TORPCONFIG
    sprintf(buf, "%-30s: %s", "Vector Torps", vectortorps ? "yes" :
       "no");
    sendMotdLine(buf);
#endif

    sprintf(buf,"%-30s: ", "Hunter Killer");
    if (killer)
      strcat(buf, "Yes");
    else 
      strcat(buf, "No");
    sendMotdLine(buf);

#ifdef CHECKMESG
    sprintf(buf, "%-30s: ", "Message to GOD Log");
    if(loggod)
      strcat(buf, "Yes");
   else
      strcat(buf, "No");
    sendMotdLine(buf);
#endif

#ifdef SB_TRANSWARP
   sprintf(buf, "%-30s: ", "SB Transwarp");
   if (twarpMode)
      strcat(buf, "Yes");
   else
      strcat(buf, "No");
   sendMotdLine(buf);
#else
   sprintf(buf, "%-30s: No", "SB Transwarp");
   sendMotdLine(buf);
#endif

   sprintf(buf, "%-30s: %d", "Surrender Counter", surrenderStart);
   sendMotdLine(buf);
}

int CheckBypass(login, host, file)
char    *login, *host, *file;
{
  FILE  *bypassfile;
  char  log_buf[64], host_buf[64], line_buf[160];
  char  *position;
  int   num1; 
  int Hits=0;

  if ((bypassfile = fopen(file, "r")) == NULL) {
    ERROR(1,( "No bypass file %s\n", file));
    fflush(stderr);
    return(FALSE);
  }
  while(fgets(line_buf, 160, bypassfile) != NULL) {
    /* Split line up */
    if((*line_buf=='#')||(*line_buf=='\n'))
      continue;
    if ((position = (char *) RINDEX(line_buf, '@')) == 0) {
      ERROR(1,( "Bad line in bypass file\n"));
      fflush(stderr);
      continue;
    }

    num1 = position - line_buf;
    STRNCPY(log_buf, line_buf, num1); /* copy login name into log_buf */
    log_buf[num1] = '\0';
    STRNCPY(host_buf, position + 1, 64); /* copy host name into host_buf */
    /* Cut off any extra spaces on the host buffer */
    position = host_buf;
    while (!isspace(*position))
      position++;
    *position = '\0';

    /*
    ERROR(1,( "Login: <%s>; host: <%s>\n", login, host));
    ERROR(1,("    Checking Bypass <%s> and <%s>.\n",log_buf,host_buf));
    */

    if (*log_buf=='*')
      Hits=1;
    else if (!strcasecmp(login, log_buf)){
      Hits=1;
    }
    if(Hits==1)
      {
        if (*host_buf == '*'){  /* Bypass any host */
          Hits++;
          break;                /* break out now. otherwise Hits will get reset
                                   to one */
        }
        else if (!strcasecmp(host,host_buf) ||   /* Bypass subdomains */
	        (strstr(host,host_buf)!=NULL)) { /* (eg, "*@usc.edu" */
          Hits++;
          break;                /* break out now. otherwise Hits will get reset
                                   to one */
        }
      }
  }
  fclose(bypassfile);
  if(Hits>=2)
    {
      /*
      ERROR(1,("Bypassing %s@%s\n",login,host));
      fflush(stderr);
      */
      return(TRUE);
    }
  else{
    /*
    ERROR(1,("NOT Bypassing %s@%s\n",login,host));
    */
    return(FALSE);
  }
}

/*ARGSUSED*/		/* some systems pass the signal when caught */
void reaper(sig)
{
    WAIT_TYPE stat=0;
    static int pid;

    MZERO( &stat, sizeof(WAIT_TYPE) );

    while ((pid = WAIT3(&stat, WNOHANG, 0)) > 0) ;
    HANDLE_SIG(SIGCHLD,reaper);

/* added the below code to catch the reason for a child dying - NBT 9/28/92 */
	if (!WIFEXITED(stat)) 
		ERROR(1,("Process # %d signal caught\n",pid));
	if (WEXITSTATUS(stat)) ERROR(1,("exited with state %d\n",stat));
	if (WIFSIGNALED(stat)) ERROR(1,("exited because of signal\n"));
	if (WTERMSIG(stat)) ERROR(1,("signal state %d\n",stat));
#ifdef WCOREDUMP
	if (WCOREDUMP(stat)) ERROR(1,("core image made\n"));
#endif
	if (WIFSTOPPED(stat)) ERROR(1,("stopped\n"));
	if (WSTOPSIG(stat)) ERROR(1,("stop signal was %d\n",stat));
}

printStats()
{
    FILE *logfile;
    time_t curtime;

    logfile=fopen(LogFileName, "a");
    if (!logfile) return;
    curtime=time(NULL);
    fprintf(logfile, "Leaving: %-16s (%c%c) %3dP %3dA %3dW/%3dL %3dmin %2d\n\t<%s@%s> %s", 
	    me->p_name, 
	    teamlet[me->p_team],
	    shipnos[me->p_no],
	    me->p_stats.st_tplanets - startTplanets,
	    me->p_stats.st_tarmsbomb - startTarms,
	    me->p_stats.st_tkills - startTkills,
	    me->p_stats.st_tlosses - startTlosses,
	    (me->p_stats.st_tticks - startTticks)/600,
	    numPlanets(me->p_team),
	    me->p_login,
#ifndef FULL_HOSTNAMES
	    me->p_monitor,
#else
	    me->p_full_hostname,
#endif
	    ctime(&curtime));
    fclose(logfile);
}

int checkbanned(login, host)
char    *login, *host;
{
  FILE  *bannedfile;
  char  log_buf[64], host_buf[64], line_buf[160];
  char  *position;
  int           num1;
  int   Hits=0;                 /* Hits==2 means we're banned */

  if ((bannedfile = fopen(Banned_File, "r")) == NULL) {
    ERROR(1,( "No banned file %s\n", Banned_File));
    fflush(stderr);
    return(FALSE);
  }
  while(fgets(line_buf, 160, bannedfile) != NULL) {
    /* Split line up */
    if((*line_buf=='#')||(*line_buf=='\n'))
      continue;
    if ((position = (char *) RINDEX(line_buf, '@')) == 0) {
      ERROR(1,( "Bad line in banned file\n"));
      fflush(stderr);
      continue;
    }
    num1 = position - line_buf;
    STRNCPY(log_buf, line_buf, num1); /* copy login name into log_buf */
    log_buf[num1] = '\0';
    STRNCPY(host_buf, position + 1, 64); /* copy host name into host_buf */
    /* Cut off any extra spaces on the host buffer */
    position = host_buf;
    while (!isspace(*position))
      position++;
    *position = '\0';

    /*
      ERROR(1,( "Login: <%s>; host: <%s>\n", login, host));
    ERROR(1,("    Checking Banned <%s> and <%s>.\n",log_buf,host_buf));
    */
    if(*log_buf=='*')

      Hits=1;
    else if (!strcmp(login, log_buf))
      Hits=1;
    if(Hits==1)
      {
        if (*host_buf == '*'){  /* Lock out any host */
          Hits++;
          break;                /* break out now. otherwise Hits will get reset
                                   to one */
        }
        else if(strstr(host,host_buf)!=NULL){ /* Lock out subdomains (eg, "*@usc
.edu" */
          Hits++;
          break;                /* break out now. otherwise Hits will get reset
                                   to one */
        }
        else if (!strcmp(host, host_buf)){ /* Lock out specific host */
          Hits++;
          break;                /* break out now. otherwise Hits will get reset
                                   to one */
        }
      }
  }
  fclose(bannedfile);
  if(Hits>=2)
    return(TRUE);
  else
    return(FALSE);
}

#ifdef SHORT_PACKETS
/* ARGSUSED */
message_flag(cur,address)
struct message *cur;
char *address;
{
    /* This is to prevent false sending with SP_S_WARNING */
    cur->args[0] = DINVALID;
}
#endif
