./ MultiCS.r82 / clustredcache.c
void stringtohtml(char *src, char *dest)
{
	while ( *src )
	{
		if ( *src=='<' ) {
			*dest = '&'; dest++;
			*dest = 'l'; dest++;
			*dest = 't'; dest++;
			*dest = ';'; dest++;
		}
		else if ( *src=='>' ) {
			*dest = '&'; dest++;
			*dest = 'g'; dest++;
			*dest = 't'; dest++;
			*dest = ';'; dest++;
		}
		else if ( *src=='&' ) {
			*dest = '&'; dest++;
			*dest = 'a'; dest++;
			*dest = 'm'; dest++;
			*dest = 'p'; dest++;
			*dest = ';'; dest++;
		}
		else if ( *src=='"' ) {
			*dest = '&'; dest++;
			*dest = 'q'; dest++;
			*dest = 'u'; dest++;
			*dest = 'o'; dest++;
			*dest = 't'; dest++;
			*dest = ';'; dest++;
		}
		else {
			*dest = *src; dest++;
		}
		src++;
	}
	*dest = 0;
}

/*
void fpeer_update(struct cacheserver_data *cache)
{
	memset( cache->fpeer, 0, sizeof(cache->fpeer) ); // NULL
	struct cachepeer_data *peer = cache->peer;
	while (peer) {
		if ( peer->host->ip ) {
			int index = peer->host->ip & 0xFF;
			peer->fnext = cache->fpeer[index];
			cache->fpeer[index] = peer;
		}
		peer = peer->next;
	}
}


//// Connected Peers
void ipeer_update(struct cacheserver_data *cache)
{
	cache->peerReq = NULL;
	cache->peerRep = NULL;
	struct cachepeer_data *peer = cache->peer;
	while (peer) {
		if (peer->ping>0) {
			if (peer->flags&FLAG_CACHE_SENDREQ) {
				peer->nextreq = cache->peerReq;
				cache->peerReq = peer;
			}
			if (peer->flags&FLAG_CACHE_SENDREP) {
				peer->nextrep = cache->peerRep;
				cache->peerRep = peer;
			}
		}
		peer = peer->next;
	}
}
*/

// 0: removed
int peer_doublecheck(struct cacheserver_data *cache, struct cachepeer_data *xpeer)
{
	struct cachepeer_data *peer = cache->peer;
	struct cachepeer_data *previous = NULL;
	while (peer) {
		if (peer!=xpeer)
		if (peer->port==xpeer->port)
		if (peer->host->ip==xpeer->host->ip) {
			if (peer->runtime) {
				if (previous) previous->next = peer->next; else cache->peer = peer->next;
				//close(peer->outsock);
				free( peer );
				return 0;				
			}
			else if (xpeer->runtime) peer_doublecheck(cache, peer);
			return 1;
		}
		previous = peer;
		peer = peer->next;
	}
	return 1;
}



///////////////////////////////////////////////////////////////////////////////
//
///////////////////////////////////////////////////////////////////////////////

#define PEER_CSP            0x010000
#define PEER_CCCAM_CLIENT   0x020000
#define PEER_CAMD35_CLIENT	0x040000
#define PEER_CS378X_CLIENT	0x080000
#define PEER_CACHEEX_SERVER	0x100000

struct cachepeer_data *getpeerbyaddr(struct cacheserver_data *cache, uint32_t ip, uint16_t port)
{
	struct cachepeer_data *peer = cache->peer;
	while(peer) {
		if ( (peer->host->ip==ip)&&(peer->recvport==port) ) return peer;
		peer = peer->next;
	}
	return NULL;
}

struct cachepeer_data *getpeerbyid(int id)
{
	struct cacheserver_data *cache = cfg.cache.server;
	while (cache) {
		struct cachepeer_data *peer = cache->peer;
		while(peer) {
			if (peer->id==id) return peer;
			peer = peer->next;
		}
		cache = cache->next;
	}
	return NULL;
}

///////////////////////////////////////////////////////////////////////////////
// Multics ID encryption
///////////////////////////////////////////////////////////////////////////////

static unsigned char T1[]={
  0x2a,0xe1,0x0b,0x13,0x3e,0x6e,0x32,0x48,
  0xd3,0x31,0x08,0x8c,0x8f,0x95,0xbd,0xd0,
  0xe4,0x6d,0x50,0x81,0x20,0x30,0xbb,0x75,
  0xf5,0xd4,0x7c,0x87,0x2c,0x4e,0xe8,0xf4,
  0xbe,0x24,0x9e,0x4d,0x80,0x37,0xd2,0x5f,
  0xdb,0x04,0x7a,0x3f,0x14,0x72,0x67,0x2d,
  0xcd,0x15,0xa6,0x4c,0x2e,0x3b,0x0c,0x41,
  0x62,0xfa,0xee,0x83,0x1e,0xa2,0x01,0x0e,
  0x7f,0x59,0xc9,0xb9,0xc4,0x9d,0x9b,0x1b,
  0x9c,0xca,0xaf,0x3c,0x73,0x1a,0x65,0xb1,
  0x76,0x84,0x39,0x98,0xe9,0x53,0x94,0xba,
  0x1d,0x29,0xcf,0xb4,0x0d,0x05,0x7d,0xd1,
  0xd7,0x0a,0xa0,0x5c,0x91,0x71,0x92,0x88,
  0xab,0x93,0x11,0x8a,0xd6,0x5a,0x77,0xb5,
  0xc3,0x19,0xc1,0xc7,0x8e,0xf9,0xec,0x35,
  0x4b,0xcc,0xd9,0x4a,0x18,0x23,0x9f,0x52,
  0xdd,0xe3,0xad,0x7b,0x47,0x97,0x60,0x10,
  0x43,0xef,0x07,0xa5,0x49,0xc6,0xb3,0x55,
  0x28,0x51,0x5d,0x64,0x66,0xfc,0x44,0x42,
  0xbc,0x26,0x09,0x74,0x6f,0xf7,0x6b,0x4f,
  0x2f,0xf0,0xea,0xb8,0xae,0xf3,0x63,0x6a,
  0x56,0xb2,0x02,0xd8,0x34,0xa4,0x00,0xe6,
  0x58,0xeb,0xa3,0x82,0x85,0x45,0xe0,0x89,
  0x7e,0xfd,0xf2,0x3a,0x36,0x57,0xff,0x06,
  0x69,0x54,0x79,0x9a,0xb6,0x6c,0xdc,0x8b,
  0xa7,0x1f,0x90,0x03,0x17,0x1c,0xed,0xd5,
  0xaa,0x5e,0xfe,0xda,0x78,0xb0,0xbf,0x12,
  0xa8,0x22,0x21,0x3d,0xc2,0xc0,0xb7,0xa9,
  0xe7,0x33,0xfb,0xf1,0x70,0xe5,0x17,0x96,
  0xf8,0x8d,0x46,0xa1,0x86,0xe2,0x40,0x38,
  0xf6,0x68,0x25,0x16,0xac,0x61,0x27,0xcb,
  0x5b,0xc8,0x2b,0x0f,0x99,0xde,0xce,0xc5
};

static unsigned char T2[]={
  0xbf,0x11,0x6d,0xfa,0x26,0x7f,0xf3,0xc8,
  0x9e,0xdd,0x3f,0x16,0x97,0xbd,0x08,0x80,
  0x51,0x42,0x93,0x49,0x5b,0x64,0x9b,0x25,
  0xf5,0x0f,0x24,0x34,0x44,0xb8,0xee,0x2e,
  0xda,0x8f,0x31,0xcc,0xc0,0x5e,0x8a,0x61,
  0xa1,0x63,0xc7,0xb2,0x58,0x09,0x4d,0x46,
  0x81,0x82,0x68,0x4b,0xf6,0xbc,0x9d,0x03,
  0xac,0x91,0xe8,0x3d,0x94,0x37,0xa0,0xbb,
  0xce,0xeb,0x98,0xd8,0x38,0x56,0xe9,0x6b,
  0x28,0xfd,0x84,0xc6,0xcd,0x5f,0x6e,0xb6,
  0x32,0xf7,0x0e,0xf1,0xf8,0x54,0xc1,0x53,
  0xf0,0xa7,0x95,0x7b,0x19,0x21,0x23,0x7d,
  0xe1,0xa9,0x75,0x3e,0xd6,0xed,0x8e,0x6f,
  0xdb,0xb7,0x07,0x41,0x05,0x77,0xb4,0x2d,
  0x45,0xdf,0x29,0x22,0x43,0x89,0x83,0xfc,
  0xd5,0xa4,0x88,0xd1,0xf4,0x55,0x4f,0x78,
  0x62,0x1e,0x1d,0xb9,0xe0,0x2f,0x01,0x13,
  0x15,0xe6,0x17,0x6a,0x8d,0x0c,0x96,0x7e,
  0x86,0x27,0xa6,0x0d,0xb5,0x73,0x71,0xaa,
  0x36,0xd0,0x06,0x66,0xdc,0xb1,0x2a,0x5a,
  0x72,0xbe,0x3a,0xc5,0x40,0x65,0x1b,0x02,
  0x10,0x9f,0x3b,0xf9,0x2b,0x18,0x5c,0xd7,
  0x12,0x47,0xef,0x1a,0x87,0xd2,0xc2,0x8b,
  0x99,0x9c,0xd3,0x57,0xe4,0x76,0x67,0xca,
  0x3c,0xfb,0x90,0x20,0x14,0x48,0xc9,0x60,
  0xb0,0x70,0x4e,0xa2,0xad,0x35,0xea,0xc4,
  0x74,0xcb,0x39,0xde,0xe7,0xd4,0xa3,0xa5,
  0x04,0x92,0x8c,0xd9,0x7c,0x1c,0x7a,0xa8,
  0x52,0x79,0xf2,0x33,0xba,0x1f,0x30,0x9a,
  0x00,0x50,0x4c,0xff,0xe5,0xcf,0x59,0xc3,
  0xe3,0x0a,0x85,0xb3,0xae,0xec,0x0b,0xfe,
  0xe2,0xab,0x4a,0xaf,0x69,0x6c,0x2c,0x5d
};

#define SN(b) (((b&0xf0)>>4)+((b&0xf)<<4))

static void fase(unsigned char *k,unsigned char *D)
{
	unsigned char l,dt; // paso 1 

	for(l=0;l<4;++l) D[l]^=k[l];  // paso 2 

	for(l=0;l<4;++l) D[l]=T1[D[l]];

	for(l=6;l>3;--l) { 
		D[(l+2)&3]^=D[(l+1)&3]; 
		dt=(SN(D[(l+1)&3])+D[l&3])&0xff; 
		D[l&3]=T2[dt];
	} 
	for(l=3;l>0;--l) {
		D[(l+2)&3]^=D[(l+1)&3]; 
		D[l&3]=T1[(SN(D[(l+1)&3])+D[l&3])&0xff]; 
	} 
	D[2]^=D[1]; 
	D[1]^=D[0]; 
}


// Packet Encryption

void encryptcache(uint8_t *buf, int len)
{
	int i;
	for (i=1; i<len; i++) buf[i] = (buf[i]+i) & 0xff;
}

void decryptcache(uint8_t *buf, int len)
{
	int i;
	for (i=1; i<len; i++) buf[i] = (0xff00+buf[i]-i) & 0xff;
}

///////////////////////////////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////////////////////////////

#define CACHE_SENT_NONE     0
#define CACHE_SENT_REQUEST  1
#define CACHE_SENT_REPLY    2

#define BIT_CACHE_NEWPROTO  0x01
#define BIT_CACHE_HACK      0x10
#define BIT_CACHE_REQPING   0x80

//	uint8_t status; // 0:Wait; 1: dcw received
#define CACHE_FLAG_DCW        0x01
//	int sendpipe; // flag send dcw to ecmpipe
#define CACHE_FLAG_SENDPIPE   0x02
// Request Sent
#define CACHE_FLAG_REQSENT    0x04
// Reply Sent
#define CACHE_FLAG_REPSENT    0x08
// Forward cache to peers
#define CACHE_FLAG_FWD        0x10
// Cacheex Reply Sent
#define CACHEEX_FLAG_REPSENT  0x20


typedef enum { NO_CYCLE=0, CW0CYCLE=1, CW1CYCLE=2} cwcycle_t;

#define DCW_ERROR    0x01
#define DCW_CYCLE    0x02
#define DCW_CHECKED  0x04
#define DCW_SKIP     0x08
#define DCW_SENT     0x10

struct cw_cache_data {
	struct cw_cache_data *next;
	uint8_t cw[16]; // for storing all codes
	uint32_t cwsum; // Checksum
	uint8_t status;
	cwcycle_t cwcycle; // cw1='1' / cw0='0'
	uint32_t peerid; // fisrt peerid 
	uint16_t nbpeers; // number of peers reporting this dcw
};


struct __attribute__ ((__packed__)) cache_data {
	struct cache_data *next; // main list
	struct cache_data *prev; // previous

	uint8_t flags;
	uint32_t recvtime;
	// CSP
	uint8_t tag;
	uint16_t sid;
	uint16_t onid;
	uint16_t caid;
	uint32_t hash; // Non-NULL
	//
	uint32_t provid;
	cwcycle_t cwcycle; // ecmtag when cw1 cycle
	uint8_t prevcw[16];
#ifdef CACHEEX
	uint8_t ecmd5[16];
#endif

	struct cw_cache_data *cwdata;

	ECM_DATA *ecm;
};

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

int cache_check( struct cache_data *req )
{
	if ( ((req->tag&0xFE)!=0x80)||!req->caid||!req->hash||!req->sid ) return 0;
	if (cfg.cache.caids[0]) {
		int i;
		for(i=0; i<32; i++) {
			if (!cfg.cache.caids[i]) break;
			if (cfg.cache.caids[i]==req->caid) return 1;
		}
		return 0;
	}
	//if (!cfg.cache.faccept0onid && !req->onid ) return 0;
	return 1;
}

int cache_check_request( unsigned char tag, unsigned short sid, unsigned short onid, unsigned short caid, unsigned int hash )
{
	if ( ((tag&0xFE)!=0x80)||!caid||!hash ) return 0;
	//if (!cfg.cache.faccept0onid && !onid ) return 0;
	return 1;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#define MAX_CACHE_INDEX  0x3FF


////typedef struct cache_data t_cachetab[MAX_CACHE_INDEX+1];


struct cache_list {
	struct cache_list *next;
	//struct cache_data *cachetab[MAX_CACHE_INDEX+1];
	unsigned short caid;
	struct cache_data **cachetab;
};


struct cache_list *cachelist;

struct cache_data **getcachetabbycaid(unsigned short caid)
{
	// look for availabe tab
	struct cache_list *x = cachelist;
	while (x) {
		if (x->caid==caid) {
			return x->cachetab;
		}
		x = x->next;
	}
	// if not found add new one
	struct cache_list *newdata = malloc( sizeof(struct cache_list) );
	newdata->next = cachelist;
	cachelist = newdata;
	newdata->caid = caid;
	newdata->cachetab = malloc( sizeof(struct cache_data) * (MAX_CACHE_INDEX+1) );
	memset( newdata->cachetab, 0, sizeof(struct cache_data) * (MAX_CACHE_INDEX+1) );
	//printf(" New cache list for caid = %02X\n", caid);
	return 	cachelist->cachetab;
}


struct cache_data *cache_new( struct cache_data *newdata )
{
	int index = newdata->sid&MAX_CACHE_INDEX;
	struct cache_data **cachetab = getcachetabbycaid(newdata->caid);
	struct cache_data *pcache = cachetab[index];
	struct cache_data *new;
	uint32_t ticks = GetTickCount();
	// add new or use dead data
	if (!pcache) {
		// nothing so add new
		//debugf(0," first data for index = %02X\n", index);
		new = malloc( sizeof(struct cache_data) );
		memset( new, 0,  sizeof(struct cache_data) );
		new->next = NULL;
		new->prev = NULL;
		cachetab[index] = new; // it becomes the current one
	}
	else if (!pcache->next) {
		//debugf(0," second data for index = %02X\n", index);
		new = malloc( sizeof(struct cache_data) );
		memset( new, 0,  sizeof(struct cache_data) );
		// add new
		new->prev = cachetab[index];
		new->next = cachetab[index];
		// update previous
		cachetab[index]->next = new;
		// update next
		cachetab[index]->prev = new;
		//
		cachetab[index] = new; // it becomes the current one
	}
	else {
		new = cachetab[index]->prev;
		if ( (new->recvtime+cfg.cache.alivetime) < ticks ) {
			// cache is dead, add data to this one without allocating new data
			// free old dcw
			struct cw_cache_data *cwdata = new->cwdata;
			while (cwdata) {
				struct cw_cache_data *tmp = cwdata;
				cwdata = cwdata->next;
				free( tmp );
			}
			//debugf(0," reuse data for index = %02X\n", index);
			pcache = new->prev; // store previous
			memset( new, 0,  sizeof(struct cache_data) );
			new->prev = pcache;
			new->next = cachetab[index];
			cachetab[index] = new; // it becomes the current one
		}
		else { // allocate new data
			//debugf(getdbgflag(DBG_CACHE,0,0)," new data for index = %02X\n", index);
			new = malloc( sizeof(struct cache_data) );
			memset( new, 0, sizeof(struct cache_data) );
			// add new in list
			new->prev = cachetab[index]->prev;
			new->next = cachetab[index];
			// update previous
			cachetab[index]->prev->next = new;
			// update next
			cachetab[index]->prev = new;
			//
			cachetab[index] = new; // it becomes the current one			
		}
	}
	pcache = cachetab[index];
	//pcache->status = CACHE_STAT_WAIT; // 0:Wait; 1: dcw received
	pcache->recvtime = ticks;
	pcache->tag = newdata->tag;
	pcache->sid = newdata->sid;
	pcache->onid = newdata->onid;
	pcache->caid = newdata->caid;
	pcache->hash = newdata->hash;
	pcache->provid = newdata->provid;
#ifdef CACHEEX
	memcpy( pcache->ecmd5, newdata->ecmd5, 16);
#endif
	return pcache;
}

struct cache_data *cache_fetch( struct cache_data *thereq )
{
	int index = thereq->sid&MAX_CACHE_INDEX;
	struct cache_data **cachetab = getcachetabbycaid(thereq->caid);
	struct cache_data *pcache = cachetab[index];
	uint32_t ticks = GetTickCount();
	while (pcache) {
		if ( (pcache->recvtime+cfg.cache.alivetime) < ticks ) return NULL;
		if ( (pcache->hash==thereq->hash)&&(pcache->sid==thereq->sid) )
			if ( (pcache->tag==thereq->tag) || !pcache->tag || !thereq->tag ) return pcache;
		pcache = pcache->next;
		if (pcache==cachetab[index]) break;
	}
	return NULL;
}


char *cwcycle2str( cwcycle_t cwcycle )
{
	if (cwcycle==CW0CYCLE) return "CW0CYCLE";
	if (cwcycle==CW1CYCLE) return "CW1CYCLE";
	else return "NO_CYCLE";
}


/*
int checkcycle( uint8_t cw1cycle, uint8_t ecmtag, uint8_t cwcycle ) 
{
	if (cfg.cache.filter) {
		// Check Cycle
		if (cw1cycle==0x80) { // cw1 cycle on tag=0x80
			if ( (ecmtag==0x80)&&(cwcycle==0) ) return 0;
			if ( (ecmtag==0x81)&&(cwcycle==1) ) return 0;
		}
		else if (cw1cycle==0x81) { // cw1 cycle on tag=0x81
			if ( (ecmtag==0x81)&&(cwcycle==0) ) return 0;
			if ( (ecmtag==0x80)&&(cwcycle==1) ) return 0;
		}
	}
	return 1;
}
*/









///////////////////////////////////////////////////////////////////////////////
// CACHE ---> ECM
///////////////////////////////////////////////////////////////////////////////

int get_cache2ecm( uint8_t *buf, struct cache_data *pcache, uint8_t *cw )
{
	pcache->tag = buf[1];
	pcache->sid = (buf[2]<<8) | buf[3];
	pcache->onid = (buf[4]<<8) | buf[5];
	pcache->caid = (buf[6]<<8) | buf[7];
	pcache->hash = (buf[8]<<24) | (buf[9]<<16) | (buf[10]<<8) | (buf[11]);
//	memcpy( &(pcache->tag), buf+1, 11);
	int index = 12;
	memcpy( &(pcache->ecm), buf+index, sizeof(void*) );
	//debugf(0, " get_cache2ecm %p\n", pcache->ecm);
	index += sizeof(void*);
	if (cw) {
		int peerid = (buf[index]<<24) | (buf[index+1]<<16) | (buf[index+2]<<8) | (buf[index+3]);
		index+=4;
		memcpy( cw, buf+index, 16);
		return peerid;
	}
	return 0;
}

int put_cache2ecm(uint8_t type, uint8_t *buf, struct cache_data *pcache, uint8_t *cw, int peerid )
{
	buf[0] = type;
	buf[1] = pcache->tag;
	buf[2] = pcache->sid>>8; buf[3] = pcache->sid&0xff;
	buf[4] = pcache->onid>>8; buf[5] = pcache->onid&0xff;
	buf[6] = pcache->caid>>8; buf[7] = pcache->caid&0xff;
	buf[8] = pcache->hash>>24; buf[9] = pcache->hash>>16; buf[10] = pcache->hash>>8; buf[11] = pcache->hash & 0xff;
//	memcpy( buf+1, &(pcache->tag), 11);
	int index = 12;
	memcpy( buf+index, &pcache->ecm, sizeof(void*) );
	//debugf(0, " put_cache2ecm %p\n", pcache->ecm);
	index += sizeof(void*);
	if (cw) {
		buf[index] = peerid>>24; buf[index+1] = peerid>>16; buf[index+2] = peerid>>8; buf[index+3] = peerid & 0xff;
		index+=4;
		memcpy( buf+index, cw, 16);
		index+=16;
	}
	return index;
}

void pipe_cache2ecm_find_failed(struct cache_data *pcache)
{
	uint8_t buf[48];
	int len = put_cache2ecm(PIPE_CACHE_FIND_FAILED, buf, pcache, NULL, 0);
	pipe_send( prg.pipe.ecm[1], buf, len);
}

void pipe_cache2ecm_find_success(struct cache_data *pcache, uint8_t *cw, int peerid )
{
	uint8_t buf[48];
	int len = put_cache2ecm(PIPE_CACHE_FIND_SUCCESS, buf, pcache, cw, peerid);
	pipe_send( prg.pipe.ecm[1], buf, len);
}

///////////////////////////////////////////////////////////////////////////////
// ECM ---> CACHE
///////////////////////////////////////////////////////////////////////////////

int get_ecm2cache(uint8_t *buf , struct cache_data *pcache, uint8_t *cw)
{
	pcache->tag = buf[1];
	pcache->sid = (buf[2]<<8) | buf[3];
	pcache->onid = (buf[4]<<8) | buf[5];
	pcache->caid = (buf[6]<<8) | buf[7];
	pcache->hash = (buf[8]<<24) | (buf[9]<<16) | (buf[10]<<8) | (buf[11]);
	pcache->provid = (buf[12]<<16) | (buf[13]<<8) | (buf[14]);
	pcache->cwcycle = buf[15];
	int index = 16;
	//
	memcpy( &pcache->ecm, buf+index, sizeof(void*) );
	index += sizeof(void*);
#ifdef CACHEEX
	memcpy( pcache->ecmd5, buf+index, 16 );
	index+=16;
#endif
	if (cw) {
		memcpy( cw, buf+index, 16 );
		index+=16;
	}
	return index;
}

int put_ecm2cache(uint8_t type, uint8_t *buf , ECM_DATA *ecm, uint16_t onid, uint8_t *cw )
{
	buf[0] = type;
	buf[1] = ecm->ecm[0];
	buf[2] = ecm->sid>>8; buf[3] = ecm->sid;
	buf[4] = onid>>8; buf[5] = onid;
	buf[6] = ecm->caid>>8; buf[7] = ecm->caid;
	buf[8] = ecm->hash>>24; buf[9] = ecm->hash>>16; buf[10] = ecm->hash>>8; buf[11] = ecm->hash;
	buf[12] = ecm->provid>>16; buf[13] = ecm->provid>>8; buf[14] = ecm->provid;
	// cwcycle
	if (!ecm->cw1cycle) buf[15] = NO_CYCLE;
	else if (ecm->ecm[0]==ecm->cw1cycle) buf[15] = CW1CYCLE; else buf[15] = CW0CYCLE;
	//
	int index = 16;
	memcpy( buf+index, &ecm, sizeof(void*) );
	index += sizeof(void*);
#ifdef CACHEEX
	memcpy( buf+index, ecm->ecmd5, 16 );
	index+=16;
#endif
	if (cw) { // previous cw if find/request, cw for reply
		memcpy( buf+index, cw, 16 );
		index+=16;
	}
	return index;
}

/*
#ifdef THREAD_DCW
void pipe_cache_find( ECM_DATA *ecm, struct cardserver_data *cs)
{
	struct cache_data req;
	req.tag = ecm->ecm[0];
	req.sid = ecm->sid;
	req.onid = cs->option.onid;
	req.caid = ecm->caid;
	req.hash = ecm->hash;
	req.provid = ecm->provid;
	req.cw1cycle = ecm->cw1cycle;


	pthread_mutex_lock( &prg.lockcache );

	struct cache_data *pcache = cache_fetch( &req );
	if (pcache==NULL) {
		pcache = cache_new( &req );
		pcache->ecm = ecm;
		pcache->cw1cycle = req.cw1cycle;
#ifdef CACHEEX
		memcpy( pcache->ecmd5, ecm->ecmd5, 16 );
#endif
		pcache->flags |= CACHE_FLAG_SENDPIPE;
		// XXX set find failed
		ecm->dcwstatus = STAT_DCW_WAIT;
		ecm->checktime = 1;
	}
	else {
		if (!cs->option.cachetimeout) {
			ecm->dcwstatus = STAT_DCW_WAIT;
			ecm->checktime = 1;
		}
		pcache->ecm = ecm;
		pcache->tag = req.tag; // set tag if not set (coming from cahceex)
		pcache->provid = req.provid; // set provid if not set (coming from csp)
		pcache->cw1cycle = req.cw1cycle;
		pcache->provid = req.provid;
#ifdef CACHEEX
		memcpy( pcache->ecmd5, ecm->ecmd5, 16 );
#endif
		pcache->flags |= CACHE_FLAG_SENDPIPE;
		// Check stored cw
		if (pcache->icwlist) {
			int i;
			for(i=0; i<pcache->icwlist; i++) {
				if ( pcache->cwlist[i].status ) {
					if ( checkcycle( pcache->cw1cycle, pcache->tag, pcache->cwlist[i].cwcycle ) ) {
						ecm_setdcw( ecm, pcache->cwlist[i].cw, DCW_SOURCE_CACHE, pcache->cwlist[i].peerid );
					}
				}
			}
		}
	}

	pthread_mutex_unlock( &prg.lockcache );
}
*/

void pipe_cache_find( ECM_DATA *ecm, struct cardserver_data *cs)
{
	uint8_t buf[64];
	uint8_t *cw = NULL;
	if ( ecm->lastdecode.ecm && (ecm->lastdecode.counter>1) ) cw = ecm->lastdecode.dcw;
	int len = put_ecm2cache(PIPE_CACHE_FIND, buf, ecm, cs->option.onid, cw);
	pipe_send( prg.pipe.cache[1], buf, len);
}

void pipe_cache_request( ECM_DATA *ecm, struct cardserver_data *cs)
{
	uint8_t buf[64];
	int len = put_ecm2cache(PIPE_CACHE_REQUEST, buf, ecm, cs->option.onid, NULL);
	pipe_send( prg.pipe.cache[1], buf, len);
}

void pipe_cache_reply( ECM_DATA *ecm, struct cardserver_data *cs)
{
	uint8_t buf[64];
	int len;
	if (ecm->dcwstatus==STAT_DCW_SUCCESS)
		len = put_ecm2cache(PIPE_CACHE_REPLY, buf, ecm, cs->option.onid, ecm->cw);
	else
		len = put_ecm2cache(PIPE_CACHE_REPLY, buf, ecm, cs->option.onid, NULL);
	pipe_send( prg.pipe.cache[1], buf, len);
}

void pipe_cache_resendreq(ECM_DATA *ecm, struct cardserver_data *cs)
{
	uint8_t buf[64];
	int len;
	len = put_ecm2cache(PIPE_CACHE_RESENDREQ, buf, ecm, cs->option.onid, NULL);
	pipe_send( prg.pipe.cache[1], buf, len);
	debugf( getdbgflag(DBG_NEWCAMD,cs->id,0), " [%s] CACHE RESENDREQ ch %04x:%06x:%04x\n", cs->name,ecm->caid,ecm->provid,ecm->sid);
}

///////////////////////////////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////////////////////////////


void sendtoip( int handle, uint32_t ip, int port, unsigned char *buf, int len)
{
	if (ip && port) {
		struct sockaddr_in si_other;
		int slen=sizeof(si_other);
		memset((char *) &si_other, 0, sizeof(si_other));
		si_other.sin_family = AF_INET;
		si_other.sin_port = htons( port );
		si_other.sin_addr.s_addr = ip;
		sendto( handle, buf, len, 0, (struct sockaddr *)&si_other, slen );
	}
}

void sendtopeer( struct cachepeer_data *peer, unsigned char *buf, int len)
{

	if (peer->host->ip && peer->port) {
		struct sockaddr_in si_other;
		int slen=sizeof(si_other);
		memset((char *) &si_other, 0, sizeof(si_other));
		si_other.sin_family = AF_INET;
		si_other.sin_port = htons( peer->port );
		si_other.sin_addr.s_addr = peer->host->ip;
#ifdef DEBUG_NETWORK2
		if (flag_debugnet) {
			debugf(getdbgflag(DBG_CACHE,0,0)," cache: send data (%d) to peer (%s:%d)\n", len, peer->host->name,peer->port);
			debughex(buf,len);
		}
#endif

		sendto(peer->outsock, buf, len, 0, (struct sockaddr *)&si_other, slen);
/*
		while (1) {
			struct pollfd pfd;
			pfd.fd = peer->outsock;
			pfd.events = POLLOUT;
			int retval = poll(&pfd, 1, 10);
			if (retval>0) {
				if ( pfd.revents & (POLLOUT) ) {
					sendto(peer->outsock, buf, len, 0, (struct sockaddr *)&si_other, slen);
				} else debugf(getdbgflag(DBG_CACHE,0,0)," cache: error sending data to peer (%s:%d) \n", peer->host->name,peer->port);
				break;
			}
			else if (retval<0) {
				if ( (errno!=EINTR)&&(errno!=EAGAIN) ) {
					debugf(getdbgflag(DBG_CACHE,0,0)," cache: error sending data to peer (%s:%d) \n", peer->host->name,peer->port);
					break;
				}
			}
			else debugf(getdbgflag(DBG_CACHE,0,0)," cache: error sending data to peer (%s:%d) \n", peer->host->name,peer->port);
		}
*/
	}
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

#define TYPE_REQUEST   1
#define TYPE_REPLY     2
#define TYPE_PINGREQ   3
#define TYPE_PINGRPL   4
#define TYPE_RESENDREQ 5

#ifdef NEWCACHE

#define TYPE_HELLO          0x03
#define TYPE_HELLO_ACK      0x10
#define TYPE_KEEPALIVE      0x11
#define TYPE_KEEPALIVE_ACK  0x12
#define TYPE_CARD_LIST      0x13
#define TYPE_SMS            0x14
#define TYPE_SMS_ACK        0x15

#define TYPE_VERSION        0x90
#define TYPE_VERSION_ACK    0x91

#define TYPE_EXTENDED       0x92
#define TYPE_EXTENDED_ACK   0x93

#define TYPE_UNKNOWN        0xFF

#endif


int peer_card_binarysearch( struct cachepeer_data *peer, uint16_t caid, uint32_t provid)
{
	if (peer->nbcards==0) return 0;
	int caprov = (caid<<16) | provid; 

	// Returns index of sid in sids, or -1 if not found
	register int xl = 0;
	register int xh = peer->nbcards - 1;
	//
	register int yl = peer->cards[xl];
	register int yh = peer->cards[xh];
	//
	int xm;
	while (yl <= caprov && yh >= caprov) {
		xm = (xl + xh)/2;
		int ym = peer->cards[xm];
		if (ym<caprov) yl = peer->cards[xl=xm+1];
		else if (ym>caprov) yh = peer->cards[xh=xm-1];
		else return 1; // found
	}
	if (peer->cards[xl] == caprov) return 1;
	return 0; // Not found
}

int peer_acceptcard( struct cachepeer_data *peer, uint16_t caid, uint32_t provid)
{
	int i;

	if ( peer->cards[0] ) {
		int caprov = (caid<<16) | provid; 
		for (i=0; i<1024; i++) {
			if (!peer->cards[i]) return 0;
			if (peer->cards[i] == caprov) break;
		}
	}
	if ( peer->sharelimits[0].caid!=0xffff ) {
		for (i=0; i<100; i++) {
			if (peer->sharelimits[i].caid==0xffff) return 0;
			if (peer->sharelimits[i].caid==caid) {
				if (peer->sharelimits[i].provid==provid) break;
				else if (peer->sharelimits[i].provid==0xFFFFFF) break;
			}
		}
	}
	return 1;
}		


void cache_send_request(struct cache_data *pcache,struct cachepeer_data *peer)
{
	uint8_t buf[64];
	//01 80 00CD 0001 0500 8D1DB359
	buf[0] = TYPE_REQUEST;
	buf[1] = pcache->tag;
	buf[2] = pcache->sid>>8;
	buf[3] = pcache->sid;
	buf[4] = pcache->onid>>8;
	buf[5] = pcache->onid&0xff;
	buf[6] = pcache->caid>>8;
	buf[7] = pcache->caid;
	buf[8] = pcache->hash>>24;
	buf[9] = pcache->hash>>16;
	buf[10] = pcache->hash>>8;
	buf[11] = pcache->hash;
	if (peer) {
		sendtopeer(peer, buf, 12);
		peer->sentreq++;
	}
	else {
		struct cacheserver_data	*cache = cfg.cache.server;
		while (cache) {
			peer = cache->peer;
			while (peer) {
				if (peer->ping>0)
				if (peer->flags&FLAG_CACHE_SENDREQ)
				if ( !peer->fblock0onid || pcache->onid )
				if ( peer_card_binarysearch(peer, pcache->caid, pcache->provid) ) {
					sendtopeer(peer, buf, 12);
					peer->sentreq++;
				}
				peer = peer->next;
			}
			cache = cache->next;
		}
	}
}


void cache_send_reply(struct cache_data *pcache,struct cachepeer_data *peer, uint8_t cw[16])
{
	uint8_t buf[64];
	//Common Data
	buf[0] = TYPE_REPLY;
	buf[1] = pcache->tag;
	buf[2] = pcache->sid>>8;
	buf[3] = pcache->sid;
	buf[4] = pcache->onid>>8;
	buf[5] = pcache->onid&0xff;
	buf[6] = pcache->caid>>8;
	buf[7] = pcache->caid;
	buf[8] = pcache->hash>>24;
	buf[9] = pcache->hash>>16;
	buf[10] = pcache->hash>>8;
	buf[11] = pcache->hash;
	buf[12] = pcache->tag;
	memcpy( buf+13, cw, 16);

	if (peer) {
		sendtopeer(peer, buf, 29);
		//peer->sentrep++;
	}
	else {
		struct cacheserver_data	*cache = cfg.cache.server;
		while (cache) {
			peer = cache->peer;
			while ( peer ) {
				if (peer->ping>0)
				//if (peer->flags&FLAG_CACHE_SENDREP)
				if ( !peer->fblock0onid || pcache->onid )
				if ( peer_card_binarysearch(peer, pcache->caid, pcache->provid) ) {
					sendtopeer(peer, buf, 29);
					peer->sentrep++;
				}
				peer = peer->next;
			}
			cache = cache->next;
		}
	}
}


void cache_send_fwdreply(struct cache_data *pcache, uint8_t cw[16], cwcycle_t cwcycle)
{
	uint8_t buf[64];
	//Common Data
	buf[0] = TYPE_REPLY;
	buf[1] = pcache->tag;
	buf[2] = pcache->sid>>8;
	buf[3] = pcache->sid;
	buf[4] = pcache->onid>>8;
	buf[5] = pcache->onid&0xff;
	buf[6] = pcache->caid>>8;
	buf[7] = pcache->caid;
	buf[8] = pcache->hash>>24;
	buf[9] = pcache->hash>>16;
	buf[10] = pcache->hash>>8;
	buf[11] = pcache->hash;
	buf[12] = pcache->tag;
	memcpy( buf+13, cw, 16);
	buf[29] = cwcycle;

	struct cacheserver_data	*cache = cfg.cache.server;
	while (cache) {
		struct cachepeer_data *peer = cache->peer;
		while ( peer ) {
			//if (peer->flags&FLAG_CACHE_SENDREP)
			if ( peer->ping>0 )
			if ( peer->fwd )
			if ( peer_card_binarysearch(peer, pcache->caid, pcache->provid) ) {
				sendtopeer(peer, buf, 30);
				peer->sentrep++;
			}
			peer = peer->next;
		}
		cache = cache->next;
	}
}

void cache_send_resendreq(struct cache_data *pcache)
{
	struct cacheserver_data *cache = cfg.cache.server;
	while (cache) {
		// <1:TYPE_RESENDREQ> <2:port> <1:ecmtag> <2:sid> <2:onid> <2:caid> <4:hash>
		uint8_t buf[64];
		buf[0] = TYPE_RESENDREQ;
		//Port
		buf[1] = 0;
		buf[2] = 0;
		buf[3] = cache->port>>8;
		buf[4] = cache->port&0xff;
		buf[5] = pcache->tag;
		buf[6] = pcache->sid>>8;
		buf[7] = pcache->sid;
		buf[8] = pcache->onid>>8;
		buf[9] = pcache->onid&0xff;
		buf[10] = pcache->caid>>8;
		buf[11] = pcache->caid;
		buf[12] = pcache->hash>>24;
		buf[13] = pcache->hash>>16;
		buf[14] = pcache->hash>>8;
		buf[15] = pcache->hash;
		struct cachepeer_data *peer = cache->peer;
		while ( peer ) {
			if ( peer->ping>0 )
			if ( peer_card_binarysearch(peer, pcache->caid, pcache->provid) )
				sendtopeer(peer, buf, 16);
			peer = peer->next;
		}
		cache = cache->next;
	}
}

void cache_send_ping(struct cacheserver_data *cache, struct cachepeer_data *peer)
{
	unsigned char buf[32];
	buf[0] = TYPE_PINGREQ;
	// New Cache IDENT
	buf[1] = 'M';
	buf[2] = 'C';
	/// buf[3] = 1 | BIT_CACHE_HACK;
	buf[3] = 1;
	// PEER ID
	buf[4] = peer->id>>8; 
	buf[5] = peer->id&0xff;
	// MULTICS CRC
	buf[6] = peer->crc[0] = 0xff & rand();
	buf[7] = peer->crc[1] = 0xff & rand();
	buf[8] = peer->crc[2] = 0xff & rand();
	//Port
	buf[9] = peer->crc[3] = 0;
	buf[10] = 0;
	buf[11] = cache->port>>8;
	buf[12] = cache->port&0xff;

	//Program
	buf[13] = 0x01; //ID
	buf[14] = 7; //LEN
	buf[15] = 'M'; buf[16] = 'u'; buf[17] = 'l'; buf[18] = 't'; buf[19] = 'i'; buf[20] = 'C'; buf[21] = 'S';
	//Version
	buf[22] = 0x02; //ID
	buf[23] = 3; //LEN
	buf[24] = 'r'; buf[25] = '0'+(REVISION/10); buf[26] = '0'+(REVISION%10);
	//
	sendtopeer( peer, buf, 27);
}


#ifdef NEWCACHE

void cache_send_keepalive(struct cacheserver_data *cache, struct cachepeer_data *peer)
{
	if (peer->protocol&1) {
		unsigned char buf[32];
		buf[0] = TYPE_KEEPALIVE;
		buf[1] = peer->id>>8; 
		buf[2] = peer->id&0xff;
		sendtopeer( peer, buf, 3);
	}
	else cache_send_ping(cache, peer);
}

void save_sms( struct sms_data *sms, struct cachepeer_data *peer)
{
	// print to sms file
	FILE *fhandle;
	fhandle=fopen(sms_file, "at");
	if (fhandle!=0) {
		// Get Time
		char timebuf [80];
		struct tm * timeinfo = localtime (&sms->rawtime);
		strftime (timebuf,80,"%x %X",timeinfo);
		if (sms->status&1) fprintf(fhandle,"\n\n[ %s ] >> SMS to peer (%s:%d)\n", timebuf, peer->host->name, peer->port);
		else  fprintf(fhandle,"\n\n[ %s ] << SMS from peer (%s:%d)\n", timebuf, peer->host->name, peer->port);
		fputs(sms->msg, fhandle);
		fclose(fhandle);
	}
}

struct sms_data *cache_new_sms(char *msg)
{
	struct sms_data *sms = malloc( sizeof(struct sms_data) );
	int len = strlen(msg);
	uint32_t hash = hashCode((uint8_t*)msg, len);
	strcpy( sms->msg, msg );
	sms->hash = hash;
	time (&sms->rawtime);
	sms->next = NULL;
	return (sms);
}

void cache_send_sms(struct cachepeer_data *peer, struct sms_data *sms)
{
	int len = strlen(sms->msg);
	sms->status = 1; // bit 0 (0:in,1:out) bit 1 (0:unread/unack, 1:read/ack)
	sms->next = peer->sms;
	peer->sms = sms;
	// Send Buffer
	uint8_t buf[1024];
	buf[0] = TYPE_SMS;
	buf[1] = sms->hash>>24;
	buf[2] = sms->hash>>16;
	buf[3] = sms->hash>>8;
	buf[4] = sms->hash;
	memcpy( buf+5, sms->msg, len );
	sendtopeer(peer, buf, len+5);
	// Debug
	debugf(getdbgflag(DBG_CACHE,0,0)," SMS to peer (%s:%d)\n", peer->host->name, peer->port);
	save_sms( sms, peer);
}


#endif

void peer_check_messages( struct cachepeer_data *peer )
{
	// Remove Old Messages if there is too much
	struct sms_data *sms = peer->sms;
	int nb = 0;
	while (sms) {
		nb++;
		if (nb>=30) break;
		sms = sms->next;
	}
	// Remove Messages
	if (sms) {
		struct sms_data *next = sms->next;
		sms->next = NULL;
		while (next) {
			sms = next;
			next = sms->next;
			free(sms);
		}
	}
}






///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// DCW CHECK & SET 
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////


/*
	look for older cw anf check for cwcycle
	return
	-1 : error
	0: nothing found
	1: found cycle
*/

int cache_fetch_goodcw( struct cache_data *thereq, uint8_t cw[16], cwcycle_t *cwcycle )
{
#ifdef TESTCHANNEL
	int testchannel = ( (thereq->caid==cfg.testchn.caid) && (!thereq->provid || thereq->provid==cfg.testchn.provid) && (thereq->sid==cfg.testchn.sid) );
	if (testchannel) {
		char dump[64];
		array2hex( cw, dump, 16);
		char dump2[64];
		array2hex( thereq->prevcw, dump2, 16);
		fdebugf(" (cache_fetch_goodcw.%x) ch %04x:%06x:%04x/%02x:%08x -> %s (%s) P:%s\n", thereq->flags&CACHE_FLAG_SENDPIPE, thereq->caid, thereq->provid, thereq->sid,
			thereq->tag, thereq->hash, dump, cwcycle2str(thereq->cwcycle), dump2 );
	}
#endif

	int index = thereq->sid&MAX_CACHE_INDEX;
	struct cache_data **cachetab = getcachetabbycaid(thereq->caid);
	struct cache_data *pcache = cachetab[index];
	struct cache_data *result = NULL;
	uint32_t ticks = GetTickCount() - cfg.cache.alivetime;
	while (pcache) {
		if ( (pcache->recvtime+cfg.cache.filtertime)<thereq->recvtime) {
			if ( pcache->recvtime < ticks ) break;
			if ( (pcache->sid==thereq->sid)&&(pcache->hash!=thereq->hash) ) // need provider XXX
			if ( !pcache->provid || !thereq->provid || (pcache->provid==thereq->provid) ) // csp luck of provid
			if ( pcache->tag && (pcache->tag!=thereq->tag) ) { // old oscam cacheex luck of ecmtag
				struct cw_cache_data *cwdata = pcache->cwdata;
				while (cwdata) {
					if ( dcwcmp16(cwdata->cw,cw) ) {
#ifdef TESTCHANNEL
						if (testchannel) {
							char dump[64];
							array2hex( cwdata->cw, dump, 16);
							fdebugf(" >> Same dcw, cache %04x:%06x:%04x/%02x:%08x::%s\n", pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash, dump);
						}
#endif
						return DCW_ERROR; // not gooooooooooooood maybe another provider of same channel
					}
					else if ( !(cwdata->status&DCW_ERROR) ) {
						if ( dcwcmp8(cwdata->cw,cw) && !similarcw(cwdata->cw+8,cw+8) ) { // ????????????????????????????
							if (cwdata->cwcycle!=CW1CYCLE) {
								*cwcycle = CW1CYCLE; // CW1 changed
#ifdef TESTCHANNEL
								if (testchannel) {
									char dump[64];
									array2hex( cwdata->cw, dump, 16);
									fdebugf(" >> CW1 cycle(%d) cache %04x:%06x:%04x/%02x:%08x::%s\n", cwdata->status, pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash, dump);
								}
#endif
								if (cwdata->status&DCW_CYCLE) return DCW_CYCLE;
								result = pcache;
							}
						}
						else if ( !similarcw(cwdata->cw,cw) && dcwcmp8(cwdata->cw+8,cw+8) ) {
							if (cwdata->cwcycle!=CW0CYCLE) {
								*cwcycle = CW0CYCLE; // CW0 changed
#ifdef TESTCHANNEL
								if (testchannel) {
									char dump[64];
									array2hex( cwdata->cw, dump, 16);
									fdebugf(" >> CW0 cycle(%d) cache %04x:%06x:%04x/%02x:%08x::%s\n", cwdata->status, pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash, dump);
								}
#endif
								if (cwdata->status&DCW_CYCLE) return DCW_CYCLE; //2
								result = pcache;
							}
						}
					}
					cwdata = cwdata->next;
				}
			}
		}
		pcache = pcache->next;
		if (pcache==cachetab[index]) break;
	}
	if (result) return DCW_CYCLE; //1
	return 0; //DCW_NOCYCLE
}


// search for same cw in cache
int cache_check_cw( uint32_t recvtime, uint8_t tag, uint16_t caid, uint32_t hash, uint16_t sid, uint8_t cw[16], int cwpart )
{
	int index = sid&MAX_CACHE_INDEX;
	struct cache_data **cachetab = getcachetabbycaid(caid);
	struct cache_data *pcache = cachetab[index];
	uint32_t ticks = GetTickCount() - cfg.cache.alivetime;
	while (pcache) {
		if ( (pcache->recvtime+cfg.cache.filtertime)<recvtime) {
			if ( pcache->recvtime < ticks ) break;
			if ( (pcache->sid==sid)&&(pcache->hash!=hash)&&(pcache->tag!=tag) ) { // ??? maybe find same dcw for different providers
				struct cw_cache_data *cwdata = pcache->cwdata;
				while (cwdata) {
					switch (cwpart) {
						case 0:
							if ( dcwcmp8(cwdata->cw,cw) ) return 0;
							if ( dcwcmp8(cwdata->cw+8,cw) ) return 0;
							break;
						case 1:
							if ( dcwcmp8(cwdata->cw,cw+8) ) return 0;
							if ( dcwcmp8(cwdata->cw+8,cw+8) ) return 0;
							break;
						case 2:
							if ( dcwcmp16(cwdata->cw,cw) ) return 0;
							break;
					}
					cwdata = cwdata->next;
				}
			}
		}
		pcache = pcache->next;
		if (pcache==cachetab[index]) break;
	}
	return 1;
}


struct cw_cache_data *iscwincache(struct cache_data *pcache, uint8_t cw[16])
{
	struct cw_cache_data *cwdata = pcache->cwdata;
	while (cwdata) {
		if ( dcwcmp16(cwdata->cw, cw) ) return cwdata;
		cwdata = cwdata->next;
	}
	return NULL;
}

// update dcw for cache data of same channel with different hash and provider
struct cache_data *cache_fetch_goodcw2( struct cache_data *thereq, uint8_t cw[16], int peerid )
{
#ifdef TESTCHANNEL
	int testchannel = ( (thereq->caid==cfg.testchn.caid) && (!thereq->provid || thereq->provid==cfg.testchn.provid) && (thereq->sid==cfg.testchn.sid) );
	if (testchannel) {
		char dump[64];
		array2hex( cw, dump, 16);
		fdebugf(" (cache_fetch_goodcw2) ch %04x:%06x:%04x/%02x:%08x -> %s\n", thereq->caid, thereq->provid, thereq->sid, thereq->tag, thereq->hash, dump);
	}
#endif
	int index = thereq->sid&MAX_CACHE_INDEX;
	struct cache_data **cachetab = getcachetabbycaid(thereq->caid);
	struct cache_data *pcache = cachetab[index];
	struct cache_data *result = NULL;
	uint32_t ticks = GetTickCount() - cfg.cache.alivetime;
	while (pcache) {
		if ( pcache->recvtime < ticks ) break;
		if ( pcache->flags&CACHE_FLAG_SENDPIPE )
		if ( (pcache->sid==thereq->sid)&&(pcache->hash!=thereq->hash) ) // need provider XXX
		//if ( !pcache->provid || !thereq->provid || (pcache->provid!=thereq->provid) )
		if ( pcache->tag && (pcache->tag==thereq->tag) )
		if ( pcache->cwcycle!=NO_CYCLE )
		if ( !isnullDCW(pcache->prevcw) )
		if ( !iscwincache(pcache,cw) )
		{
			if (  ( (pcache->cwcycle==CW1CYCLE) && dcwcmp8(pcache->prevcw,cw) && !similarcw(pcache->prevcw+8,cw+8) ) ||
				( (pcache->cwcycle==CW0CYCLE) && !similarcw(pcache->prevcw,cw) && dcwcmp8(pcache->prevcw+8,cw+8) )  ) {

				pipe_cache2ecm_find_success(pcache, cw, peerid);

#ifdef TESTCHANNEL
				if (testchannel) {
					char dump1[64];
					char dump2[64];
					array2hex( cw, dump1, 16);
					array2hex( pcache->prevcw, dump2, 16);
					fdebugf(" ==%s== cache %04x:%06x:%04x/%02x:%08x %s => %s\n", cwcycle2str(pcache->cwcycle), pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash, dump2,dump1);
					fdebugf(" ==%s== Update %04x:%06x:%04x/%02x:%08x from %04x:%06x:%04x/%02x:%08x\n", cwcycle2str(pcache->cwcycle), 
						pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash,
						thereq->caid, thereq->provid, thereq->sid, thereq->tag, thereq->hash );
				}
#endif

			}
		}
		pcache = pcache->next;
		if (pcache==cachetab[index]) break;
	}
	return result;
}

// cache check3 
// update dcw for cache data of same caid,tag,hash with different sid
//[17:36:34.278] <- ecm from cs378x client 'm1' ch 0100:003315:233d:41353b6b
//[17:36:34.278] <- ecm from cs378x client 'm1' ch 0100:003315:2351:41353b6b

struct cache_data *cache_fetch_goodcw3( struct cache_data *thereq, uint8_t cw[16], int peerid )
{
#ifdef TESTCHANNEL
	int testchannel = ( (thereq->caid==cfg.testchn.caid) && (!thereq->provid || thereq->provid==cfg.testchn.provid) && (thereq->sid==cfg.testchn.sid) );
	if (testchannel) {
		char dump[64];
		array2hex( cw, dump, 16);
		fdebugf(" (cache_fetch_goodcw3) ch %04x:%06x:%04x/%02x:%08x -> %s\n", thereq->caid, thereq->provid, thereq->sid, thereq->tag, thereq->hash, dump);
	}
#endif
	int index = thereq->sid&MAX_CACHE_INDEX;
	struct cache_data **cachetab = getcachetabbycaid(thereq->caid);
	struct cache_data *pcache = cachetab[index];
	struct cache_data *result = NULL;
	uint32_t ticks = GetTickCount() - cfg.cache.alivetime;
	while (pcache) {
		if ( pcache->recvtime < ticks ) break;
		if ( pcache->flags&CACHE_FLAG_SENDPIPE )
		if (pcache->sid!=thereq->sid)
		if ( (pcache->hash==thereq->hash) ) // need provider XXX
		if ( pcache->tag && (pcache->tag==thereq->tag) )
		if ( pcache->cwcycle!=NO_CYCLE )
		if ( !isnullDCW(pcache->prevcw) )
		if ( !iscwincache(pcache,cw) )
		{
			if (  ( (pcache->cwcycle==CW1CYCLE) && dcwcmp8(pcache->prevcw,cw) && !similarcw(pcache->prevcw+8,cw+8) ) ||
				( (pcache->cwcycle==CW0CYCLE) && !similarcw(pcache->prevcw,cw) && dcwcmp8(pcache->prevcw+8,cw+8) )  ) {

				pipe_cache2ecm_find_success(pcache, cw, peerid);

#ifdef TESTCHANNEL
				if (testchannel) {
					char dump1[64];
					char dump2[64];
					array2hex( cw, dump1, 16);
					array2hex( pcache->prevcw, dump2, 16);
					fdebugf(" ==%s== cache %04x:%06x:%04x/%02x:%08x %s => %s\n", cwcycle2str(pcache->cwcycle), pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash, dump2,dump1);
					fdebugf(" ==%s== Update %04x:%06x:%04x/%02x:%08x from %04x:%06x:%04x/%02x:%08x\n", cwcycle2str(pcache->cwcycle), 
						pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash,
						thereq->caid, thereq->provid, thereq->sid, thereq->tag, thereq->hash );
				}
#endif

			}
		}
		pcache = pcache->next;
		if (pcache==cachetab[index]) break;
	}
	return result;
}


// -1: bad cw
// 0: nothing to do
// 1: dcw set
// 2: dcw set & sent to pipe
int cache_setdcw( struct cache_data *req, uint8_t cw[16], cwcycle_t cwcycle, int peerid )
{
	if ( !acceptDCW(cw) ) return -1;

	// Search for Cache data
	struct cw_cache_data *cwdata = NULL;
	struct cache_data *pcache = cache_fetch( req );
	if (pcache==NULL) pcache = cache_new( req );
	else {
		if (!pcache->tag) pcache->tag = req->tag; // set tag if not set (coming from cahceex)
		if (!pcache->provid && req->provid) pcache->provid = req->provid; // set provid if not set (coming from csp)
		// check for dcw
		cwdata = pcache->cwdata;
		while (cwdata) {
			if ( dcwcmp16(cwdata->cw, cw) ) break;
			cwdata = cwdata->next;
		}
	}
	// add new if not found
	if (!cwdata) {
		cwdata = malloc( sizeof(struct cw_cache_data) );
		memset( cwdata, 0, sizeof(struct cw_cache_data) );
		memcpy(cwdata->cw, cw, 16);
		cwdata->status = 0;
		cwdata->cwcycle = NO_CYCLE;
		cwdata->peerid = peerid;
		cwdata->next = pcache->cwdata;
		pcache->cwdata = cwdata;
	}
	cwdata->nbpeers++;

	if (cwdata->status&DCW_ERROR) return DCW_ERROR;

	// TEST for min Peers
	if (cwdata->nbpeers!=cfg.cache.threshold) return DCW_ERROR | DCW_SKIP;

	// ACCEPTED
	if (cfg.cache.dcwcheck2) cache_fetch_goodcw2(pcache, cw, peerid);
	if (cfg.cache.dcwcheck3) cache_fetch_goodcw3(pcache, cw, peerid);

	// Half Nulled CW
	char nullcw[8] = "\0\0\0\0\0\0\0\0";
	if ( !dcwcmp8(cw,nullcw) && !dcwcmp8(cw+8,nullcw) ) {

		if ( !isnullDCW(pcache->prevcw) ) {
			if (  ( (pcache->cwcycle==CW1CYCLE) && dcwcmp8(pcache->prevcw,cw) && !similarcw(pcache->prevcw+8,cw+8) ) ||
				( (pcache->cwcycle==CW0CYCLE) && !similarcw(pcache->prevcw,cw) && dcwcmp8(pcache->prevcw+8,cw+8) )  ) {
				// update new dcw
				cwdata->status |= DCW_CYCLE;
				cwdata->cwcycle = pcache->cwcycle;
				cwdata->peerid = peerid;
				if ( (pcache->flags&CACHE_FLAG_SENDPIPE) && !(cwdata->status&DCW_SENT) ) {
					pipe_cache2ecm_find_success(pcache, cw, peerid);
					cwdata->status |= DCW_SENT;
				}
				return cwdata->status;
			}
		}
		// cache without cw1 cycle
		if (cfg.cache.filter && (cwcycle==NO_CYCLE) ) {
			cwdata->status |= cache_fetch_goodcw(pcache, cw, &cwcycle);
			cwdata->cwcycle = cwcycle;
			cwdata->peerid = peerid;
			if (!(cwdata->status&DCW_CYCLE)) return cwdata->status; //XXX
		}
		else {
			// update new dcw
			cwdata->status |= DCW_CYCLE;
			cwdata->cwcycle = cwcycle;
			cwdata->peerid = peerid;
		}
		// Check Cycle
		if ( (pcache->cwcycle!=NO_CYCLE)&&(pcache->cwcycle!=cwcycle) ) {
			cwdata->status |= DCW_ERROR;
			return cwdata->status;
		}
	}
	else {
		// half nulled cw: exit if non-nds
		if ( (req->caid>>8)!=9 ) {
			cwdata->status |= DCW_ERROR;
			return cwdata->status;
		}
		//
		if ( dcwcmp8(cw,nullcw) ) cwcycle = CW1CYCLE;
		else if ( dcwcmp8(cw+8,nullcw) ) cwcycle = CW0CYCLE;
		// update new dcw
		cwdata->status |= DCW_CYCLE;
		cwdata->cwcycle = cwcycle;
		cwdata->peerid = peerid; // CSP CACHE
	}

	if ( (pcache->flags&CACHE_FLAG_SENDPIPE) && !(cwdata->status&DCW_SENT) ) {
		cwdata->status |= DCW_SENT;
		pipe_cache2ecm_find_success(pcache, cw, peerid);
#ifdef TESTCHANNEL
		int testchannel = ( (pcache->caid==cfg.testchn.caid) && (!pcache->provid || pcache->provid==cfg.testchn.provid) && (pcache->sid==cfg.testchn.sid) );
		if (testchannel) {
			char dump[64];
			array2hex( cw, dump, 16);
			fdebugf(" (Cache->Setdcw) ch %04x:%06x:%04x/%02x:%08x -> %s (%s)\n", pcache->caid, pcache->provid, pcache->sid, pcache->tag, pcache->hash,
				dump, cwcycle2str(pcache->cwcycle) );
		}
#endif
	}
	return cwdata->status;
}



///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// RECEIVE MESSAGES
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

void cache_recvmsg(struct cacheserver_data *cache)
{
	unsigned int recv_ip;
	unsigned short recv_port;
	unsigned char buf[2048];
	char str[1024];
	struct sockaddr_in si_other;
	socklen_t slen=sizeof(si_other);
	struct cachepeer_data *peer;

	int received = recvfrom( cache->handle, buf, sizeof(buf), 0, (struct sockaddr*)&si_other, &slen);
	if ( (received<2)||(received>512) ) return;
	memcpy( &recv_ip, &si_other.sin_addr, 4);
	recv_port = ntohs(si_other.sin_port);

#ifdef DEBUG_NETWORK2
	if (flag_debugnet) {
		debugf(getdbgflag(DBG_CACHE,0,0)," cache: recv data (%d) from address (%s:%d)\n", received, ip2string(recv_ip), recv_port );
		debughex(buf,received);
	}
#endif

	// Store Data
	struct cache_data req;
	switch(buf[0]) {


		case TYPE_REQUEST:
			// Check Peer
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			// Check Status
			if (IS_DISABLED(peer->flags)) break;
			// Get DATA
			req.tag = buf[1];
			req.sid = (buf[2]<<8) | buf[3];
			req.onid = (buf[4]<<8) | buf[5];
			req.caid = (buf[6]<<8) | buf[7];
			req.hash = (buf[8]<<24) | (buf[9]<<16) | (buf[10]<<8) |buf[11];
			req.provid = 0;

			pthread_mutex_lock( &prg.lockcache );

			// Check Cache Request
			if ( cache_check(&req) ) {
				peer->reqnb++;
				// ADD CACHE
				struct cache_data *pcache = cache_fetch( &req );
				if (pcache==NULL) pcache = cache_new( &req );
				else {
					if (!pcache->tag) pcache->tag = req.tag; // set tag if not set (coming from cahceex)
					if (!pcache->provid && req.provid) pcache->provid = req.provid; // set provid if not set (coming from csp)
					if ( cfg.cache.forward || ((peer->ismultics)&&(peer->protocol&BIT_CACHE_HACK)) )
					if ( pcache->cwdata && !(pcache->flags&CACHE_FLAG_SENDPIPE) ) {
						struct cw_cache_data *cwdata = pcache->cwdata;
						while (cwdata) {
							if (!(cwdata->status&DCW_ERROR)) {
								cache_send_reply( pcache, peer, cwdata->cw);
								if (cfg.cache.forward) peer->sentrep++;
							}
							cwdata = cwdata->next;
						}
					}
				}
			}

			pthread_mutex_unlock( &prg.lockcache );
			break;


		case TYPE_REPLY:
			// Check Peer
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			// Check Status
			if (IS_DISABLED(peer->flags)) break;
			// Check Integrity
			if (buf[12]!=buf[1]) break;
			// SetUp Request
			req.tag = buf[1];
			req.sid = (buf[2]<<8) | buf[3];
			req.onid = (buf[4]<<8) | buf[5];
			req.caid = (buf[6]<<8) | buf[7];
			req.hash = (buf[8]<<24) | (buf[9]<<16) | (buf[10]<<8) |buf[11];
			req.provid = 0;

			pthread_mutex_lock( &prg.lockcache );

			// Check Cache Request
			if ( cache_check(&req) ) {
				// check for length
				if ( received>=29 ) {
					uint8_t cw[16];
					peer->repok++;
					memcpy(cw, buf+13, 16);
					// Search for Cache data
					cwcycle_t cwcycle = NO_CYCLE;
					if ( peer->fwd && (received==30) ) cwcycle = buf[29];
					int status = cache_setdcw(&req,cw,cwcycle,peer->id|PEER_CSP);
					if ( !(status&DCW_ERROR) ) { // && (status&DCW_CYCLE) ) {
						if (!peer->fwd) {
							cache_send_fwdreply( &req, cw, cwcycle);
						}
					}
				}
			}

			pthread_mutex_unlock( &prg.lockcache );
			break;

		case TYPE_RESENDREQ:
			// Check Peer
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			// Check Status
			if (IS_DISABLED(peer->flags)) break;
			// Check Packer length
			if (received<16) break;
			// Good Packet
			struct cache_data req;
			req.tag = buf[5];
			req.sid = (buf[6]<<8) | buf[7];
			req.onid = (buf[8]<<8) | buf[9];
			req.caid = (buf[10]<<8) | buf[11];
			req.hash = (buf[12]<<24) | (buf[13]<<16) | (buf[14]<<8) | buf[15];
			req.provid = 0;
			//
			pthread_mutex_lock( &prg.lockcache );
			// Check Cache Request
			if ( cache_check(&req) ) {
				struct cache_data *pcache = cache_fetch( &req );
				if (pcache!=NULL) {
					buf[4] = TYPE_REPLY;
					buf[16] = buf[5];
					struct cw_cache_data *cwdata = pcache->cwdata;
					while (cwdata) {
						if (!(cwdata->status&DCW_ERROR)) {
							memcpy( buf+17, cwdata->cw, 16);
							sendtopeer( peer, buf+4, 29);
						}
						cwdata = cwdata->next;
					}
				}
			}
			//
			pthread_mutex_unlock( &prg.lockcache );
			break;

		case TYPE_PINGREQ:
			// Check Peer
			peer = cache->peer;
			int port = (buf[11]<<8)|buf[12];
			struct cachepeer_data *peerip = NULL;
			while (peer) {
				if (peer->host->ip==recv_ip) {
					peerip = peer;
					if (peer->port==port) break; // Found
				}
				peer = peer->next;
			}
			if (!peer) {
				if (peerip) {
					// Check for peer reuse
					peer = cache->peer;
					while (peer) {
						if ( (peer->port==port) && !strcmp(peer->host->name,peerip->host->name) ) break;
						peer = peer->next;
					}
					if (!peer) {
						if (peerip->port==0) peerip->port = port;
						else {
							// add new peer
							peer = malloc( sizeof(struct cachepeer_data) );
							memset( peer, 0, sizeof(struct cachepeer_data) );
							peer->host = peerip->host;
							peer->port = port;
							peer->outsock = cache->handle; //CreateClientSockUdp(0,0);
							peer->runtime = 1;
							peer->fblock0onid = peerip->fblock0onid;
							peer->id = cfg.cache.peerid;
							cfg.cache.peerid++;
							peer->flags = FLAG_CACHE_SENDREQ | FLAG_CACHE_SENDREP;
							peer->next = peerip->next;
							peerip->next = peer;

							if (!peerip->autoadd || !cfg.cache.autoadd) {
								peer->flags |= FLAG_DISABLE;
								// Check for extended reply ( Program Name/Version )
								int index = 13;
								while (received>index) {
									if ( (index+buf[index+1]+2)>received ) break;
									switch(buf[index]) {
										case 0x01:
											if (buf[index+1]<32) { memcpy(peer->program, buf+index+2, buf[index+1]); peer->program[buf[index+1]] = 0; }
											break;
										case 0x02:
											if (buf[index+1]<32) { memcpy(peer->version, buf+index+2, buf[index+1]); peer->version[buf[index+1]] = 0; }
											break;
									}
									index += 2+buf[index+1];
								}
							}
							else debugf(getdbgflag(DBG_CACHE,0,0), " cache: new peer (%s:%d)\n", peer->host->name, peer->port );
						}
					}
					else {
						peer->host->checkiptime = 0;
					}
				}
				else if (cfg.cache.autoadd) {
					// add new peer
					peer = malloc( sizeof(struct cachepeer_data) );
					memset( peer, 0, sizeof(struct cachepeer_data) );
					peer->flags = FLAG_CACHE_SENDREQ | FLAG_CACHE_SENDREP;
					// ADD HOST
					struct host_data *host = add_host( &cfg, ip2string(recv_ip) );
					host->ip = recv_ip;
					peer->host = host;
					peer->port = port;
					peer->id = cfg.cache.peerid;
					peer->srvid = cache->id;
					peer->outsock = cache->handle; //CreateClientSockUdp(0,0);
					peer->runtime = 1;
					cfg.cache.peerid++;
					cfg_addcachepeer(cache, peer);
					if (!cfg.cache.autoenable) {
						peer->flags |= FLAG_DISABLE;
						// Check for extended reply ( Program Name/Version )
						int index = 13;
						while (received>index) {
							if ( (index+buf[index+1]+2)>received ) break;
							switch(buf[index]) {
								case 0x01:
									if (buf[index+1]<32) { memcpy(peer->program, buf+index+2, buf[index+1]); peer->program[buf[index+1]] = 0; }
									break;
								case 0x02:
									if (buf[index+1]<32) { memcpy(peer->version, buf+index+2, buf[index+1]); peer->version[buf[index+1]] = 0; }
									break;
							}
							index += 2+buf[index+1];
						}
					}
					else debugf(getdbgflag(DBG_CACHE,0,0), " cache: new peer (%s:%d)\n", peer->host->name, peer->port );
				}
				else debugf( getdbgflag(DBG_CACHE,0,0), " cache: Alert! unknown peer (%s:%d)\n", ip2string(recv_ip), port );
			}
			//
			if (peer) {
				// Check Status
				if (IS_DISABLED(peer->flags)) break;
				// Set Defaults
				memset( peer->cards, 0, sizeof(peer->cards) );
				peer->protocol = 0; // Normal CSP Protocol
				peer->program[0] = 0;
				peer->version[0] = 0;
				// Check for extended reply ( Program Name/Version )
				int index = 13;
				while (received>index) {
					if ( (index+buf[index+1]+2)>received ) break;
					switch(buf[index]) {
						case 0x01:
							if (buf[index+1]<32) { memcpy(peer->program, buf+index+2, buf[index+1]); peer->program[buf[index+1]] = 0; }
							break;
						case 0x02:
							if (buf[index+1]<32) { memcpy(peer->version, buf+index+2, buf[index+1]); peer->version[buf[index+1]] = 0; }
							break;
					}
					index += 2+buf[index+1];
				}
				// Set Default Reply
				buf[0] = TYPE_PINGRPL;
				// Check for New Protocol
				if ( buf[1]=='M' && buf[2]=='C' ) {
					peer->protocol = buf[3];
					if (peer->protocol&1) {
						buf[0] = TYPE_HELLO_ACK;
						// Decode CRC
						buf[13] = cache->port>>8;
						buf[14] = cache->port;
						fase( buf+11, buf+6);
					}
				} else if ( !peer->csp || (received>13) ) { peer->flags |= FLAG_DISABLE; break; }
				sendtopeer( peer, buf, 9);
				// Check for activity
				if (peer->recvport!=recv_port) {
					peer->ping = 0;
					peer->recvport = recv_port;
					peer->ismultics = 0;
				}
			}
			break;


		case TYPE_PINGRPL:
			// Get Peer
			peer = cache->peer;
			int peerid = (buf[4]<<8) | buf[5];
			while (peer) {
				if ( (peer->host->ip==recv_ip)&&(peer->id==peerid) ) {
					peer->protocol = 0;
					peer->recvport = recv_port;
					peer->lastpingrecv = GetTickCount();
					if (peer->ping>0)
						peer->ping = (peer->ping+peer->lastpingrecv-peer->lastpingsent)/2;
					else {
						if (!peer_doublecheck(cache,peer)) break;
						debugf(getdbgflag(DBG_CACHE,0,peer->id), " cache: Peer (%s:%d) come Online\n", peer->host->name, peer->port );
						peer->ping = peer->lastpingrecv-peer->lastpingsent;
						//ipeer_update(cache);
					}
					peer->ping++;
					break;
				}
				peer = peer->next;
			}
			break;


#ifdef NEWCACHE
				case TYPE_HELLO_ACK:
					// Get Peer
					peerid = (buf[4]<<8) | buf[5];
					peer = cache->peer;
					while (peer) {
						if ( (peer->host->ip==recv_ip)&&(peer->id==peerid) ) {
							// Check for private new cache
							uint8_t k[4]; k[0] = cache->port>>8; k[1] = cache->port; k[2]=peer->port>>8; k[3]=peer->port; 
							fase( k, peer->crc);
							if ( (peer->crc[0]==buf[6])&&(peer->crc[1]==buf[7])&&(peer->crc[2]==buf[8]) ) peer->ismultics =1; else peer->ismultics = 0;
							//
							peer->recvport = recv_port;
							peer->lastpingrecv = GetTickCount();
							if (peer->ping>0)
								peer->ping = (peer->ping+peer->lastpingrecv-peer->lastpingsent)/2;
							else {
								if (!peer_doublecheck(cache,peer)) break;
								debugf(getdbgflag(DBG_CACHE,0,peer->id), " cache: Peer (%s:%d) come Online*\n", peer->host->name, peer->port );
								peer->ping = peer->lastpingrecv-peer->lastpingsent;
								//ipeer_update(cache);
							}
							peer->ping++;
							//debugf(getdbgflag(DBG_CACHE,0,peer->id), " cache: sending card data to peer (%s:%d)\n", peer->host->name, peer->port );
							// Send CARDS DATA
							buf[0] = TYPE_CARD_LIST;
							buf[1] = 1; // Reset Cards
							int pos = 2;

							//sendtopeer( peer, buf, pos);

							if (peer->sharelimits[0].caid!=0xffff) {
								int i;
								for (i=0; i<100; i++) {
									if (peer->sharelimits[i].caid==0xffff) break;
									uint32_t caprov = (peer->sharelimits[i].caid<<16)|(peer->sharelimits[i].provid);
 									buf[pos] = caprov>>24;
 									buf[pos+1] = caprov>>16;
 									buf[pos+2] = caprov>>8;
 									buf[pos+3] = caprov;
									pos +=4;
									if (pos>400) {
										sendtopeer( peer, buf, pos);
										buf[0] = TYPE_CARD_LIST;
										buf[1] = 0; // no Reset
										pos = 2;
									}
								}
								if (pos>2) {
									sendtopeer( peer, buf, pos);
								}
							}
							else
							{
								struct cardserver_data *cs = cfg.cardserver;
								while (cs) {
									int i;
									if (cs->option.fallowcache)
									for (i=0; i<cs->card.nbprov; i++) {
										uint32_t caprov = (cs->card.caid<<16)|(cs->card.prov[i].id);
 										buf[pos] = caprov>>24;
 										buf[pos+1] = caprov>>16;
 										buf[pos+2] = caprov>>8;
 										buf[pos+3] = caprov;
										pos +=4;
										if (pos>400) {
											sendtopeer( peer, buf, pos);
											buf[0] = TYPE_CARD_LIST;
											buf[1] = 0; // no Reset
											pos = 2;
										}
									}
									cs = cs->next;
								}
								if (pos>2) {
									sendtopeer( peer, buf, pos);
								}
							}

							break;
						}
						peer = peer->next;
					}
					break;


		case TYPE_CARD_LIST:
			// Check Peer
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			// reset cards
			int idx = 0;
			if (buf[1]&1) memset( peer->cards, 0, sizeof(peer->cards) ); 
			else for (idx=0; idx<1024; idx++) if (!peer->cards[idx]) break;
			//
			int totalcards = (received-2)/4;
			int j=0;
			while ( (j<totalcards)&&(idx<1024) ){
				peer->cards[idx] = (buf[2+j*4]<<24)|(buf[3+j*4]<<16)|(buf[4+j*4]<<8)|(buf[5+j*4]);
				j++; idx++;
			}
			peer->nbcards = idx;
			// Arrange Cards
			int i;
			for (i=0; i<(idx-1); i++)
				for (j=i+1; j<idx; j++)
					if ( peer->cards[i] > peer->cards[j] ) { uint32_t x=peer->cards[i]; peer->cards[i] = peer->cards[j]; peer->cards[j] = x; }
			break;


		case TYPE_KEEPALIVE:
			// Check Peer
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			// Check Status
			if (IS_DISABLED(peer->flags)) break;
			if (!peer->nbcards) break;
			// Send Reply
			buf[0] = TYPE_KEEPALIVE_ACK;
			sendtopeer( peer, buf, received);
			break;


		case TYPE_KEEPALIVE_ACK:
			// Check Peer
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			// Check Status
			if (IS_DISABLED(peer->flags)) break;
			//
			peer->lastpingrecv = GetTickCount();

			if (peer->ping>0)
				peer->ping = (peer->ping+peer->lastpingrecv-peer->lastpingsent)/2;
			else
				peer->ping = peer->lastpingrecv-peer->lastpingsent;
			peer->ping++;
			break;


		case TYPE_SMS:
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			if (received<6) break;
			uint32_t hash = (buf[1]<<24) | (buf[2]<<16) | (buf[3]<<8) | (buf[4]);
			buf[received] = 0;
			//
			peer_check_messages( peer );
			// Create data
			struct sms_data *sms = malloc( sizeof(struct sms_data) );
			stringtohtml( buf+5, sms->msg);
//			strcpy( sms->msg, (char*)buf+5);
			sms->hash = hash;
			sms->status = 0; // bit 0 (0:in,1:out) bit 1 (0:unread/unAck, 1:read/ack)
			time (&sms->rawtime);
			sms->next = peer->sms;
			peer->sms = sms;

			// SEND ACK
			buf[0] = TYPE_SMS_ACK;
			sendtopeer( peer, buf, 5 );
			// debug
			debugf(getdbgflag(DBG_CACHE,0,0)," cache: SMS from peer (%s:%d)\n", peer->host->name, peer->port);
			// print to sms file
			save_sms( sms, peer);
			break;


		case TYPE_SMS_ACK:
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (!peer) break;
			if (received!=5) break;
			if (!peer->sms) break;
			hash = (buf[1]<<24) | (buf[2]<<16) | (buf[3]<<8) | (buf[4]);
			// Search for data
			sms = peer->sms;
			while (sms) {
				if ( (sms->hash==hash)&&(sms->status==1) ) {
					debugf(getdbgflag(DBG_CACHE,0,0)," cache: SMS ACK from peer (%s:%d)\n", peer->host->name, peer->port);
					sms->status = 3;
				}
				sms = sms->next;
			}
			break;
#endif


		case TYPE_VERSION:
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if (peer) {
				buf[0] = TYPE_VERSION_ACK;
				buf[1] = 'r';
				buf[2] = '0'+(REVISION/10);
				buf[3] = '0'+(REVISION%10);
				sendtopeer( peer, buf, 4 );
			}
			break;

		case TYPE_EXTENDED:
			if (buf[1]=='V') { // Version
				buf[0] = TYPE_EXTENDED_ACK;
				buf[2] = 'r';
				buf[3] = '0'+(REVISION/10);
				buf[4] = '0'+(REVISION%10);
				sendtoip( cache->handle, recv_ip, recv_port, buf, 5);
			}
			break;

		case TYPE_UNKNOWN:
			break;

		default:
			if (received>100) array2hex( buf, str, 100); else array2hex( buf, str, received);
			debugf(getdbgflag(DBG_CACHE,0,0)," cache: Unknown message from %s (%d) : %s\n", ip2string(recv_ip), received, str );
#ifdef NEWCACHE
			peer = getpeerbyaddr(cache, recv_ip,recv_port);
			if ( peer && (peer->protocol&1) ) {
				buf[1] = buf[0];
				buf[0] = TYPE_UNKNOWN;
				sendtopeer( peer, buf, 2 );
			}
#endif
			break;

	}
}


void cache_pipe_recvmsg()
{
	uint8_t buf[64];
	uint8_t cw[16];
	struct cache_data req;
	struct cache_data *pcache;

	int len =  pipe_recv( prg.pipe.cache[0], buf );
	if (len<=0) return;

	switch (buf[0]) {

		case PIPE_CACHE_FIND:
			get_ecm2cache(buf , &req, req.prevcw);
			//debugf(0, " Get PIPE_CACHE_FIND: %04x:%06x:%04x:%08x\n", req.caid, req.provid, req.sid, req.hash);

			pcache = cache_fetch( &req );
			if (pcache==NULL) {
				pcache = cache_new( &req );
				pcache->ecm = req.ecm;
				pcache->cwcycle = req.cwcycle;

#ifdef CACHEEX
				if (len==16+sizeof(void*)+16+16) memcpy( pcache->prevcw, req.prevcw, 16);
				memcpy( pcache->ecmd5, req.ecmd5, 16 );
#else
				if (len==16+sizeof(void*)+16) memcpy( pcache->prevcw, req.prevcw, 16);
#endif
				pcache->flags |= CACHE_FLAG_SENDPIPE;
				// Send find failed
				pipe_cache2ecm_find_failed(pcache);
			}
			else {
				pcache->ecm = req.ecm;
				pcache->tag = req.tag; // set tag if not set (coming from cahceex)
				pcache->provid = req.provid; // set provid if not set (coming from csp)
				pcache->cwcycle = req.cwcycle;
				pcache->provid = req.provid;
#ifdef CACHEEX
				if (len==16+sizeof(void*)+16+16) memcpy( pcache->prevcw, req.prevcw, 16);
				memcpy( pcache->ecmd5, req.ecmd5, 16 );
#else
				if (len==16+sizeof(void*)+16) memcpy( pcache->prevcw, req.prevcw, 16);
#endif
				pcache->flags |= CACHE_FLAG_SENDPIPE;


				// Check stored cw with status = 0, look for same cycle
				if ( !isnullDCW(pcache->prevcw) ) {

					// look for consecutif cw & same cycle
					struct cw_cache_data *cwdata = pcache->cwdata;
					while (cwdata) {
						if ( !(cwdata->status&DCW_ERROR) && !(cwdata->status&DCW_SENT) )
						if ( (cwdata->status&DCW_CYCLE)&&(cwdata->nbpeers>=cfg.cache.threshold) ) {
							if ( pcache->cwcycle==cwdata->cwcycle ) {
								if (  ( (pcache->cwcycle==CW1CYCLE) && dcwcmp8(pcache->prevcw,cwdata->cw) && !similarcw(pcache->prevcw+8,cwdata->cw+8) ) ||
									( (pcache->cwcycle==CW0CYCLE) && !similarcw(pcache->prevcw,cwdata->cw) && dcwcmp8(pcache->prevcw+8,cwdata->cw+8) )  ) {
										cwdata->status |= DCW_SENT;
										pipe_cache2ecm_find_success(pcache, cwdata->cw, cwdata->peerid );
								}
							}
						}
						cwdata = cwdata->next;
					}

					// look for consecutif cw only
					cwdata = pcache->cwdata;
					while (cwdata) {
						if ( !(cwdata->status&DCW_ERROR) && !(cwdata->status&DCW_SENT) )
						if ( !(cwdata->status&DCW_CYCLE) && (cwdata->nbpeers>=cfg.cache.threshold) ) {
							if (  ( (pcache->cwcycle==CW1CYCLE) && dcwcmp8(pcache->prevcw,cwdata->cw) && !similarcw(pcache->prevcw+8,cwdata->cw+8) ) ||
								( (pcache->cwcycle==CW0CYCLE) && !similarcw(pcache->prevcw,cwdata->cw) && dcwcmp8(pcache->prevcw+8,cwdata->cw+8) )  ) {
									cwdata->status |= DCW_SENT;
									pipe_cache2ecm_find_success(pcache, cwdata->cw, cwdata->peerid );
							}
						}
						cwdata = cwdata->next;
					}

				}

				else { // NO PREVIOUS CW

					// cached cw with cycle
					struct cw_cache_data *cwdata = pcache->cwdata;
					while (cwdata) {
						if ( !(cwdata->status&DCW_ERROR) && !(cwdata->status&DCW_SENT) )
						if ( (cwdata->status&DCW_CYCLE)&&(cwdata->nbpeers>=cfg.cache.threshold) ) {
							if ( (pcache->cwcycle==NO_CYCLE)||(pcache->cwcycle==cwdata->cwcycle) ) {
								cwdata->status |= DCW_SENT;
								pipe_cache2ecm_find_success(pcache, cwdata->cw, cwdata->peerid );
							}
						}
						cwdata = cwdata->next;
					}

					// no cycle for cached cw
					cwdata = pcache->cwdata;
					while (cwdata) {
						if ( !(cwdata->status&DCW_ERROR) && !(cwdata->status&DCW_SENT) )
						if ( !(cwdata->status&DCW_CYCLE) && (cwdata->nbpeers>=5) ) {
							cwdata->status |= DCW_SENT;
							pipe_cache2ecm_find_success(pcache, cwdata->cw, cwdata->peerid );
						}
						cwdata = cwdata->next;
					}

				}

			}
			break;


		case PIPE_CACHE_REQUEST:
			get_ecm2cache(buf , &req, NULL);
			//debugf(0, " Get PIPE_CACHE_REQUEST: %04x:%06x:%04x:%08x\n", req.caid, req.provid, req.sid, req.hash);
			pcache = cache_fetch( &req );
			if (pcache==NULL) pcache = cache_new( &req );
			else {
				pcache->tag = req.tag; // set tag if not set (coming from cahceex)
				pcache->provid = req.provid; // set provid if not set (coming from csp)
			}
			pcache->ecm = req.ecm;
			pcache->flags |= CACHE_FLAG_SENDPIPE;
			// Send Request if not dcw sent
			if (!(pcache->flags&CACHE_FLAG_REQSENT)) {
				pcache->flags |= CACHE_FLAG_REQSENT;
				cfg.cache.req++;
				cache_send_request(pcache,NULL);
			}
			break;


		case PIPE_CACHE_REPLY:
			get_ecm2cache(buf , &req, cw);
			//debugf(0, " Get PIPE_CACHE_REPLY: %04x:%06x:%04x:%08x\n", req.caid, req.provid, req.sid, req.hash);

			pcache = cache_fetch( &req );
			if (pcache==NULL) pcache = cache_new( &req );
			else {
				pcache->tag = req.tag; // set tag if not set (coming from cahceex)
				pcache->provid = req.provid; // set provid if not set (coming from csp)
			}
			//Check & update DCW
			struct cw_cache_data *cwdata = pcache->cwdata;
			while (cwdata) {
				if ( dcwcmp16(cwdata->cw, cw) ) break;
				cwdata = cwdata->next;
			}
			// ADD if not found
			if (!cwdata) {
				struct cw_cache_data *cwdata = malloc( sizeof(struct cw_cache_data) );
				memset( cwdata, 0, sizeof(struct cw_cache_data) );
				memcpy(cwdata->cw, cw, 16);
				//cwdata->cwcycle = NO_CYCLE;
				//cwdata->peerid = peerid;
				cwdata->next = pcache->cwdata;
				pcache->cwdata = cwdata;
				if (cfg.cache.dcwcheck2) cache_fetch_goodcw2(pcache, cw, 0);
				if (cfg.cache.dcwcheck3) cache_fetch_goodcw3(pcache, cw, 0);
			}
			//cwdata->status |= DCW_CYCLE;

			// Send Reply
			if ( !(pcache->flags&CACHE_FLAG_REPSENT) ) cfg.cache.rep++;
			pcache->flags |= CACHE_FLAG_REPSENT;
			cache_send_reply(pcache, NULL, cw);
			break;

		case PIPE_CACHE_RESENDREQ:
			get_ecm2cache(buf , &req, NULL);
/*
			pcache = cache_fetch( &req );
			if (pcache==NULL) pcache = cache_new( &req );
			else {
				struct cw_cache_data *cwdata = pcache->cwdata;
				while (cwdata) {
					pipe_cache2ecm_find_success(pcache, cwdata->cw, cwdata->peerid );
					cwdata = cwdata->next;
				}
			}
*/
			cache_send_resendreq(&req);
			break;

	}
}


void cache_check_peers(struct cacheserver_data *cache)
{
	struct cachepeer_data *peer = cache->peer;
	while (peer) {
		if (!IS_DISABLED(peer->flags))
		if ( (peer->host->ip)&&(peer->port) ) {
			uint32_t ticks = GetTickCount();
			if (peer->ping==0) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+9000)<ticks) ) { // send every 15s
					cache_send_ping(cache, peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->ping = -1;
				}
			}
			else if (peer->ping==-1) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+19000)<ticks) ) { // send every 15s
					cache_send_ping(cache, peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->ping = -2;
				}
			}
			else if (peer->ping==-2) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+29000)<ticks) ) { // send every 15s
					cache_send_ping(cache, peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->ping = -3;
				}
			}
			else if (peer->ping<=-3) { // inactive
				if ( (!peer->lastpingsent)||((peer->lastpingsent+59000)<ticks) ) { // send every 15s
					cache_send_ping(cache, peer);
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
				}
			}
			else if (peer->ping>0) {
				if ( (!peer->lastpingrecv)&&((peer->lastpingsent+9000)<ticks) ) {
					if (peer->lastpingnb==1) {
#ifdef NEWCACHE
						cache_send_keepalive(cache, peer);
#else
						cache_send_ping(cache, peer);
#endif
						peer->lastpingsent = ticks;
						peer->lastpingnb = 2;
					}
					else if (peer->lastpingnb==2) {
#ifdef NEWCACHE
						cache_send_keepalive(cache, peer);
#else
						cache_send_ping(cache, peer);
#endif
						peer->lastpingsent = ticks;
						peer->lastpingnb = 3;
					}
					else if (peer->lastpingnb==3) {
						cache_send_ping(cache, peer);
						peer->lastpingsent = ticks;
						peer->lastpingrecv = 0;
						peer->ping = 0;
						peer->host->checkiptime = 0; // maybe ip changed
						//ipeer_update(cache);
					}
				}
				else if ( (peer->lastpingsent+60000)<ticks ) { // send every 75s
#ifdef NEWCACHE
					cache_send_keepalive(cache, peer);
#else
					cache_send_ping(cache, peer);
#endif
					peer->lastpingsent = ticks;
					peer->lastpingrecv = 0;
					peer->lastpingnb = 1;
				}
			}
		}
		peer = peer->next;
	}
}


///////////////////////////////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////////////////////////////

#ifdef EPOLL_CACHE


void *cache_thread(void *param)
{
	prg.pid_cache = syscall(SYS_gettid);
	prg.tid_cache = pthread_self();
	prctl(PR_SET_NAME,"Cache RecvMSG Thread",0,0,0);

	sleep(3);

	int i;
	struct epoll_event evlist[MAX_EPOLL_EVENTS]; // epoll recv events

	uint32_t chkticks = 0;
	while (!prg.restart) {
		// Check Peers Ping
		if ( GetTickCount()>(chkticks+5000) ) {
			struct cacheserver_data *cache = cfg.cache.server;
			while (cache) {
				cache_check_peers(cache);
				cache = cache->next;
			}
			chkticks = GetTickCount();
		}

		int ready = epoll_wait( prg.epoll.cache, evlist, MAX_EPOLL_EVENTS, 1001);
		if (ready == -1) {
			if ( (errno==EINTR)||(errno==EAGAIN) ) {
				usleep(1000);
				continue;
			}
			else {
				usleep(99000);
				debugf(DBG_ERROR,"Err! epoll_wait (%d)", errno);
			}
		}
		else if (ready==0) continue; // timeout

		for (i=0; i < ready; i++) {
			if ( evlist[i].events & (EPOLLIN|EPOLLPRI) ) cache_recvmsg(evlist[i].data.ptr);
		}
	}
	return NULL;
}

#else

void *cache_thread(void *param)
{
	prg.pid_cache = syscall(SYS_gettid);
	prg.tid_cache = pthread_self();
	prctl(PR_SET_NAME,"Cache RecvMSG Thread",0,0,0);

	uint32_t chkticks = 0;
	while (!prg.restart) {
		// Check Peers Ping
		if ( GetTickCount()>(chkticks+3000) ) {
			struct cacheserver_data *cache = cfg.cache.server;
			while (cache) {
				cache_check_peers(cache);
				cache = cache->next;
			}
			chkticks = GetTickCount();
		}

		struct pollfd pfd[100];
		int pfdcount = 0;
#ifndef THREAD_CACHE_PIPE
		pfd[pfdcount].fd = prg.pipe.cache[0];
		pfd[pfdcount].events = POLLIN | POLLPRI;
		pfdcount++;
#endif
		struct cacheserver_data *cache = cfg.cache.server;
		while (cache) {
			if (cache->handle>0) {
				cache->ipoll = pfdcount;
				pfd[pfdcount].fd = cache->handle;
				pfd[pfdcount++].events = POLLIN | POLLPRI;
			} else cache->ipoll = -1;
			cache = cache->next;
		}

		int retval = poll(pfd, pfdcount, 3001);

		if ( retval>0 ) {
#ifndef THREAD_CACHE_PIPE
			if ( pfd[0].revents & (POLLIN|POLLPRI) ) {
				pthread_mutex_lock( &prg.lockcache );
				cache_pipe_recvmsg();
				pthread_mutex_unlock( &prg.lockcache );
			}
#endif
			struct cacheserver_data *cache = cfg.cache.server;
			while (cache) {
				if ( (cache->handle>0)&&(cache->ipoll>=0)&&(cache->handle==pfd[cache->ipoll].fd) )
				if ( pfd[cache->ipoll].revents & (POLLIN|POLLPRI) ) {
					//pthread_mutex_lock( &prg.lockcache );
					cache_recvmsg(cache);
					//pthread_mutex_unlock( &prg.lockcache );
				}
				cache = cache->next;
			}
		} else usleep( 99000 );
	}

	//close(cfg.cache.handle);
	return NULL;
}

#endif


///////////////////////////////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////////////////////////////
#ifdef THREAD_CACHE_PIPE

void *cache_pipe_thread(void *param)
{
	prg.pid_cache_pipe = syscall(SYS_gettid);
	prg.tid_cache_pipe = pthread_self();
	prctl(PR_SET_NAME,"Cache Pipe Thread",0,0,0);

	while (!prg.restart) {
		struct pollfd pfd;
		pfd.fd = prg.pipe.cache[0];
		pfd.events = POLLIN | POLLPRI;
		int retval = poll(&pfd, 1, 3031);
		if ( retval>0 ) {
			pthread_mutex_lock( &prg.lockcache );
			cache_pipe_recvmsg();
			pthread_mutex_unlock( &prg.lockcache );
		}
		else usleep( 99000 );
	}
	return NULL;
}

#endif
///////////////////////////////////////////////////////////////////////////////
// 
///////////////////////////////////////////////////////////////////////////////

int start_thread_cache()
{
#ifdef THREAD_CACHE_PIPE
	create_thread(&prg.tid_cache, (threadfn)cache_pipe_thread,NULL);
#endif

	create_thread(&prg.tid_cache, (threadfn)cache_thread,NULL);
	return 0;
}