/*

Written by Attila Vass (C)2010
Modifications require permission

gcc voyager_aquire.c -o voyager_aquire

*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <net/if.h>
#include <errno.h>
#include <signal.h>
#include <string.h>

//#define	DEBUGCHU	1


#define	FLICKER_HERTZ	60

#define	SEND_BUFFER_SIZE	1024
#define	RECEIVE_BUFFER_SIZE	2048


#define	RTSPINIT_1	"DESCRIBE rtsp://%d.%d.%d.%d:%d/cam1/mjpeg RTSP/1.0\r\nCSeq: 1\r\nAccept: application/sdp\r\nUser-Agent: ./AttilaRTSPClient (Attila Vass (C)2010)\r\n\r\n"
#define	RTSPINIT_2	"SETUP rtsp://%d.%d.%d.%d:%d/cam1/mjpeg/trackID=1 RTSP/1.0\r\nCSeq: 2\r\nTransport: RTP/AVP/TCP;unicast;interleaved=0-1\r\nUser-Agent: ./AttilaRTSPClient (Attila Vass (C)2010)\r\n\r\n"
#define	RTSPINIT_3	"SETUP rtsp://%d.%d.%d.%d:%d/cam1/mjpeg/trackID=2 RTSP/1.0\r\nCSeq: 3\r\nTransport: RTP/AVP/TCP;unicast;interleaved=2-3\r\nSession: %s\r\nUser-Agent: ./AttilaRTSPClient (Attila Vass (C)2010)\r\n\r\n"
#define	RTSPINIT_GET	"PLAY rtsp://%d.%d.%d.%d:%d/cam1/mjpeg/ RTSP/1.0\r\nCSeq: 4\r\nSession: %s\r\nRange: npt=0.000-\r\nUser-Agent: ./AttilaRTSPClient (Attila Vass (C)2010)\r\n\r\n"
#define	RTSPINIT_TDOWN	"TEARDOWN rtsp://%d.%d.%d.%d:%d/cam1/mjpeg/ RTSP/1.0\r\nCSeq: 5\r\nSession: %s\r\nUser-Agent: ./AttilaRTSPClient (Attila Vass (C)2010)\r\n\r\n"

#define	GO_NIGHT_VISION	"GET /param.cgi?action=update&ImageSource.I0.Sensor.WB=AWB&ImageSource.I0.DayNight.Mode=manual&ImageSource.I0.DayNight.ManualStatus=night&ImageSource.I0.DayNight.SwitchDelay=1&ImageSource.I0.DayNight.AmbiguitySupression=no&ImageSource.I0.DayNight.SwitchLevel=low&ImageSource.I0.AE.Mode=&ImageSource.I0.AE.Shutter=0&ImageSource.I0.AE.BacklightEnabled=no&ImageSource.I0.Sensor.HREnabled=no&ImageSource.I0.Sensor.Flickerless=%d&ImageSource.I0.AE.WDREnabled=no&JsVar=result&OnJs=OnResultNoReload HTTP/1.1\r\nAccept: */*\r\nReferer: http://%d.%d.%d.%d/SETTING_SENSOR.asp\r\nAccept-Language: en-us\r\nAccept-Encoding: gzip, deflate\r\nUser-Agent: ./AttilaRTSPClient (Attila Vass (C)2010)\r\nHost: %d.%d.%d.%d\r\nConnection: Keep-Alive\r\nAuthorization: Basic %s\r\n\r\n"
#define	GO_DAY_VISION	"GET /param.cgi?action=update&ImageSource.I0.Sensor.WB=AWB&ImageSource.I0.DayNight.Mode=manual&ImageSource.I0.DayNight.ManualStatus=day&ImageSource.I0.DayNight.SwitchDelay=1&ImageSource.I0.DayNight.AmbiguitySupression=no&ImageSource.I0.DayNight.SwitchLevel=low&ImageSource.I0.AE.Mode=&ImageSource.I0.AE.Shutter=0&ImageSource.I0.AE.BacklightEnabled=no&ImageSource.I0.Sensor.HREnabled=no&ImageSource.I0.Sensor.Flickerless=%d&ImageSource.I0.AE.WDREnabled=no&JsVar=result&OnJs=OnResultNoReload HTTP/1.1\r\nAccept: */*\r\nReferer: http://%d.%d.%d.%d/SETTING_SENSOR.asp\r\nAccept-Language: en-us\r\nAccept-Encoding: gzip, deflate\r\nUser-Agent: ./AttilaRTSPClient (Attila Vass (C)2010)\r\nHost: %d.%d.%d.%d\r\nConnection: Keep-Alive\r\nAuthorization: Basic %s\r\n\r\n"


unsigned char	ConnAddr[4]={0,0,0,0};
char		SessionID[64]={'\0'};
unsigned int	CameraPort,AllSize;

#define	MAX_PICTURE_BUFFER	327680
unsigned char	PictureBuffer[MAX_PICTURE_BUFFER];
unsigned int	PictureBufferPointer=0;

#define	MAX_PAYLOAD_BUFFER	393216
unsigned char	PayloadBuffer[MAX_PAYLOAD_BUFFER];
unsigned int	PayloadBufferPointer=0;

enum {
CONNSTAT_NONE=0,
CONNSTAT_INIT_1,
CONNSTAT_INIT_2,
CONNSTAT_INIT_3,
CONNSTAT_INIT_GET,
CONNSTAT_DONE,
CONNSTAT_SUCCESS,
CONNSTAT_PROBLEM
};

time_t			TimeOutIfReached;
int			RB_Ptr=0;
int			ClientSocketID=-1,brokenpipe=0;
unsigned int		ConnectionStatus=CONNSTAT_NONE;
struct sockaddr_in	serv_addr;
char			SendBuffer[SEND_BUFFER_SIZE];
unsigned char		ReceiveBuffer[RECEIVE_BUFFER_SIZE];
char			TmpBuff[SEND_BUFFER_SIZE];

unsigned char jpg_header[25] = {
0xff,0xd8,0xff,0xe0,0x00,0x10,0x4a,0x46,0x49,0x46,0x00,0x01,0x01,0x00,0x00,0x01,0x00,0x01,0x00,0x00,0xFF,0xDB,0x00,0x43,0x00
};

unsigned char lum_dc_codelens[] = {
0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
};

unsigned char lum_dc_symbols[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
};

unsigned char lum_ac_codelens[] = {
0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d,
};

unsigned char lum_ac_symbols[] = {
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa,
};

unsigned char chm_dc_codelens[] = {
0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
};

unsigned char chm_dc_symbols[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
};

unsigned char chm_ac_codelens[] = {
0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77,
};

unsigned char chm_ac_symbols[] = {
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
0xf9, 0xfa,
};

unsigned char *MakeQuantHeader(unsigned char *p, unsigned char *qt, int tableNo)
{
	*p++ = 0xff;
	*p++ = 0xdb;	/* DQT */
	*p++ = 0;	   /* length msb */
	*p++ = 67;	  /* length lsb */
	*p++ = tableNo;
	memcpy(p, qt, 64);
return (p + 64);
}

unsigned char *MakeHuffmanHeader(unsigned char *p, unsigned char *codelens, int ncodes, unsigned char *symbols, int nsymbols, int tableNo, int tableClass)
{
	*p++ = 0xff;

	*p++ = 0xc4;	/* DHT */
	*p++ = 0;	   /* length msb */
	*p++ = 3 + ncodes + nsymbols; /* length lsb */
	*p++ = (tableClass << 4) | tableNo;
	memcpy(p, codelens, ncodes);
	p += ncodes;
	memcpy(p, symbols, nsymbols);
	p += nsymbols;
return (p);
}

unsigned char *MakeDRIHeader(unsigned char *p, u_short dri)
{
	*p++ = 0xff;
	*p++ = 0xdd;	/* DRI */
	*p++ = 0x0;	 /* length msb */
	*p++ = 4;	   /* length lsb */
	*p++ = dri >> 8;        /* dri msb */
	*p++ = dri & 0xff;      /* dri lsb */
return (p);
}

/*
     *  Arguments:
     *    type, width, height: as supplied in RTP/JPEG header
     *    lqt, cqt: quantization tables as either derived from
     *         the Q field using MakeTables() or as specified
     *         in section 4.2.
     *    dri: restart interval in MCUs, or 0 if no restarts.
     *
     *    p: pointer to return area
     *
     *  Return value:
     *    The length of the generated headers.
     *
     *    Generate a frame and scan headers that can be prepended to the
     *    RTP/JPEG data payload to produce a JPEG compressed image in
     *    interchange format (except for possible trailing garbage and
     *    absence of an EOI marker to terminate the scan).
*/
int MakeHeaders(unsigned char *p, int type, int w, int h, unsigned char *lqt, unsigned char *cqt, u_short dri)
{
	unsigned char *start = p;

	/* convert from blocks to pixels */
	w <<= 3;
	h <<= 3;

	*p++ = 0xff;
	*p++ = 0xd8;	/* SOI */

	p = MakeQuantHeader(p, lqt, 0);
	p = MakeQuantHeader(p, cqt, 1);

	if (dri != 0)
	        p = MakeDRIHeader(p, dri);

	*p++ = 0xff;
	*p++ = 0xc0;	/* SOF */
	*p++ = 0;	   /* length msb */
	*p++ = 17;	  /* length lsb */
	*p++ = 8;	   /* 8-bit precision */
	*p++ = h >> 8;          /* height msb */
	*p++ = h;	   /* height lsb */
	*p++ = w >> 8;          /* width msb */
	*p++ = w;	   /* wudth lsb */
	*p++ = 3;	   /* number of components */
	*p++ = 0;	   /* comp 0 */
	if (type == 0)
	        *p++ = 0x21;    /* hsamp = 2, vsamp = 1 */
	else
	        *p++ = 0x22;    /* hsamp = 2, vsamp = 2 */
	*p++ = 0;	   /* quant table 0 */
	*p++ = 1;	   /* comp 1 */
	*p++ = 0x11;	/* hsamp = 1, vsamp = 1 */
	*p++ = 1;	   /* quant table 1 */
	*p++ = 2;	   /* comp 2 */
	*p++ = 0x11;	/* hsamp = 1, vsamp = 1 */
	*p++ = 1;	   /* quant table 1 */
	p = MakeHuffmanHeader(p, lum_dc_codelens,
		          sizeof(lum_dc_codelens),
		          lum_dc_symbols,
		          sizeof(lum_dc_symbols), 0, 0);
	p = MakeHuffmanHeader(p, lum_ac_codelens,
		          sizeof(lum_ac_codelens),
		          lum_ac_symbols,
		          sizeof(lum_ac_symbols), 0, 1);
	p = MakeHuffmanHeader(p, chm_dc_codelens,
		          sizeof(chm_dc_codelens),
		          chm_dc_symbols,
		          sizeof(chm_dc_symbols), 1, 0);
	p = MakeHuffmanHeader(p, chm_ac_codelens,
		          sizeof(chm_ac_codelens),
		          chm_ac_symbols,
		          sizeof(chm_ac_symbols), 1, 1);

	*p++ = 0xff;
	*p++ = 0xda;	/* SOS */
	*p++ = 0;	   /* length msb */
	*p++ = 12;	  /* length lsb */
	*p++ = 3;	   /* 3 components */
	*p++ = 0;	   /* comp 0 */
	*p++ = 0;	   /* huffman table 0 */
	*p++ = 1;	   /* comp 1 */
	*p++ = 0x11;	/* huffman table 1 */
	*p++ = 2;	   /* comp 2 */
	*p++ = 0x11;	/* huffman table 1 */
	*p++ = 0;	   /* first DCT coeff */
	*p++ = 63;	  /* last DCT coeff */
	*p++ = 0;	   /* sucessive approx. */

return (p - start);
};


int InitConnection(unsigned short port)
{
int		setFlag = 1;
register	int a;


#if DEBUGCHU
	printf("\nChecking Connection...");fflush(stdout);
#endif


	bzero((char*)&serv_addr,sizeof(serv_addr));
	serv_addr.sin_port = htons(port);
	serv_addr.sin_family = AF_INET;
	for(a=0;a<4;a++) {*(char*)(((char*)&serv_addr.sin_addr)+a)=ConnAddr[a];}

	a=0;
	if((ClientSocketID=socket(AF_INET,SOCK_STREAM,0))<0)
	{
#if DEBUGCHU
		printf("\nClient: can't open stream socket on port %d. (%d.%d.%d.%d)\0",port,
		(unsigned char)*((char*)((char*)&serv_addr.sin_addr)),
		(unsigned char)*((char*)((char*)&serv_addr.sin_addr+1)),
		(unsigned char)*((char*)((char*)&serv_addr.sin_addr+2)),
		(unsigned char)*((char*)((char*)&serv_addr.sin_addr+3)));
#endif
		ClientSocketID=-1;
		return(-1);
	}

#if DEBUGCHU
 printf("\nOpenClientSocket : ClientSocketID = %d (%d.%d.%d.%d : %d)",ClientSocketID,(unsigned char)*((char*)((char*)&serv_addr.sin_addr)),(unsigned char)*((char*)((char*)&serv_addr.sin_addr+1)),(unsigned char)*((char*)((char*)&serv_addr.sin_addr+2)),(unsigned char)*((char*)((char*)&serv_addr.sin_addr+3)),port);fflush(stdout);
#endif

	if(connect(ClientSocketID,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)
	{
#if DEBUGCHU
		switch(errno)
		{
			case EACCES:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Permission denied...");fflush(stdout);
				break;
			case ENONET:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Machine is not on the network...");fflush(stdout);
				break;
			case ENOPKG:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Package not installed...");fflush(stdout);
				break;
			case ECOMM:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Communication error on send...");fflush(stdout);
				break;
			case EPROTO:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Protocol error...");fflush(stdout);
				break;
			case ETIMEDOUT: 
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Connection timed out...");fflush(stdout);
				break;
			case ENOTSOCK:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Socket operation on non-socket...");fflush(stdout);
				break;
			case EDESTADDRREQ:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Destination address required...");fflush(stdout);
				break;
			case EMSGSIZE:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Message too long...");fflush(stdout);
				break;
			case EPROTOTYPE:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Protocol wrong type for socket...");fflush(stdout);
				break;
			case ENOPROTOOPT:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Protocol not available...");fflush(stdout);
				break;
			case EPROTONOSUPPORT:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Protocol not supported...");fflush(stdout);
				break;
			case ESOCKTNOSUPPORT:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Socket type not supported...");fflush(stdout);
				break;
			case EOPNOTSUPP:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Operation not supported...");fflush(stdout);
				break;
			case EPFNOSUPPORT:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Protocol family not supported...");fflush(stdout);
				break;
			case EAFNOSUPPORT:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Address family not supported by protocol family...");fflush(stdout);
				break;
			case EADDRINUSE:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Address already in use...");fflush(stdout);
				break;
			case EADDRNOTAVAIL:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Can't assign requested address...");fflush(stdout);
				break;
			case ENETDOWN:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Network is down...");fflush(stdout);
				break;
			case ENETUNREACH:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Network is unreachable...");fflush(stdout);
				break;
			case ENETRESET:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Network dropped connection on reset...");fflush(stdout);
				break;
			case ECONNABORTED:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Software caused connection abort...");fflush(stdout);
				break;
			case ECONNRESET:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Connection reset by peer...");fflush(stdout);
				break;
			case ENOBUFS:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : No buffer space available...");fflush(stdout);
				break;
			case EISCONN:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Socket is already connected...");fflush(stdout);
				break;
			case ENOTCONN:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Socket is not connected...");fflush(stdout);
				break;
			case ESHUTDOWN:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Can't send after socket shutdown...");fflush(stdout);
				break;
			case ETOOMANYREFS:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Too many references: can't splice...");fflush(stdout);
				break;
			case ECONNREFUSED:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Connection refused...");fflush(stdout);
				break;
			case EHOSTDOWN:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Host is down...");fflush(stdout);
				break;
			case EHOSTUNREACH:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : No route to host...");fflush(stdout);
				break;
			case EALREADY:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Operation already in progress...");fflush(stdout);
				break;
			case EINPROGRESS:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Operation now in progress...");fflush(stdout);
				break;
			case EWOULDBLOCK:
				printf("\nConnect Failed : Can NOT connect to it...\n# Cause : Operation would block...");fflush(stdout);
				break;
			default:
				printf("\nConnect Failed : Can NOT connect to it...\n# Error Number : %d...",a);fflush(stdout);
				break;
		}
#endif
		shutdown(ClientSocketID,SHUT_RDWR);
		close(ClientSocketID);
		ClientSocketID=-1;
		return(-1);
	}
	else
		ConnectionStatus=CONNSTAT_INIT_1;

#if DEBUGCHU
printf("\nOk, now ioctl...");fflush(stdout);
#endif

 	if(ioctl(ClientSocketID,FIONBIO,&setFlag)<0)
	{
		printf("\nClient: can't set no blocking\0");
		shutdown(ClientSocketID,SHUT_RDWR);
		close(ClientSocketID);
		ClientSocketID=-1;
		ConnectionStatus=CONNSTAT_NONE;
		return(-1);
	}

#if DEBUGCHU
printf("\nConnection successful...");fflush(stdout);
#endif

return(1);
}



int SendThisString(char *ThisString)
{
int	a=0;

#if DEBUGCHU
printf("\nSendThisString\n'%s'\n\n",ThisString);fflush(stdout);
#endif

	if(ConnectionStatus==CONNSTAT_NONE) return(-1);

	while((a<(SEND_BUFFER_SIZE-1))&&(ThisString[a]!='\0'))
	{
		SendBuffer[a]=ThisString[a];
		++a;
	}
	SendBuffer[a++]='\0';

#if DEBUGCHU
//printf("= '%s'",SendBuffer);fflush(stdout);
#endif

	if((a=write(ClientSocketID,SendBuffer,a))==-1)
	{
#if DEBUGCHU
printf("=> closing...");fflush(stdout);
#endif
		if(brokenpipe)
		{
			shutdown(ClientSocketID,SHUT_RDWR);
			close(ClientSocketID);
			ClientSocketID=-1;
			ConnectionStatus=CONNSTAT_NONE;
			brokenpipe=0;
			return(-1);
		}
		else
		{
			ConnectionStatus=CONNSTAT_PROBLEM;
			return(-2);
		}
	}
#if DEBUGCHU
printf("(%d)",a);fflush(stdout);
#endif

return(0);
}

int FindMatching(char *Src, char* InThis)
{
int a=0,b,c;

	b=0;while(Src[b]!='\0') ++b;
	c=0;while(InThis[c]!='\0') ++c;
	c-=b;
	for(a=0;a<=c;a++)
	{
		b=0;
		while((Src[b]==InThis[a+b])&&(Src[b]!='\0')&&(InThis[a+b]!='\0')) ++b;
		if(Src[b]=='\0') return(a);
	}
return(-1);
}

void EvaluateReceiveBuffer(void)
{
int	a,b;
time_t	tmptt;

#if DEBUGCHU
printf("\nReceived Buffer Pointer = %d [%d]: \n'",RB_Ptr,ConnectionStatus);fflush(stdout);


	for(a=0;a<RB_Ptr;a++)
	{
		if(ReceiveBuffer[a]>=' ') printf("%c",ReceiveBuffer[a]); else printf("%c[%02x]",ReceiveBuffer[a],ReceiveBuffer[a]);fflush(stdout);
	}

#endif

	switch(ConnectionStatus)
	{
		case CONNSTAT_INIT_1 :
			if(FindMatching("a=control:trackID=1",ReceiveBuffer)>=0)
			{
				sprintf(TmpBuff,RTSPINIT_2,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],CameraPort);
				SendThisString(TmpBuff);
				ConnectionStatus=CONNSTAT_INIT_2;
			}
		break;
		case CONNSTAT_INIT_2 :
			if((b=FindMatching("Session: ",ReceiveBuffer))>=0)
			{
				b+=9;
				for(a=0;((a<64)&&(ReceiveBuffer[b]!='\r')&&(ReceiveBuffer[b]!='\0'));a++,b++) SessionID[a]=ReceiveBuffer[b];
				SessionID[a]='\0';

#if DEBUGCHU
				printf("\n\nSESSION ID = %s",SessionID);fflush(stdout);
#endif

				sprintf(TmpBuff,RTSPINIT_3,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],CameraPort,SessionID);
				SendThisString(TmpBuff);
				ConnectionStatus=CONNSTAT_INIT_3;
			}
		break;
		case CONNSTAT_INIT_3 :
			if((b=FindMatching("Session: ",ReceiveBuffer))>=0)
			{
				sprintf(TmpBuff,RTSPINIT_GET,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],CameraPort,SessionID);
				SendThisString(TmpBuff);
				ConnectionStatus=CONNSTAT_INIT_GET;
			}
		break;
	}
}

int EvaluateReceivedPayload(void)
{
unsigned int	Q,W,H,LQT_Ptr,CQT_Ptr,PayLoadStart,PayLoadLength,NextPayloadPtr,FragOffs,Chn,PType,JType,nuofhdr;
int		a,b,ReVa;
unsigned char	GotHeader=0;

#if DEBUGCHU
printf("\nEval...");fflush(stdout);
#endif

	PayLoadStart=-1;ReVa=1;				// UnSuccessful by default
	for(a=0;a<(PayloadBufferPointer-4);a++)
	{
		if((PayloadBuffer[a]=='\r')&&(PayloadBuffer[a+1]=='\n')&&(PayloadBuffer[a+2]=='\r')&&(PayloadBuffer[a+3]=='\n'))	// full response
		{
			PayLoadStart=a+4;
		}
	}
	if(PayLoadStart==-1) return(ReVa);

	while(PayLoadStart<PayloadBufferPointer)
	{
#if DEBUGCHU
for(b=(PayLoadStart);b<(PayLoadStart+32);b++)
{
	printf("%02x ",PayloadBuffer[b]);fflush(stdout);
}
#endif
		if(PayloadBuffer[PayLoadStart++]!=0x24)		// Interleaved frame
		{
#if DEBUGCHU
			printf("\nNot Pointing At Interleaved frame at %d  (%02x)",PayLoadStart,PayloadBuffer[PayLoadStart]);fflush(stdout);
#endif
			return(ReVa);
		}

#if DEBUGCHU
printf("\n%d:IF...",PayLoadStart);fflush(stdout);
#endif

		Chn=(unsigned int)PayloadBuffer[PayLoadStart++];
		PayLoadLength=(((unsigned int)PayloadBuffer[PayLoadStart++])<<8);
		PayLoadLength+=((unsigned int)PayloadBuffer[PayLoadStart++]);
		NextPayloadPtr=(PayLoadStart+PayLoadLength);
		++PayLoadStart;						// Version/Padding/Extension/Marker
		PType=(unsigned int)PayloadBuffer[PayLoadStart++];	// 0x1A : JPEG-compressed video

#if DEBUGCHU
printf(" l=%d : t=%02x tl=%d",PayLoadLength,PType,NextPayloadPtr);fflush(stdout);
#endif

		switch(PType)
		{
			case 0x1A :
			case 0x9A :
				PayLoadStart+=11;			// Skip Seq,TS,SyncSourceID  and Type Specific(1byte)
				FragOffs=(((unsigned int)PayloadBuffer[PayLoadStart++])<<16);
				FragOffs+=(((unsigned int)PayloadBuffer[PayLoadStart++])<<8);
				FragOffs+=((unsigned int)PayloadBuffer[PayLoadStart++]);
				JType=(unsigned int)PayloadBuffer[PayLoadStart++];
				Q=(unsigned int)PayloadBuffer[PayLoadStart++];
				W=(unsigned int)PayloadBuffer[PayLoadStart++];
				H=(unsigned int)PayloadBuffer[PayLoadStart++];

#if DEBUGCHU
printf("\n\tVideo t=%d q=%d %dx%d Frag=%d  PicBufPtr=%d   (%d)",JType,Q,W*8,H*8,FragOffs,PictureBufferPointer,(PictureBufferPointer-FragOffs));fflush(stdout);
#endif

				if(FragOffs==0)
				{
					if(GotHeader==0)
					{
						PayLoadStart+=2;			// Skip MBZ and Precision
						nuofhdr=(((unsigned int)PayloadBuffer[PayLoadStart++])<<8);
						nuofhdr+=((unsigned int)PayloadBuffer[PayLoadStart++]);
						LQT_Ptr=PayLoadStart;
						CQT_Ptr=PayLoadStart+(nuofhdr>>1);		// Should be 128/2 = 64
						PayLoadStart+=nuofhdr;

						memset(PictureBuffer,0,MAX_PICTURE_BUFFER);

#if DEBUGCHU
printf("\nHEADER! Tablng=%d %d:%d [%02x][%02x]",nuofhdr,LQT_Ptr,CQT_Ptr,PayloadBuffer[LQT_Ptr],PayloadBuffer[CQT_Ptr]);fflush(stdout);
#endif

						PictureBufferPointer=MakeHeaders(&PictureBuffer[18], JType, (int)W, (int)H, &PayloadBuffer[LQT_Ptr], &PayloadBuffer[CQT_Ptr], 0);
						PictureBufferPointer+=18;
						memcpy(PictureBuffer,jpg_header,25);
						for(b=(PayLoadStart);((b<NextPayloadPtr)&&(PictureBufferPointer<MAX_PICTURE_BUFFER));b++,PictureBufferPointer++)
						{
							PictureBuffer[PictureBufferPointer]=PayloadBuffer[b];
						}
						GotHeader=1;
						PayLoadStart=NextPayloadPtr;
					}
					else
					{
#if DEBUGCHU
printf("\nNEW HEADER at %d -> terminating",PayLoadStart);fflush(stdout);
#endif
						PayLoadStart=PayloadBufferPointer;
					}
/*
printf("\nStart=%d  : ",PayLoadStart);fflush(stdout);
for(b=(PayLoadStart);b<(PayLoadStart+32);b++)
{
	printf("%02x ",PayloadBuffer[b]);fflush(stdout);
}
*/
				}
				else
				{
					if(GotHeader==1)
					{

#if DEBUGCHU
printf("\nBODY! (%d bytes to %d) Frag=%d   ",(NextPayloadPtr-PayLoadStart),PictureBufferPointer,FragOffs);fflush(stdout);
#endif

						for(b=(PayLoadStart);((b<NextPayloadPtr)&&(PictureBufferPointer<MAX_PICTURE_BUFFER));b++,PictureBufferPointer++)
						{
							PictureBuffer[PictureBufferPointer]=PayloadBuffer[b];
						}
#if DEBUGCHU
printf(" -> Addr=%d   ",PictureBufferPointer);fflush(stdout);
#endif
					}
					if(PType==0x9A)
					{
						PayLoadStart=PayloadBufferPointer;	// last chunk
						ReVa=0;
					}
					else
						PayLoadStart=NextPayloadPtr;
				}
			break;
			default :
#if DEBUGCHU
				printf("\nDid not recognize %d...",PType);fflush(stdout);
#endif
				PayLoadStart=NextPayloadPtr;
			break;
		}
	}
return(ReVa);
}





#define	MAX_B64_SIZE	256

char	Base64Table[64]={'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'};
char	Base64EncodedResult[MAX_B64_SIZE];
int Base64Encode(char *This)			// Every 8 bit will be encoded down to 6 bit
{
int		a,b,c,mode;
unsigned char	current,accu;

	mode=0;b=0;
	for(a=0;((This[a]!='\0')&&(b<MAX_B64_SIZE));a++)
	{
		current=This[a];
		switch(mode)
		{
			case 0 :
				if(b==MAX_B64_SIZE) {Base64EncodedResult[b-1]='\0';return(1);}
				Base64EncodedResult[b++]=Base64Table[((unsigned int)((current>>2)&0x3F))&0x3F];
				accu=((current&0x03)<<4);
				mode=1;
			break;
			case 1 :
				if(b==MAX_B64_SIZE) {Base64EncodedResult[b-1]='\0';return(1);}
				Base64EncodedResult[b++]=Base64Table[((unsigned int)(((current>>4)&0x0F)|accu))&0x3F];
				accu=((current&0x0F)<<2);
				mode=2;
			break;
			case 2 :
				if(b==MAX_B64_SIZE) {Base64EncodedResult[b-1]='\0';return(1);}
				Base64EncodedResult[b++]=Base64Table[((unsigned int)(((current>>6)&0x03)|accu))&0x3F];
				if(b==MAX_B64_SIZE) {Base64EncodedResult[b-1]='\0';return(1);}
				Base64EncodedResult[b++]=Base64Table[((unsigned int)((current&0x3F)))&0x3F];
				mode=0;
			break;
		}
	}
	if(b==MAX_B64_SIZE) {Base64EncodedResult[b-1]='\0';return(1);}
	Base64EncodedResult[b]='\0';
return(0);
}


int SetNightMode(int Mode,char *user,char *password)
{
	if((user==NULL)||(password==NULL)) return(1);
	if((user[0]=='\0')||(password[0]=='\0')) return(1);
	sprintf(PayloadBuffer,"%s:%s\0",user,password);
	if((Base64Encode(PayloadBuffer))) return(1);		// overflow

	InitConnection(CameraPort);
	if(ConnectionStatus!=CONNSTAT_NONE)
	{
		if(Mode==0)
			sprintf(TmpBuff,GO_NIGHT_VISION,FLICKER_HERTZ,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],Base64EncodedResult);
		else
			sprintf(TmpBuff,GO_DAY_VISION,FLICKER_HERTZ,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],Base64EncodedResult);
		SendThisString(TmpBuff);
		sleep(1);
	}
	if(ClientSocketID>0)
	{
		shutdown(ClientSocketID,SHUT_RDWR);
		close(ClientSocketID);
	}
}

void CheckMessage(void)
{
int	a=0,b;

	if(ConnectionStatus==CONNSTAT_NONE)
	{
		AllSize=0;
		PayloadBufferPointer=0;
		InitConnection(CameraPort);
		if(ConnectionStatus!=CONNSTAT_NONE)
		{
			sprintf(TmpBuff,RTSPINIT_1,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],CameraPort);
			SendThisString(TmpBuff);
			TimeOutIfReached=time(NULL)+5;
		}
		else
			sleep(1);
		return;
	}

	if((a=read(ClientSocketID,&ReceiveBuffer[RB_Ptr],RECEIVE_BUFFER_SIZE-RB_Ptr))>0)
	{
		RB_Ptr+=a;
		ReceiveBuffer[RB_Ptr]='\0';

#if 0
for(b=0;b<RB_Ptr;b++)
{
	if(ReceiveBuffer[b]>=' ') printf("%c",ReceiveBuffer[b]); else printf("%c[%02x]",ReceiveBuffer[b],ReceiveBuffer[b]);fflush(stdout);
}
#endif

		if(ConnectionStatus==CONNSTAT_INIT_GET)
		{
			if((PayloadBufferPointer+RB_Ptr)<MAX_PAYLOAD_BUFFER)
			{
				memcpy(&PayloadBuffer[PayloadBufferPointer],ReceiveBuffer,RB_Ptr);
				PayloadBufferPointer+=RB_Ptr;
				RB_Ptr=0;
			}
			else
				ConnectionStatus=CONNSTAT_DONE;

#if DEBUGCHU
printf("\nPayloadBufferPointer=%d  [%d]",PayloadBufferPointer,ConnectionStatus);fflush(stdout);
#endif

			if(ConnectionStatus==CONNSTAT_DONE)		// done reading picture
			{
				sprintf(TmpBuff,RTSPINIT_TDOWN,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],CameraPort,SessionID);
				SendThisString(TmpBuff);
				if(EvaluateReceivedPayload())
					ConnectionStatus=CONNSTAT_PROBLEM;
				else
					ConnectionStatus=CONNSTAT_SUCCESS;
				usleep(10000);
				if(ClientSocketID>0)
				{
					shutdown(ClientSocketID,SHUT_RDWR);
					close(ClientSocketID);
					ClientSocketID=-1;
				}
			}
		}
		else
		{
			for(a=0;a<(RB_Ptr-1);a++)
			{
				if((ReceiveBuffer[a]=='\r')&&(ReceiveBuffer[a+1]=='\n')&&(ReceiveBuffer[a+2]=='\0'))	// full response
				{
					switch(ConnectionStatus)
					{
						case CONNSTAT_INIT_1 :
						case CONNSTAT_INIT_2 :
						case CONNSTAT_INIT_3 :
							EvaluateReceiveBuffer();
							a=RB_Ptr=0;
						break;
					}
				}
			}
		}

	}

	if(ConnectionStatus!=CONNSTAT_SUCCESS)
	{
		if((TimeOutIfReached<=time(NULL))||(ConnectionStatus==CONNSTAT_PROBLEM))	// Timed out... restart
		{
			if(ConnectionStatus==CONNSTAT_INIT_GET)			// It was still reading...
			{
				sprintf(TmpBuff,RTSPINIT_TDOWN,ConnAddr[0],ConnAddr[1],ConnAddr[2],ConnAddr[3],CameraPort,SessionID);
				SendThisString(TmpBuff);
				if(EvaluateReceivedPayload())
					ConnectionStatus=CONNSTAT_PROBLEM;
				else
					ConnectionStatus=CONNSTAT_SUCCESS;
			}

			if(ClientSocketID>0)
			{
				shutdown(ClientSocketID,SHUT_RDWR);
				close(ClientSocketID);
				ClientSocketID=-1;
			}
			if(ConnectionStatus!=CONNSTAT_SUCCESS)
			{
				ConnectionStatus=CONNSTAT_NONE;
				printf("\nRESTART");fflush(stdout);
			}
		}
	}

#if DEBUGCHU
//printf("\nEOF CHKMSG [%d:%d]",ConnectionStatus,RB_Ptr);
#endif
}

void IntSigCatch(int sig)
{
int	a,b;

	switch(sig)
	{
		case SIGQUIT:
		case SIGTERM:
		case SIGINT:
			if(ClientSocketID>0)
			{
				shutdown(ClientSocketID,SHUT_RDWR);
				close(ClientSocketID);
			}
#if DEBUGCHU
			printf("\nClosing socket %d",ClientSocketID);fflush(stdout);
#endif
			exit(0);
		break;
		case SIGPIPE :
			brokenpipe=1;
#if DEBUGCHU
			printf("\nBROKEN PIPE");fflush(stdout);
#endif

			signal(SIGPIPE,IntSigCatch);
		break;
	}
}

void SaveFile(char *filename)
{
FILE	*fp;
int	a,b,c;

	if((fp=fopen(filename,"wb"))!=NULL)
	{
		a=PictureBufferPointer;
		b=0;
		while(a>0)
		{
			c=fwrite(&PictureBuffer[b],sizeof(char),a,fp);
			a-=c;
			b+=c;
		}
		fflush(fp);
		fclose(fp);
	}
}

int main(int argc,char** argv)
{
int	a,b,c,d,e,mode;

	if(argc<2)
	{
		printf("\n\nUsage :\t%s IP.IP.IP.IP[:port] filename.jpg\t\t// Port is 554 if not specified, filename is 'saved.jpg' if not specified",argv[0]);
		printf("\nor\t%s IP.IP.IP.IP -nightmode or -daymode username password\n\n",argv[0]);fflush(stdout);
		return(0);
	}

	PictureBufferPointer=0;
	mode=0;				// standard picture aquisition
	if(argv[2]!=NULL)
	{
		if((argv[2][0]=='-')&&(argv[2][1]=='n')) mode=1;
		else if((argv[2][0]=='-')&&(argv[2][1]=='d')) mode=2;
		if(mode>0)
		{
			if((argv[3]==NULL)||(argv[4]==NULL))
			{
				printf("\n\nUsage :\t%s IP.IP.IP.IP[:port] filename.jpg\t\t// Port is 554 if not specified, filename is 'saved.jpg' if not specified",argv[0]);
				printf("\nor\t%s IP.IP.IP.IP -nightmode or -daymode username password\n\n",argv[0]);fflush(stdout);
				return(0);
			}
		}
	}

	a=-1;b=-1;c=-1;d=-1;e=-1;
	sscanf(argv[1],"%d.%d.%d.%d:%d",&a,&b,&c,&d,&e);
	if((a==-1)||(b==-1)||(c==-1)||(d==-1))
	{
		printf("\n\nUsage :\t%s IP.IP.IP.IP[:port] filename.jpg\t\t// Port is 554 if not specified, filename is 'saved.jpg' if not specified",argv[0]);
		printf("\nor\t%s IP.IP.IP.IP -nightmode or -daymode username password\n\n",argv[0]);fflush(stdout);
		return(0);
	}
	if(e==-1)
	{
		if(mode==0)
			CameraPort=554;
		else
			CameraPort=80;
	}
	else
		CameraPort=e;
	ConnAddr[0]=(unsigned int)a;
	ConnAddr[1]=(unsigned int)b;
	ConnAddr[2]=(unsigned int)c;
	ConnAddr[3]=(unsigned int)d;

	signal(SIGQUIT,IntSigCatch);
	signal(SIGTERM,IntSigCatch);
	signal(SIGINT,IntSigCatch);
	signal(SIGPIPE,IntSigCatch);

	if(mode>0)
	{
		return(SetNightMode((mode-1),argv[3],argv[4]));
	}
	else
	{
		while(ConnectionStatus!=CONNSTAT_SUCCESS)
		{
			CheckMessage();
			usleep(5000);
		}
		if(PictureBufferPointer>0)
		{
			if(argv[2]!=NULL)
				SaveFile(argv[2]);
			else
				SaveFile("saved.jpg");
		}
	}

return(1);
}

