./ MultiCS.r82 / cacheex.c
///////////////////////////////////////////////////////////////////////////////
// TOOL
///////////////////////////////////////////////////////////////////////////////


int acceptshare( struct sharelimit_data sharelimits[100], uint16_t caid, uint32_t provid)
{
	int i;
	if (sharelimits[0].caid==0xffff) return 1;
	for (i=0; i<100; i++) {
		if (sharelimits[i].caid==0xffff) break;
		if (sharelimits[i].caid==caid) {
			if (sharelimits[i].provid==provid) return 1;
			else if (sharelimits[i].provid==0xFFFFFF) return 1;
		}
	}
	return 0;
}


///////////////////////////////////////////////////////////////////////////////
// TOOL
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// pipe --> cacheex
///////////////////////////////////////////////////////////////////////////////

int pipe_send_cacheex_push_out(ECM_DATA *ecm)
{
	uint8_t buf[128];
	buf[0] = PIPE_CACHEEX_PUSH_LOCAL;
	buf[1] = (ecm->caid)>>8; buf[2] = (ecm->caid)&0xff;
	buf[3] = ecm->ecm[0];
	buf[4] = ecm->provid>>16; buf[5] = ecm->provid>>8; buf[6] = ecm->provid & 0xff;
	buf[7] = (ecm->sid)>>8; buf[8] = (ecm->sid)&0xff;
	buf[9] = ecm->hash>>24; buf[10] = ecm->hash>>16; buf[11] = ecm->hash>>8; buf[12] = ecm->hash & 0xff;
	memcpy(buf+13, ecm->ecmd5, 16);
	memcpy(buf+29, ecm->cw, 16);
	pipe_send( prg.pipe.cacheex[1], buf, 45);
	return 1;
}

int pipe_send_cacheex_push_cache(struct cache_data *pcache, uint8_t *cw, uint8_t *nodeid)
{
	uint8_t buf[128];
	buf[0] = PIPE_CACHEEX_PUSH_REMOTE;
	buf[1] = (pcache->caid)>>8; buf[2] = (pcache->caid)&0xff;
	buf[3] = pcache->tag;
	buf[4] = pcache->provid>>16; buf[5] = pcache->provid>>8; buf[6] = pcache->provid & 0xff;
	buf[7] = (pcache->sid)>>8; buf[8] = (pcache->sid)&0xff;
	buf[9] = pcache->hash>>24; buf[10] = pcache->hash>>16; buf[11] = pcache->hash>>8; buf[12] = pcache->hash & 0xff;
	memcpy(buf+13, pcache->ecmd5, 16);
	memcpy(buf+29, cw, 16);
	memcpy( buf+29+16, nodeid, 8);
	pipe_send( prg.pipe.cacheex[1], buf, 29+16+8);
	return 1;
}

int pipe_send_cacheex_push_in(struct cache_data *pcache, uint8_t cw[16], int clid, int hop)
{
	uint8_t buf[128];
	buf[0] = PIPE_CACHEEX_PUSH_IN;
	buf[1] = (pcache->caid)>>8; buf[2] = (pcache->caid)&0xff;
	buf[3] = pcache->tag;
	buf[4] = pcache->provid>>16; buf[5] = pcache->provid>>8; buf[6] = pcache->provid & 0xff;
	buf[7] = (pcache->sid)>>8; buf[8] = (pcache->sid)&0xff;
	buf[9] = pcache->hash>>24; buf[10] = pcache->hash>>16; buf[11] = pcache->hash>>8; buf[12] = pcache->hash & 0xff;
	memcpy(buf+13, pcache->ecmd5, 16);
	memcpy(buf+29, cw, 16);
	buf[45] = clid>>8;
	buf[46] = clid;
	buf[47] = hop;
	pipe_send( prg.pipe.cacheex[1], buf, 48);
	return 1;
}

///////////////////////////////////////////////////////////////////////////////
// PUSH OUT 
///////////////////////////////////////////////////////////////////////////////

int get_cccam_cacheex_push(struct cache_data *pcache, uint8_t cw[16], uint8_t *buf, uint8_t *nodeid )
{
	memset(buf, 0, 57+8 );

	buf[0] = pcache->caid>>8;
	buf[1] = pcache->caid;

	buf[2] = pcache->provid>>24;
	buf[3] = pcache->provid>>16;
	buf[4] = pcache->provid>>8;
	buf[5] = pcache->provid;

	buf[10] = pcache->sid>>8;
	buf[11] = pcache->sid;

	buf[12] = 0x24; // ( 16 + 4 + 16 )
	buf[13] = 0;

	buf[14] = 0;

	buf[19] = pcache->tag;

	memcpy(buf+20, pcache->ecmd5, 16);
	buf[36] = pcache->hash;
	buf[37] = pcache->hash >> 8;
	buf[38] = pcache->hash >> 16;
	buf[39] = pcache->hash >> 24; 
	memcpy(buf+40, cw, 16);
	if (nodeid) {
		buf[56] = 2;
		memcpy(buf+57, cccam_nodeid, 8 );
		memcpy(buf+57+8, nodeid, 8 );
		return 57+8+8;
	}
	buf[56] = 1; // Uphops = 1 (local)
	memcpy(buf+57, cccam_nodeid, 8 );
	return 57+8;
}

int get_camd35_cacheex_push(struct cache_data *pcache, uint8_t cw[16], uint8_t *buf, uint8_t *nodeid )
{
	memset(buf, 0, 57+8 );
	buf[0] = 0x3F;
	buf[1] = 57+8-20;

	buf[8] = pcache->sid>>8;
	buf[9] = pcache->sid;

	buf[10] = pcache->caid>>8;
	buf[11] = pcache->caid;

	buf[12] = pcache->provid>>24;
	buf[13] = pcache->provid>>16;
	buf[14] = pcache->provid>>8;
	buf[15] = pcache->provid;

	buf[19] = pcache->tag;

	memcpy(buf+20, pcache->ecmd5, 16);
	buf[36] = pcache->hash;
	buf[37] = pcache->hash >> 8;
	buf[38] = pcache->hash >> 16;
	buf[39] = pcache->hash >> 24; 
	memcpy(buf+40, cw, 16);
	if (nodeid) {
		buf[1] += 8;
		buf[56] = 2;
		memcpy(buf+57, cccam_nodeid, 8 );
		memcpy(buf+57+8, nodeid, 8 );
		return 57+8+8;
	}
	buf[56] = 1; // Uphops = 1 (local)
	memcpy(buf+57, cccam_nodeid, 8 );
	return 57+8;
}

void cacheex_push(struct cache_data *pcache, uint8_t cw[16], uint8_t *nodeid )
{
	uint8_t camd35buf[128];
	int camd35len = get_camd35_cacheex_push( pcache, cw, camd35buf, nodeid );
	uint8_t cccambuf[128];
	int cccamlen = get_cccam_cacheex_push( pcache, cw, cccambuf, nodeid );

	// PUSH TO SERVERS cacheex=3
	struct server_data *srv = cfg.cacheexserver;
	while (srv) {
		//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," CACHEEX PUSH to server (%s:%d) %04x:%06x:%04x:%08x\n",srv->host->name, srv->port,pcache->caid,pcache->provid,pcache->sid,pcache->hash); debughex(cw,16);
		if ( (srv->cacheex_mode==3) && (srv->connection.status>0) )
		if ( acceptshare(srv->sharelimits, pcache->caid, pcache->provid) ) {
			if (srv->type==TYPE_CCCAM) {
				if ( !cc_msg_send( srv->handle, &srv->sendblock, CC_MSG_CACHE_PUSH, cccamlen, cccambuf) ) disconnect_srv(srv);
			}
#ifdef CAMD35_CLI
			else if (srv->type==TYPE_CAMD35) camd35_sendto( srv->handle, srv->host->ip, srv->port, &srv->encryptkey, srv->ucrc, camd35buf, camd35len);
#endif
#ifdef CS378X_CLI
			else if (srv->type==TYPE_CS378X) {
				if ( !cs378x_send( srv->handle, &srv->encryptkey, srv->ucrc, camd35buf, camd35len) )  disconnect_srv(srv);
			}
#endif
			srv->cacheex.push[0]++;
			if (nodeid) srv->cacheex.push[2]++; else srv->cacheex.push[1]++;
		}
		srv = srv->next;
	}

#ifdef CCCAM_SRV
	// PUSH TO CCCAM CLIENTS cacheex=2
	struct cccam_server_data *cccam = cfg.cccam.server;
	while (cccam) {
		struct cc_client_data *cli = cccam->cacheexclient;
		while (cli) {
			if ( (cli->cacheex_mode==2) && (cli->connection.status>0) )
			if ( acceptshare(cli->sharelimits, pcache->caid, pcache->provid) ) {
				if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_CACHE_PUSH, cccamlen, cccambuf) ) cc_disconnect_cli(cli);
				cli->cacheex.push[0]++;
				if (nodeid) cli->cacheex.push[2]++; else cli->cacheex.push[1]++;
				//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," CACHEEX PUSH to client %04x:%06x:%04x:%08x\n",pcache->caid,pcache->provid,pcache->sid,pcache->hash);// debughex(req.cw,16);
			}
			cli = cli->next;
		}
		cccam = cccam->next;
	}
#endif

#ifdef CAMD35_SRV
	// PUSH TO CAMD35 CLIENTS cacheex=2
	struct camd35_server_data *camd35 = cfg.camd35.server;
	while (camd35) {
		struct camd35_client_data *cli = camd35->cacheexclient;
		while (cli) {
			if ( (cli->cacheex_mode==2) && (cli->connection.status>0) )
			if ( !nodeid || memcmp(nodeid, cli->nodeid, 8) )
			if ( acceptshare(cli->sharelimits, pcache->caid, pcache->provid) ) {
				camd35_sendto( camd35->handle, cli->ip, cli->port, &cli->encryptkey, cli->ucrc, camd35buf, camd35len);
				cli->cacheex.push[0]++;
				if (nodeid) cli->cacheex.push[2]++; else cli->cacheex.push[1]++;
				//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," CACHEEX PUSH to client %04x:%06x:%04x:%08x\n",pcache->caid,pcache->provid,pcache->sid,pcache->hash);// debughex(req.cw,16);
			}
			cli = cli->next;
		}
		camd35 = camd35->next;
	}
#endif

#ifdef CS378X_SRV
	// PUSH TO CS378X CLIENTS cacheex=2
	struct camd35_server_data *cs378x = cfg.cs378x.server;
	while (cs378x) {
		struct camd35_client_data *cli = cs378x->cacheexclient;
		while (cli) {
			if ( (cli->cacheex_mode==2) && (cli->connection.status>0) )
			if ( !nodeid || memcmp(nodeid, cli->nodeid, 8) )
			if ( acceptshare(cli->sharelimits, pcache->caid, pcache->provid) ) {
				if ( !cs378x_send( cli->handle, &cli->encryptkey, cli->ucrc, camd35buf, camd35len) ) cs378x_disconnect_cli(cli);
				cli->cacheex.push[0]++;
				if (nodeid) cli->cacheex.push[2]++; else cli->cacheex.push[1]++;
				//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," CACHEEX PUSH to client %04x:%06x:%04x:%08x\n",pcache->caid,pcache->provid,pcache->sid,pcache->hash);// debughex(req.cw,16);
			}
			cli = cli->next;
		}
		cs378x = cs378x->next;
	}
#endif

}



void cacheex_pipe_recvmsg()
{
	uint8_t buf[512];
	uint8_t cw[16];
	struct cache_data req;
	struct pollfd pfd;

	do {
		int len =  pipe_recv( prg.pipe.cacheex[0], buf );
		if (len>0) {
			//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," Recv from CacheEX Pipe\n"); debughex(buf,len);
			switch(buf[0]) {
				case PIPE_WAKEUP:  // ADD NEW CLIENT
					break;

				case PIPE_CACHEEX_PUSH_LOCAL: // from local
					// Setup Cache Request
					req.caid = (buf[1]<<8) | buf[2];
					req.tag = buf[3];
					req.provid = (buf[4]<<16) | (buf[5]<<8) | (buf[6]);
					req.sid = (buf[7]<<8) | buf[8];
					req.hash = (buf[9]<<24) | (buf[10]<<16) | (buf[11]<<8) | (buf[12]);
					memcpy( req.ecmd5, buf+13, 16);
					memcpy( cw, buf+29, 16);
					//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," PIPE_CACHEEX_PUSH_OUT %04x:%06x:%04x:%08x\n", req.caid,req.provid,req.sid,req.hash);
					// Send Reply
					cfg.cacheex.rep++;
					cacheex_push( &req, cw, NULL );
					break;

				case PIPE_CACHEEX_PUSH_REMOTE: // from cacheex peers
					// Setup Cache Request
					req.caid = (buf[1]<<8) | buf[2];
					req.tag = buf[3];
					req.provid = (buf[4]<<16) | (buf[5]<<8) | (buf[6]);
					req.sid = (buf[7]<<8) | buf[8];
					req.hash = (buf[9]<<24) | (buf[10]<<16) | (buf[11]<<8) | (buf[12]);
					memcpy( req.ecmd5, buf+13, 16);
					memcpy( cw, buf+29, 16);
					//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," PIPE_CACHEEX_PUSH_REMOTE %04x:%06x:%04x:%08x\n", req.caid,req.provid,req.sid,req.hash);
					// Send Reply
					cfg.cacheex.rep++;
					cacheex_push( &req, cw, buf+29+16 );
					break;

			}//switch
		}//if

		pfd.fd = prg.pipe.cacheex[0];
		pfd.events = POLLIN | POLLPRI;
	} while ( poll(&pfd, 1, 1)>0 );
}

/*
1 for server>clients
1 for client>servers
*/
///////////////////////////////////////////////////////////////////////////////

int cacheex_check( struct cache_data *req )
{
	if ( !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;
	}
	return 1;
}

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

void *cacheex_recvmsg_thread(void *param)
{
	prg.pid_ccex_msg = syscall(SYS_gettid);

	struct pollfd pfd[CACHEEX_MAX_PFD];
	int pfdcount = 0;
	while(1) {
		pfdcount = 0;

		// Clients mode 2
		struct cccam_server_data *cccam = cfg.cccam.server;
		while (cccam) {
			if ( !IS_DISABLED(cccam->flags)&&(cccam->handle>0) ) {
				struct cc_client_data *cccli = cccam->cacheexclient;
				while (cccli) {
					if (cccli->cacheex_mode==2) {
						if ( !IS_DISABLED(cccli->flags)&&(cccli->handle>0) ) {
							cccli->ipoll = pfdcount;
							pfd[pfdcount].fd = cccli->handle;
							pfd[pfdcount++].events = POLLIN | POLLPRI;
						} else cccli->ipoll = -1;
					}
					cccli = cccli->next;
				}
			}
			cccam = cccam->next;
		}

		// Servers mode 3
		struct server_data *srv = cfg.cacheexserver;
		while (srv) {
			if ( (srv->cacheex_mode==3) && !IS_DISABLED(srv->flags) && (srv->handle>0) ) {
				srv->ipoll = pfdcount;
				pfd[pfdcount].fd = srv->handle;
				pfd[pfdcount++].events = POLLIN | POLLPRI;
			} else srv->ipoll = -1;
			srv = srv->next;
		}

		int retval = poll(pfd, pfdcount, 3005); // for 3seconds

		if ( retval>0 ) {
			if (cfg.delay.thread) usleep(cfg.delay.thread);

			// CCcam Clients - cacheex_mode = 3
			struct cccam_server_data *cccam = cfg.cccam.server;
			while (cccam) {
				if ( !IS_DISABLED(cccam->flags)&&(cccam->handle>0) ) {
					//pthread_mutex_lock(&prg.lockcccli);
					struct cc_client_data *cccli = cccam->cacheexclient;
					while (cccli) {
						if (cccli->cacheex_mode==2) {
							if ( !IS_DISABLED(cccli->flags)&&(cccli->handle>0)&&(cccli->ipoll>=0)&&(cccli->handle==pfd[cccli->ipoll].fd) ) {
								if ( pfd[cccli->ipoll].revents & (POLLHUP|POLLNVAL) ) cc_disconnect_cli(cccli);
								else if ( pfd[cccli->ipoll].revents & (POLLIN|POLLPRI) ) cc_cli_recvmsg(cccli);
							}
						}
						cccli = cccli->next;
					}
					//pthread_mutex_unlock(&prg.lockcccli);
				}
				cccam = cccam->next;
			}

			//Servers
			struct server_data *srv = cfg.cacheexserver;
			while (srv) {
				if ( (srv->cacheex_mode==3) && !IS_DISABLED(srv->flags) && (srv->handle>0) )
				if ( (srv->ipoll>=0)&&(srv->handle==pfd[srv->ipoll].fd) ) {
					if ( pfd[srv->ipoll].revents & (POLLIN|POLLPRI) ) {
						if (srv->type==TYPE_CCCAM) cc_srv_recvmsg(srv);
#ifdef CAMD35_CLI
						else if (srv->type==TYPE_CAMD35) camd35_srv_recvmsg(srv);
#endif
#ifdef CS378X_CLI
						else if (srv->type==TYPE_CS378X) cs378x_srv_recvmsg(srv);
#endif
					}
				}
				srv = srv->next;
			}

		}
		else if ( retval<0 ) {
			debugf(0, " thread receive messages: poll error %d(errno=%d)\n", retval, errno);
		}

	}
	return NULL;
}

void *cacheex_pipe_thread(void *param)
{
	struct pollfd pfd;
	while(1) {
		pfd.fd = prg.pipe.cacheex[0];
		pfd.events = POLLIN | POLLPRI;

		int retval = poll(&pfd, 1, 3015); // for 3seconds

		if ( retval>0 ) {
			cacheex_pipe_recvmsg();
		}
		else if ( retval<0 ) {
			debugf(0, " thread receive messages: poll error %d(errno=%d)\n", retval, errno);
		}

	}
	return NULL;
}

int start_thread_cacheex()
{
	//memset( icacheextab, 0, sizeof(icacheextab));
	//create_thread(&prg.tid_cacheex, (threadfn)cacheex_send_thread,NULL);
	create_thread(&prg.tid_cacheex, cacheex_recvmsg_thread,NULL);
	create_thread(&prg.tid_cacheex, cacheex_pipe_thread,NULL);
	return 0;
}