#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#include "memStuffForPipSqueeks.h"
#include "cci.h"
#include "accept.h"

static ListenAddress listenPort;

extern char *GetLine();

int MCCIReturnListenPortSocketDescriptor()
{
	return(listenPort);
}

MCCICloseConnection(clientPort)
MCCIPort clientPort;
{
#ifdef DEBUG
	printf("CloseConnection(): I've been called\n");
#endif
	MoCCITerminateAConnection(clientPort);
	NetCloseConnection(clientPort);

}

MCCICloseAcceptPort()
{
	NetCloseAcceptPort(listenPort);
}

int MCCIServerInitialize(portNumber)
/* return 0 on failure */
/* return listenPort on success */
int portNumber;
{

	listenPort = NetServerInitSocket(portNumber);

	if (listenPort == -1)
		return(0);
	else
		return(1);

}

int MCCIGetSocketDescriptor(clientPort)
/* this routine is platform dependent and is not assumed to be supported
 * on all platforms.  It is only here for those routines that wish to have
 * a select() external to the MCCI library.
 * this routine extracts the socket descriptor from the MCCIPort and
 * returns it */
MCCIPort clientPort;
{
        return(NetGetSocketDescriptor(clientPort));
}



int MCCISendResponseLine(client,code,text)
MCCIPort client;
int code; 	/* response code */
char *text; 	/* text response (no newline)*/
{
int length/*,lengthSent*/;
char *buff;

	if (!(buff = (char *) MALLOC(strlen(text) + 7))) {
		/* out of memory */
		return(MCCI_OUTOFMEMORY);
		}

	sprintf(buff,"%d %s\r\n",code,text);
	length = strlen(buff);
	if (length != NetServerWrite(client,buff,length)) {
		return(MCCI_FAIL);
		}

	return(MCCI_OK);
}

MCCIPort MCCICheckAndAcceptConnection()
/* return NULL if no connection */
/* return a MCCIPort if connected */
{
MCCIPort client;

	if (NetIsThereAConnection(listenPort)){
		client = NetServerAccept(listenPort);
		}
	else {
		return(NULL);
		}
#ifdef DEBUG
	printf("Current cci connections: max number=%d, currentNumber=%d\n",
			MoCCIMaxNumberOfConnectionsAllowed(),
			MoCCICurrentNumberOfConnections());
#endif

	if (client && MoCCIMaxNumberOfConnectionsAllowed()) {
		/* if maxNumConnections == 0, then no limit */
		if ((MoCCICurrentNumberOfConnections() + 1) >
		    MoCCIMaxNumberOfConnectionsAllowed()) {
			MCCISendResponseLine(client,MCCIR_MAX_CONNECTIONS,
			"Maximum number of allowed CCI connections exceeded");
			MCCICloseConnection(client);
			return(NULL);

			}
		}
	return(client);
}



int MCCIIsThereInput(client)
/* return 1 on true, 0 on false */
MCCIPort client;
{
        if (!client)
                return(0);
        return(NetIsThereInput(client));
}

int MCCIReadContent(client,content)
/* read from client. Next line should contain Content-Length: value 
   and then the content body. Returns the number of chars read. 
   0 on error. space is allocated and placed into 'content'*/
MCCIPort client;
char **content;
{
char *s;
int length;
char garbage;
char *line;
int x;

#ifdef DEBUG
	printf("MCCIReadContent(): Just entered...about to GetLine()\n");
#endif
	*content = (char *) 0;
	line = GetLine(client);
#ifdef DEBUG
	printf("MCCIReadContent(): read line \"%s\"\n",line);
#endif
	/* read content length */
	s = strchr(line,':'); /* skip to length */
	if ((!s) || (!strlen(s))) {
		/* bad value */
		return(0);
		}
	s++;
	length = atoi(s);
	if ((length > 10000000) || (length < 0)) {
		/* bad value */
		return(0);
		}
	if (!((*content) = (char*) MALLOC(length+1))) {
		/* to recover protocol, this needs to be read in
			any way before returning, but if we're out of memory,
			it's likely hopeless anyway */
		for (x = 0; x < length; x++) {
			if (!NetRead(client,&garbage,1)) {
				break;
				}
			}
		return(0);
		}
#ifdef DEBUG
	printf("ReadContent(): about to read %d bytes\n",length);
#endif

	length = ReadBuffer(client,*content,length);
	(*content)[length]='\0';
	return(length);
}

int MCCIHandleSend(client,line,retText)
     /* take care of the SEND request parsing */
     /* return value to send back to cci client */
     MCCIPort client;
     char *line; 	/* GET request line */
     char *retText;	/* text to be returned to cci client */
{
  int retCode;
  char *s,*end,*start;
  
  if (!(s = strchr(line,' ')))
    { /* skip over SEND */
      strcpy(retText,"Error in protocol");
      return(MCCIR_ERROR);
    }
  
  
  GetWordFromString(s,&start,&end);
  if (!strncasecmp(start,MCCI_S_ANCHOR,strlen(MCCI_S_ANCHOR)))
    {
/* SEND ANCHOR */
      s = end;
      GetWordFromString(s,&start,&end);
      /* ejb 9 March 1995 added BEFORE and AFTER cases */
      if (start && (start != end))
/* SEND ANCHOR STOP => turn off SEND ANCHOR */
	if (!strncasecmp(start,MCCI_S_STOP,strlen(MCCI_S_STOP))) 
	  MCCIRequestSendAnchor(&retCode,retText,client,0);
	else 
/* SEND ANCHOR BEFORE => Mosaic sends anchor, BEFORE done getting */
	  if (!strncasecmp(start,MCCI_S_BEFORE,strlen(MCCI_S_BEFORE)))
	    MCCIRequestSendAnchor(&retCode,retText,client,MCCI_SEND_BEFORE);
	  else
/* SEND ANCHOR AFTER => Mosaic sends anchor, AFTER done getting */
	    if (!strncasecmp(start,MCCI_S_AFTER,strlen(MCCI_S_AFTER)) ||
		(!(start)))
	      MCCIRequestSendAnchor(&retCode,retText,client,MCCI_SEND_AFTER);
	    else
/* SEND ANCHOR XXXXX => Mosaic doesn't know what to do with it */
	      {
		/* we don't know what to do with it. */
		strcpy(retText,"what\'s this stuff after ANCHOR?");
		return(MCCIR_ERROR);
	      }
/* SEND ANCHOR => Mosaic sends anchor, AFTER done getting*/
      else
	MCCIRequestSendAnchor(&retCode,retText,client,MCCI_SEND_AFTER);
    }
  else if (!strncasecmp(start,MCCI_S_OUTPUT,strlen(MCCI_S_OUTPUT))) {
/* SEND OUTPUT */
		s = end;
		GetWordFromString(s,&start,&end);
		if (start && (start != end)) {
			if (!strncasecmp(start, MCCI_S_STOP,
						strlen(MCCI_S_STOP))){
				/* SEND OUTPUT STOP*/
				s = end;
				/* check for mime type */
				GetWordFromString(s,&start,&end);
				if (start && (start != end)) {
					*end = '\0';
					MCCIRequestSendOutput(&retCode,retText,
							client,0,start);
					}
				else {
					/* no output type... so all types */
					MCCIRequestSendOutput(&retCode,retText,
							client,0,(char *)0);
					}
				}
			else {
				/* SEND OUTPUT type */
				*end = '\0';
				MCCIRequestSendOutput(&retCode,retText,
							client,1,start);
				}
			
			}
		else {
			/* "SEND OUTPUT" so send it all */
			MCCIRequestSendOutput(&retCode,retText,
							client,1,(char *)0);
			}
		}
	
  else if (!strncasecmp(start,MCCI_S_BROWSERVIEW,strlen(MCCI_S_BROWSERVIEW))){
/* SEND BROWSERVIEW */
		s = end;
		GetWordFromString(s,&start,&end);
		if (start && (start != end)) {
			if (!strncasecmp(start, MCCI_S_STOP,
						strlen(MCCI_S_STOP))){
				/* SEND BROWSERVIEW STOP*/
				MCCIRequestSendBrowserView(&retCode,retText,
							client,0);
				}
			else {
				/* SEND BROWSERVIEW garbageHere */
				MCCIRequestSendBrowserView(&retCode,retText,
							client,1);
				}
			}
		else {
			/* SEND BROWSERVIEW*/
			MCCIRequestSendBrowserView(&retCode,retText,
							client,1);
			}
		}
	else {
/* SEND ??? */
		strcpy(retText,"SEND what???");
		return(MCCIR_ERROR);
		}
	return(retCode);
}


int MCCIHandlePost(client,line,retText)
/* take care of the Post request parsing */
/* return value to send back to cci client */
MCCIPort client;
char *line; 	/* GET request line */
char *retText;	/* text to be returned to cci client */
{
char *s,*end;
char *url;
char *postData;
int postDataLength;
char *mimeType;
int retCode;
int output;
char *tmpend;
char *next;

#ifdef DEBUG
	printf("MCCIHandlePost(): parsing line: \"%s\"\n",line);
#endif
	if (!(s = strchr(line,' '))){ /* skip over POST */
		strcpy(retText,"Error in protocol");
		return(MCCIR_ERROR);
		}

	GetWordFromString(s,&url,&end); /* Get <url> */
	if ((!url) || (url == end)) {
		strcpy(retText,"Hey bud, where's the URL for POST?");
		return(MCCIR_ERROR);
		}
	s = end;
	url++; /* skip over '<' */
	end--; /* backup over '>' */
	*end = '\0'; /* terminate url */

	url = strdup(url);

#ifdef DEBUG
	printf("MCCIHandlePost(): extracted url: \"%s\"\n",url);
#endif

	GetWordFromString(s,&mimeType,&end); /* Get Content Type*/
	if ((!mimeType) || (mimeType == end)) {
		strcpy(retText,"No Content-Type?");
		return(MCCIR_ERROR);
		}
	tmpend = end;

	s = end;
	GetWordFromString(s,&next,&end); /* move pointer to OUTPUT */

	*tmpend = '\0'; /* terminate the content-type */
	mimeType = strdup(mimeType);

#ifdef DEBUG
	printf("MCCIHandlePost(): mimeType: \"%s\"\n",mimeType);
#endif

	output = MCCI_DEFAULT;
	if (next && (next != end)) {
	    if (!strncasecmp(next,MCCI_S_OUTPUT,
				strlen(MCCI_S_OUTPUT))) {
		/* output tag */
		s = end;
		GetWordFromString(s,&next,&end);
		if (next && (next != end)) {
			if (!strncasecmp(next,MCCI_S_CURRENT,strlen(MCCI_S_CURRENT))) {
				output = MCCI_OUTPUT_CURRENT;
				}
			else if (!strncasecmp(next,MCCI_S_NEW,strlen(MCCI_S_NEW))) {
				output = MCCI_OUTPUT_NEW;
				}
			else if (!strncasecmp(next,MCCI_S_NONE,strlen(MCCI_S_NONE))) {
				output = MCCI_OUTPUT_NONE;
				}
			else {
				output = MCCI_DEFAULT;
				}
			s = end;
			GetWordFromString(s,&next,&end);
			}
		    }
	    }
	else {
		output = MCCI_DEFAULT; 
		}

#ifdef DEBUG
	printf("POST url = \"%s\",mimeType=\"%s\",output=%d\n",
				url,mimeType,output);
#endif

	postDataLength = MCCIReadContent(client,&postData);
	if (postDataLength < 1) {
		strcpy(retText,"No data for POST");
		return(MCCIR_ERROR);
		}

#ifdef DEBUG
	printf("Got the data, datalength = %d\n",postDataLength);
#endif

	MCCIRequestPost(client,&retCode, retText, url, mimeType, 
				postData, postDataLength, output);

	free(url);
	free(mimeType);
	
	return(retCode);
}


int MCCIHandleGet(client,line,retText)
/* take care of the GET request parsing */
/* return value to send back to cci client */
MCCIPort client;
char *line; 	/* GET request line */
char *retText;	/* text to be returned to cci client */
{
char *s;
char *url/*,*start*/,*end,*next;
int output/*,absRel*/;
char *headerExt;
int headerExtLength;
int retCode;

	output = MCCI_DEFAULT;
/*	absRel = MCCI_DEFAULT;*/
	headerExt = (char *) 0;
 	headerExtLength=0;
	
	if (!(s = strchr(line,' '))){ /* skip over GET */
		strcpy(retText,"Error in protocol");
		return(MCCIR_ERROR);
		}
	GetWordFromString(s,&url,&end); /* URL */
	if (strncasecmp(url,"URL",3)) {
		strcpy(retText,"No URL?");
		return(MCCIR_ERROR);
		}
	s = end;
	GetWordFromString(s,&url,&end); /* actual <url> */
	if ((!url) || (url == end)) {
		strcpy(retText,"Hey bud, where's the URL?");
		return(MCCIR_ERROR);
		}
	s = end;
	url++; /* skip over '<' */
	end--; /* backup over '>' */
	*end = '\0'; /* terminate url */

	url = strdup(url);
#ifdef DEBUG
	printf("GetURL: URL=\"%s\"\n",url);
#endif

	GetWordFromString(s,&next,&end);
	if (next && (next != end)) {
	    if (!strncasecmp(next,MCCI_S_OUTPUT,
				strlen(MCCI_S_OUTPUT))) {
		/* output tag */
		s = end;
		GetWordFromString(s,&next,&end);
		if (next && (next != end)) {
			if (!strncasecmp(next,MCCI_S_CURRENT,strlen(MCCI_S_CURRENT))) {
				output = MCCI_OUTPUT_CURRENT;
				}
			else if (!strncasecmp(next,MCCI_S_NEW,strlen(MCCI_S_NEW))) {
				output = MCCI_OUTPUT_NEW;
				}
			else if (!strncasecmp(next,MCCI_S_NONE,strlen(MCCI_S_NONE))) {
				output = MCCI_OUTPUT_NONE;
				}
			else {
				output = MCCI_OUTPUT_CURRENT;
				}
			s = end;
			GetWordFromString(s,&next,&end);
			}
		    }
	    }
#ifdef DEBUG
	printf("pt #2 GetURL: URL=\"%s\"\n",url);
#endif

	if (next && (next != end)) {
	    if (!strncasecmp(next,MCCI_S_HEADER,strlen(MCCI_S_HEADER))) {
		/* get header extention */
		    headerExtLength = MCCIReadContent(client,&headerExt);
		    }
		}
#ifdef DEBUG
	printf("pt #3 GetURL: URL=\"%s\"\n",url);
#endif
        /* set flag to be caught in MoCCISendAnchorToCCI */
	cciStatPreventSendAnchor(client, url); 
	MCCIRequestGetURL(&retCode,retText,url,output,headerExt);
	if ((headerExtLength > 0) && (headerExt)) {
		FREE(headerExt);
		}
	free(url);
	return(retCode);
}



int MCCIHandleInput(client)
/* read input from the client and do something with it */
/* return 1 on success, 0 on failure or disconnect */
MCCIPort client;
{
int retCode;
char retText[MCCI_MAX_RETURN_TEXT];
char *line;

	line = GetLine(client);

#ifdef DEBUG
if (line)
  printf("Server Read: %s\n",line);
else
  printf("Server Read: NULL line");
#endif

	if (!line) {
		/* error or disconnect */
		MCCICloseConnection(client);
		return(0);
		}

/* parse the request */
/* to save speed & memory this parse is destructive to the text in 'line' */

	if (!strncasecmp(line,MCCI_S_DISCONNECT,strlen(MCCI_S_DISCONNECT))) {
                MCCISendResponseLine(client,MCCIR_DISCONNECT_OK,
                        "DISCONNECT request received");
		return(0);
		}
	else if (!strncasecmp(line,MCCI_S_GET,strlen(MCCI_S_GET))) {
		retCode = MCCIHandleGet(client,line,retText);
		MCCISendResponseLine(client,retCode,retText);
		}
	else if (!strncasecmp(line,MCCI_S_DISPLAY,strlen(MCCI_S_DISPLAY))) {
		/*
		MCCIRequestDisplay();
		*/
                MCCISendResponseLine(client,MCCIR_DISPLAY_OK,
                        "DISPLAY request received");
		}
	else if (!strncasecmp(line,MCCI_S_QUIT,strlen(MCCI_S_QUIT))) {
                MCCISendResponseLine(client,MCCIR_QUIT_OK,
                        "QUIT request received exiting...");
		MCCIRequestQuit();
		}
	else if (!strncasecmp(line,MCCI_S_SEND,strlen(MCCI_S_SEND))) {
		retCode = MCCIHandleSend(client,line,retText);
                MCCISendResponseLine(client,retCode,retText);
		}
	else if (!strncasecmp(line,MCCI_S_POST,strlen(MCCI_S_POST))) {
		retCode = MCCIHandlePost(client,line,retText);
		MCCISendResponseLine(client,retCode,retText);
		}
	else {
		/* 
		MCCIRRequestUnrecognized();
		*/
		MCCISendResponseLine(client,MCCIR_UNRECOGNIZED,
			"Command not recognized");
		}
		

	return(1);
}


MCCISendAnchorHistory(client,url)
MCCIPort client;
char *url;
{
char buff[1024];

	sprintf(buff,"%s <%s>", MCCI_S_ANCHOR, url);
	return(MCCISendResponseLine(client, MCCIR_ANCHOR_INFO,buff));
}


int MCCISendOutputFile(client,contentType,fileName)
MCCIPort client;
char *contentType;
char *fileName;
/* this routine used to send output back to the client */
{
int length;
int countDown;
char buff[1030];
struct stat fileInfo;
FILE *fp;

        if (stat(fileName,&fileInfo)) { /* get the length of the file */
                return(MCCI_FAIL);
		}

        if (!(fp = fopen(fileName,"r"))) {
                return(MCCI_FAIL);
                }

	sprintf(buff,"%d Send Data Output\r\n",MCCIR_SEND_DATA_OUTPUT);
	length = strlen(buff);
        if (length != NetServerWrite(client,buff,length)) {
                return(MCCI_FAIL);
                }

	sprintf(buff,"Content-Type: %s \r\n",contentType);
	length = strlen(buff);
        if (length != NetServerWrite(client,buff,length)) {
                return(MCCI_FAIL);
                }

	sprintf(buff,"Content-Length: %d \r\n",fileInfo.st_size);
	length = strlen(buff);
        if (length != NetServerWrite(client,buff,length)) {
                return(MCCI_FAIL);
                }

	countDown = fileInfo.st_size;
	while(countDown > 0) {
		if (0 < (length = fread(buff,1,1024,fp))) {
			if (length != NetServerWrite(client,buff,length)) {
				return(MCCI_FAIL);
				}
			countDown -= length;

			}
		else {
			/* error reading here...but we promised to send 
			   countDown number of bytes, so send nulls */
			while (countDown > 0) {
				if (1 != NetServerWrite(client,"\0",1)) {
					return(MCCI_FAIL);
					}
				countDown--;
				}
			}
		}
	fclose(fp);
	return(MCCI_OK);
	
}


int MCCISendBrowserViewOutput(client,url,contentType,data,dataLength)
/* Send BrowserView output response to client */
MCCIPort client;
char *url;
char *contentType;
char *data;
int dataLength;
{
char buff[1024];
int length;

	if (MCCI_OK!=MCCISendResponseLine(client,MCCIR_SEND_BROWSERVIEW,url)){
                return(MCCI_FAIL);
		}


	sprintf(buff,"Content-Type: %s\r\n",contentType);
	length = strlen(buff);
        if (length != NetServerWrite(client,buff,length)) {
                return(MCCI_FAIL);
                }

	sprintf(buff,"Content-Length: %d \r\n",dataLength);
	length = strlen(buff);
        if (length != NetServerWrite(client,buff,length)) {
                return(MCCI_FAIL);
                }

        if (dataLength!= NetServerWrite(client,data,dataLength)) {
                return(MCCI_FAIL);
		}
	
	return(MCCI_OK);
}
