./ MultiCS.r82 / srv-cccam.c
///////////////////////////////////////////////////////////////////////////////
// File: srv-cccam.c
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
// PROTO
///////////////////////////////////////////////////////////////////////////////

void cc_senddcw_cli(struct cc_client_data *cli);


struct cccam_server_data *getcccamserverbyid(uint32_t id)
{
	struct cccam_server_data *cccam = cfg.cccam.server;
	while (cccam) {
		if (!(cccam->flags&FLAG_DELETE))
			if (cccam->id==id) return cccam;
		cccam = cccam->next;
	}
	return NULL;
}

struct cc_client_data *getcccamclientbyid(uint32_t id)
{
	struct cccam_server_data *cccam = cfg.cccam.server;
	while (cccam) {
		if (!(cccam->flags&FLAG_DELETE)) {
			struct cc_client_data *cli = cccam->client;
			while (cli) {
				if (!(cli->flags&FLAG_DELETE))
					if (cli->id==id) return cli;
				cli = cli->next;
			}
			cli = cccam->cacheexclient;
			while (cli) {
				if (!(cli->flags&FLAG_DELETE))
					if (cli->id==id) return cli;
				cli = cli->next;
			}
		}
		cccam = cccam->next;
	}
	return NULL;
}

struct cc_client_data *getcecccamclientbyid(uint32_t id)
{
	struct cccam_server_data *cccam = cfg.cccam.server;
	while (cccam) {
		if (!(cccam->flags&FLAG_DELETE)) {
			struct cc_client_data *cli = cccam->cacheexclient;
			while (cli) {
				if (!(cli->flags&FLAG_DELETE))
					if (cli->id==id) {
						debugf(0, "found cccam cacheex client '%s'\n", cli->user);
						return cli;
					}
				cli = cli->next;
			}
		}
		cccam = cccam->next;
	}
	return NULL;
}

struct cc_client_data *getcccamclientbyname(struct cccam_server_data *cccam, char *name)
{
	if (!(cccam->flags&FLAG_DELETE)) {
		uint32_t hash = hashCode( (unsigned char *)name, strlen(name) );
		struct cc_client_data *cli = cccam->client;
		while (cli) {
			if (!(cli->flags&FLAG_DELETE))
				if (cli->userhash==hash)
					if ( !strcmp(cli->user,name) ) return cli;
			cli = cli->next;
		}
	}
	return NULL;
}

///////////////////////////////////////////////////////////////////////////////
// CCCAM SERVER: DISCONNECT CLIENTS
///////////////////////////////////////////////////////////////////////////////

void cc_disconnect_cli(struct cc_client_data *cli)
{
	cli->connection.status = 0;
	uint32_t ticks = GetTickCount();
	cli->connection.uptime += ticks - cli->connection.time;
	cli->connection.lastseen = ticks; // Last Seen
	close(cli->handle);
	cli->handle = -1;
	cli->parent->clipfd.update = 1;
	debugf(0," CCcam: client '%s' disconnected \n", cli->user);
}


///////////////////////////////////////////////////////////////////////////////
// CCCAM SERVER: CONNECT CLIENTS
///////////////////////////////////////////////////////////////////////////////

unsigned int seed;
uint8_t fast_rnd()
{
  unsigned int offset = 12923;
  unsigned int multiplier = 4079;

  seed = seed * multiplier + offset;
  return (uint8_t)(seed % 0xFF);
}

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

int cc_sendinfo_cli(struct cc_client_data *cli, int sendversion)
{
	uint8_t buf[CC_MAXMSGSIZE];
	memset(buf, 0, CC_MAXMSGSIZE);
	memcpy(buf, cfg.cccam.nodeid, 8 );
	memcpy(buf + 8, cfg.cccam.version, 32);		// cccam version (ascii)
	memcpy(buf + 40, cfg.cccam.build, 32);       // build number (ascii)
	if (sendversion) {
		buf[38] = REVISION >> 8;
		buf[37] = REVISION & 0xff;
		buf[36] = 0;
		buf[35] = 'S';
		buf[34] = 'C';
		buf[33] = 'M';
	}
	//debugdump(cfg.cccam.nodeid,8,"Sending server data version: %s, build: %s nodeid ", cfg.cccam.version, cfg.cccam.build);
	return cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_SRV_INFO, 0x48, buf);
}

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

int cc_sendcard_del(struct cardserver_data *cs, struct cc_client_data *cli)
{
	uint8_t buf[4];
	buf[0] = cs->id >> 24;
	buf[1] = cs->id >> 16;
	buf[2] = cs->id >> 8;
	buf[3] = cs->id & 0xff;
	return cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_CARD_DEL, 4, buf);
}


int cc_sendcard_cli(struct cardserver_data *cs, struct cc_client_data *cli, int uphops)
{
	uint8_t buf[CC_MAXMSGSIZE];
	memset(buf, 0, sizeof(buf));
	buf[0] = cs->id >> 24;
	buf[1] = cs->id >> 16;
	buf[2] = cs->id >> 8;
	buf[3] = cs->id & 0xff;
	buf[4] = cs->id >> 24;
	buf[5] = cs->id >> 16;
	buf[6] = cs->id >> 8;
	buf[7] = cs->id & 0xff;
	buf[8] = cs->card.caid >> 8;
	buf[9] = cs->card.caid & 0xff;
	buf[10] = uphops;
	buf[11] = cli->dnhops; // Dnhops
	//buf[20] = cs->card.nbprov;
	int j;
	int nbprov = 0;
	for (j=0; j<cs->card.nbprov; j++) {
		if ( card_sharelimits(cli->sharelimits, cs->card.caid, cs->card.prov[j].id) ) {
			//memcpy(buf + 21 + (j*7), card->provs[j], 7);
			buf[21+nbprov*7] = 0xff&(cs->card.prov[j].id>>16);
			buf[22+nbprov*7] = 0xff&(cs->card.prov[j].id>>8);
			buf[23+nbprov*7] = 0xff&(cs->card.prov[j].id);
/*
			buf[24+nbprov*7] = 0xff&(cs->card.prov[j].ua>>24);
			buf[25+nbprov*7] = 0xff&(cs->card.prov[j].ua>>16);
			buf[26+nbprov*7] = 0xff&(cs->card.prov[j].ua>>8);
			buf[27+nbprov*7] = 0xff&(cs->card.prov[j].ua);
*/
			nbprov++;
		}
	}
	if (!nbprov) return 0; // Denied
	buf[20] = nbprov;
	buf[21 + (nbprov*7)] = 1;
	memcpy(buf + 22 + (nbprov*7), cfg.cccam.nodeid, 8);
	return cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_CARD_ADD, 30 + (nbprov*7), buf);
}


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

void cc_sendcards_cli(struct cc_client_data *cli)
{
	int nbcard=0;
	struct cardserver_data *cs = cfg.cardserver;

	int i;
	if (cli->csport[0]) {
		for(i=0;i<MAX_CSPORTS;i++) {
			if(cli->csport[i]) {
				cs = getcsbyport(cli->csport[i]);
				if ( cs && cs->option.fsharecccam && !(cli->flags&FLAG_EXPIRED) )
					if (cc_sendcard_cli(cs, cli,0)) nbcard++;
			} else break;
		}
	}
	else if (cfg.cccam.csport[0]) {
		for(i=0;i<MAX_CSPORTS;i++) {
			if(cfg.cccam.csport[i]) {
				cs = getcsbyport(cfg.cccam.csport[i]);
				if ( cs && cs->option.fsharecccam && !(cli->flags&FLAG_EXPIRED) )
					if (cc_sendcard_cli(cs, cli,0)) nbcard++;
			} else break;
		}
	}
	else {
		while (cs) {
			if ( cs->option.fsharecccam && !(cli->flags&FLAG_EXPIRED) )
			if (cc_sendcard_cli(cs, cli,0)) nbcard++;
			cs = cs->next;
		}
	}

	debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam: %d cards --> client(%s)\n",  nbcard, cli->user);
}

///////////////////////////////////////////////////////////////////////////////
void *cacheex_cc_cli_recvmsg(struct cc_client_data *cli);

void *cc_connect_cli(struct connect_cli_data *param)
{
	uint8_t buf[CC_MAXMSGSIZE];
	uint8_t data[64];
	int i;
	struct cc_crypt_block sendblock;	// crypto state block
	struct cc_crypt_block recvblock;	// crypto state block
	char usr[64];
	char pwd[255];
	// Store data from param
	struct cccam_server_data *cccam = param->server;
	int sock = param->sock;
	uint32_t ip = param->ip;
	free(param);

#ifdef IPLIST
	struct ip_hacker_data *ipdata = iplist_find( cccam->iplist, ip );
	if (ipdata) iplist_newlogin( ipdata );
#endif

	//
	struct cc_client_data tmpcli;

	memset(usr, 0, sizeof(usr));
	memset(pwd, 0, sizeof(pwd));

	// create & send random seed
	for(i=0; i<12; i++ ) data[i]=fast_rnd();
	// Create Multics ID
	data[3] = (data[0]^'M') + data[1] + data[2];
	data[7] = data[4] + (data[5]^'C') + data[6];
	data[11] = data[8] + data[9] + (data[10]^'S');
	//Create checksum for "O" cccam:
	for (i = 0; i < 4; i++) {
		data[12 + i] = (data[i] + data[4 + i] + data[8 + i]) & 0xff;
	}
#ifdef DEBUG_NETWORK
	if (flag_debugnet) {
		debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," CCcam%d: Send Random Key 16\n", cccam->id);
		debughex(data, 16);
	}
#endif
	if ( !send_nonb(sock, data, 16, 500) ) {
		close(sock);
		return NULL;
	}
	//XOR init bytes with 'CCcam'
	cc_crypt_xor(data);
#ifdef DEBUG_NETWORK
	if (flag_debugnet) {
		debugf(0, " CCcam: XOR init bytes with 'CCcam'\n");
		debughex(data, 16);
	}
#endif
	//SHA1
	SHA_CTX ctx;
	SHA1_Init(&ctx);
	SHA1_Update(&ctx, data, 16);
	SHA1_Final(buf, &ctx);

	//init crypto states
	cc_crypt_init(&sendblock, buf, 20);
	cc_decrypt(&sendblock, data, 16);
		//debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," CCcam%d: sendblock:cc_crypt_init \n", cccam->id); debughex(sendblock.keytable,256);
	cc_crypt_init(&recvblock, data, 16);
	cc_decrypt(&recvblock, buf, 20);
		//debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," CCcam%d: recvblock:cc_crypt_init \n", cccam->id); debughex(recvblock.keytable,256);

#ifdef DEBUG_NETWORK
	if (flag_debugnet) {
		debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," CCcam: SHA1 hash\n");
		debughex(buf,20);
	}
#endif
	memcpy(usr,buf,20);
	if ((i=recv_nonb(sock, buf, 20,5000)) == 20) {
#ifdef DEBUG_NETWORK
		if (flag_debugnet) {
			debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," CCcam%d: receive SHA1 hash %d\n", cccam->id, i);
			debughex(buf,i);
		}
#endif
		cc_decrypt(&recvblock, buf, 20);
#ifdef DEBUG_NETWORK
		if (flag_debugnet) {
			debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," Decrypted SHA1 hash (20):\n");
			debughex(buf,20);
		}
#endif
		if ( memcmp(buf,usr,20)!=0 ) {
			//debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," cc_connect_cli(): wrong sha1 hash from client! (%s)\n",ip2string(ip));
			close(sock);
			return NULL;
		}
	} else {
		debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," cc_connect_cli(): recv sha1 timeout\n");
		close(sock);
		return NULL;
	}

	// receive username
	i = recv_nonb(sock, buf, 20,5000);
#ifdef DEBUG_NETWORK
	if (flag_debugnet) {
		debugf( getdbgflag(DBG_CCCAM,cccam->id,0) , " CCcam%d: receive username %s -%d\n", cccam->id, ip2string(ip), i);
		debughex(buf,i);
	}
#endif
	if (i == 20) {
		cc_decrypt(&recvblock, buf, i);
		memcpy(usr,buf,20);
		usr[20] = 0;
		//strcpy(usr, (char*)buf);
		//debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," cc_connect_cli(): username '%s'\n", usr);
	}
	else {
		//debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," cc_connect_cli(): recv user timeout\n");
		close(sock);
		return NULL;
	}

	// test username
	for(i=0; i<20; i++) {
		if (usr[i]==0) break;
		if (usr[i]<=32) { // bad username
			close(sock);
			return NULL;
		}
	}

	// Check for username
	///pthread_mutex_lock(&prg.lockcccli);
	int found = 0;
	uint32_t hash = hashCode( (unsigned char *)usr, strlen(usr) );
	struct cc_client_data *cli = cccam->client;
	while (cli) {
		if (cli->userhash==hash)
		if (!strcmp(cli->user,usr)) {
			if (IS_DISABLED(cli->flags)) { // Connect only enabled clients
				///pthread_mutex_unlock(&prg.lockcccli);
				debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: connection refused for client '%s' (%s), client disabled\n", cccam->id, usr, ip2string(ip));
				close(sock);
				return NULL;
			}
			found = 1;
			break;
		}
		cli = cli->next;
	}
	if (!found) {
		cli = cccam->cacheexclient;
		while (cli) {
			if (cli->userhash==hash)
			if (!strcmp(cli->user,usr)) {
				if (IS_DISABLED(cli->flags)) { // Connect only enabled clients
					///pthread_mutex_unlock(&prg.lockcccli);
					debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: connection refused for cacheexclient '%s' (%s), client disabled\n", cccam->id, usr, ip2string(ip));
					close(sock);
					return NULL;
				}
				found = 1;
				break;
			}
			cli = cli->next;
		}
	}
	///pthread_mutex_unlock(&prg.lockcccli);

	if (!found) {
		debugf(getdbgflag(DBG_CCCAM,cccam->id,0)," CCcam%d: Unknown Client '%s' (%s)\n", cccam->id, usr, ip2string(ip));
		close(sock);
		return NULL;
	}
	memcpy( &tmpcli, cli, sizeof(struct cc_client_data));

	// Check for Host
	if (cli->host) {
		struct host_data *host = cli->host;
		host->clip = ip;
		if ( host->ip && (host->ip!=ip) ) {
			uint32_t sec = getseconds()+60;
			if ( host->checkiptime > sec ) host->checkiptime = sec;
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: Aborted connection from Client '%s' (%s), ip refused\n", cccam->id, usr, ip2string(ip));
			cli->nbloginerror++;
			close(sock);
			return NULL;
		}
	}

	// Encrypted Password
	if ((i=recv_nonb(sock, buf, 6,5000)) == 6) {
		memset(pwd, 0, sizeof(pwd));
		strcpy(pwd, cli->pass);
		cc_encrypt(&recvblock, (uint8_t*)pwd, strlen(pwd));
		cc_decrypt(&recvblock, buf, 6);
		if ( memcmp(buf,"CCcam\0",6) ) {
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: login failed from client '%s'\n", cccam->id, usr);
			cli->nbloginerror++;
			close(sock);
			return NULL;
		}
	}
	else {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: login failed from client '%s', error receiving crypted password\n", cccam->id, usr);
		cli->nbloginerror++;
		close(sock);
		return NULL;
	}

	if (cli->ip==ip) cli->nbdiffip++;


	// Send passwd ack
	memset(buf, 0, 20);
	memcpy(buf, "CCcam\0", 6);
	//debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id),"Server: send ack '%s'\n",buf);
	cc_encrypt(&sendblock, buf, 20);
	if (!send_nonb(sock, buf, 20, 100) ) {
		close(sock);
		return NULL;
	}
	memcpy(&tmpcli.sendblock,&sendblock,sizeof(sendblock));
	memcpy(&tmpcli.recvblock,&recvblock,sizeof(recvblock));
	debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: client '%s' connected\n", cccam->id, usr);
	// Recv cli data
	memset(buf, 0, sizeof(buf));
	i = cc_msg_recv( sock, &tmpcli.recvblock, buf, 5000);
	if ( i<65 ) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: Error recv cli '%s' data (%d/%d)\n", cccam->id, cli->user, i, errno);
		debughex(buf,i);
		close(sock);
		return NULL;
	}
	// Setup Client Data
	// pthread_mutex_lock(&prg.lockcccli);
	memcpy( tmpcli.nodeid, buf+24, 8);
	memcpy( tmpcli.version, buf+33, 31);
	memcpy( tmpcli.build, buf+65, 31 );
	debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: client '%s' running version %s build %s\n", cccam->id, usr, tmpcli.version, tmpcli.build);  // cli->nodeid,8,
	// Check for Nodeid/CCcam Version
	if (cli->option.checknodeid)
	if (cli->option.nodeid[0] && cli->option.nodeid[7]) {
		if (memcmp(cli->option.nodeid, tmpcli.nodeid, 8)) { // diff nodeid
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: login failed from client '%s' (%s), wrong nodeid\n", cccam->id, usr, ip2string(ip));
			cli->nbloginerror++;
			close(sock);
			return NULL;
		}
	}

	//Check Reconnection
	if (cli->connection.status>0) {
		if ( (GetTickCount()-cli->connection.time) > 60000 ) {
			cc_disconnect_cli(cli);
			if (cli->ip==ip) debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: Client '%s' (%s) already connected\n", cccam->id, usr, ip2string(ip));
			else {
				debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: Client '%s' (%s) already connected with different ip, Connection closed.\n", cccam->id, usr, ip2string(ip));
				cli->nbloginerror++;
				close(sock);
				return NULL;
			}
		}
		else {
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam%d: Client '%s' just connected, new connection closed (%s)\n", cccam->id, usr, ip2string(ip));
			cli->nbloginerror++;
			close(sock);
			return NULL;
		}
	}
	cli->nblogin++;

#ifdef IPLIST
	if (ipdata) iplist_goodlogin(ipdata);
#endif

	memcpy(&cli->sendblock, &tmpcli.sendblock,sizeof(tmpcli.sendblock));
	memcpy(&cli->recvblock, &tmpcli.recvblock,sizeof(tmpcli.recvblock));
	memcpy(cli->nodeid, tmpcli.nodeid, 8);
	// store nodeid if not set :)
	if (!cli->option.nodeid[0] && !cli->option.nodeid[7]) {
		memcpy(cli->option.nodeid, tmpcli.nodeid, 8);
		prg.updatenodes = 1;
	}
	memcpy(cli->version, tmpcli.version, 31);
	memcpy(cli->build, tmpcli.build, 31 );

	cli->cardsent = 0;
	memset( &cli->ecm, 0, sizeof(cli->ecm) );
	memset( &cli->lastecm, 0, sizeof(cli->lastecm) );

	cli->handle = sock;
	cli->connection.status = 1;
	cli->connection.time = cli->lastactivity = GetTickCount();
	cli->lastecmtime = 0;
	cli->chkrecvtime = 0;
	cli->ip = ip;
	cli->msg.len = 0;
//	pthread_mutex_unlock(&prg.lockcccli);

	// send cli data ack
	cc_msg_send( sock, &cli->sendblock, CC_MSG_CLI_INFO, 0, NULL);
	//cc_msg_send( sock, &cli->sendblock, CC_MSG_BAD_ECM, 0, NULL);
	int sendversion = ( (cli->version[28]=='W')&&(cli->version[29]='H')&&(cli->version[30]='O') );
	cc_sendinfo_cli(cli, sendversion);
	//cc_msg_send( sock, &cli->sendblock, CC_MSG_BAD_ECM, 0, NULL);
	cli->cardsent = 1;
	//TODO: read from client packet CC_MSG_BAD_ECM
	//len = cc_msg_recv(cli->handle, &cli->recvblock, buf, 3);
	usleep(55000);
#ifdef CACHEEX
	if (!cli->cacheex_mode)
#endif
	cc_sendcards_cli(cli);
	cli->handle = sock;

#ifdef CACHEEX
	if (cli->cacheex_mode==3) {
		if (!create_thread( &cli->tid, (threadfn)cacheex_cc_cli_recvmsg, cli )) {
			cc_disconnect_cli(cli);
			return NULL;
		}
	}
	else
	if (cli->cacheex_mode) pipe_wakeup( prg.pipe.cacheex[1] );
	else
#endif

#ifdef EPOLL_CCCAM
	pipe_pointer( prg.pipe.cccam[1], PIPE_CLI_CONNECTED, cli );
#else
	pipe_wakeup( prg.pipe.cccam[1] );
#endif

	// update pfd data ???
	cli->parent->clipfd.update = 1;
	return cli;
}

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

void cccam_srv_accept(struct cccam_server_data *srv)
{
	struct sockaddr_in newaddr;
	socklen_t socklen = sizeof(struct sockaddr);
	int newfd = accept( srv->handle, (struct sockaddr*)&newaddr, /*(socklen_t*)*/&socklen);
	if ( newfd<=0 ) {
		if ( (errno!=EAGAIN) && (errno!=EINTR) ) debugf( getdbgflag(DBG_CCCAM,srv->id,0)," CCcam%d: Accept failed (errno=%d)\n", srv->id,errno);
	}
	else {
		SetSocketReuseAddr(newfd);
		uint32_t newip = newaddr.sin_addr.s_addr;

#ifdef IPLIST
		struct ip_hacker_data *ipdata = iplist_find( srv->iplist, newip );
		if (!ipdata) {
			ipdata = iplist_add( newip );
			ipdata->next = srv->iplist;
			srv->iplist = ipdata;
		}
#endif
		if ( isblockedip(newip) ) {
			debugf(getdbgflag(DBG_CCCAM,srv->id,0)," CCcam%d: New Connection (%s) closed, ip blocked\n", srv->id, ip2string(newip) );
			close(newfd);
		}
#ifdef IPLIST
		else if ( !iplist_accept( ipdata ) ) {
			debugf(getdbgflag(DBG_CCCAM,srv->id,0)," CCcam%d: New Connection (%s) closed, ip temporary blocked\n", srv->id, ip2string(newip) );
			close(newfd);
		}
#endif
		else {
			pthread_t srv_tid;
			if (cfg.cccam.keepalive) SetSocketKeepalive(newfd);
			SetSocketNoDelay(newfd);
			SetSoketNonBlocking(newfd);
			//debugf(getdbgflag(DBG_CCCAM,srv->id,0)," CCcam%d: new connection (%s)\n", srv->id, ip2string(newip) );
			struct connect_cli_data *newdata = malloc( sizeof(struct connect_cli_data) );
			newdata->server = srv; 
			newdata->sock = newfd; 
			newdata->ip = newaddr.sin_addr.s_addr;
			if ( !create_thread(&srv_tid, (threadfn)cc_connect_cli,newdata) ) {
				free( newdata );
				close( newfd );
			}
		}
	}
}

void *cccam_accept_thread(void *param)
{
	prctl(PR_SET_NAME,"CCcam Accept",0,0,0);
	sleep(5);

	while(!prg.restart) {

		struct pollfd pfd[18];
		int pfdcount = 0;

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

		if (pfdcount) {
			int retval = poll(pfd, pfdcount, 3006);
			if ( retval>0 ) {
				struct cccam_server_data *cccam = cfg.cccam.server;
				while (cccam) {
					if ( !IS_DISABLED(cccam->flags) && (cccam->handle>0) && (cccam->ipoll>=0) && (cccam->handle==pfd[cccam->ipoll].fd) ) {
						if ( pfd[cccam->ipoll].revents & (POLLIN|POLLPRI) ) cccam_srv_accept(cccam);
					}
					cccam = cccam->next;
				}
			}
			else if (retval<0) usleep(96000);
		} else sleep(1);
	}
	return NULL;
}

////////////////////////////////////////////////////////////////////////////////
// CCCAM SERVER: SEND DCW TO CLIENTS
////////////////////////////////////////////////////////////////////////////////

void cc_senddcw_cli(struct cc_client_data *cli)
{
	uint8_t buf[CC_MAXMSGSIZE];
	uint32_t ticks = GetTickCount();

	if (cli->ecm.status==STAT_DCW_SENT) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," +> cw send failed to CCcam client '%s', cw already sent\n", cli->user); 
		return;
	}
	if (cli->handle<=0) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," +> cw send failed to CCcam client '%s', client disconnected\n", cli->user); 
		return;
	}
	if (!cli->ecm.busy) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," +> cw send failed to CCcam client '%s', no ecm request\n", cli->user); 
		return;
	}

	ECM_DATA *ecm = cli->ecm.request;
	//FREEZE
	int samechannel = (cli->lastecm.caid==ecm->caid)&&(cli->lastecm.prov==ecm->provid)&&(cli->lastecm.sid==ecm->sid);
	int enablefreeze=0;
	if (samechannel) {
		if ( (cli->lastecm.hash!=ecm->hash)&&(cli->lastecm.tag!=ecm->ecm[0]) )
		if ( (cli->lastecm.status=1)&&(cli->lastdcwtime+200<ticks) ) enablefreeze = 1;
	} else cli->zap++;
	//
	cli->lastecm.caid = ecm->caid;
	cli->lastecm.prov = ecm->provid;
	cli->lastecm.sid = ecm->sid;
	cli->lastecm.hash = ecm->hash;
	cli->lastecm.tag = ecm->ecm[0];
	cli->lastecm.decodetime = ticks-cli->ecm.recvtime;
	cli->lastecm.request = cli->ecm.request;

	if ( (ecm->dcwstatus==STAT_DCW_SUCCESS)&&(ecm->hash==cli->ecm.hash) ) {
		cli->lastecm.dcwsrctype = ecm->dcwsrctype;
		cli->lastecm.dcwsrcid = ecm->dcwsrcid;
		cli->lastecm.status=1;
		cli->ecmok++;
		cli->lastdcwtime = ticks;
		cli->ecmoktime += ticks-cli->ecm.recvtime;
		//cli->lastecmoktime = ticks-cli->ecm.recvtime;
		memcpy( buf, ecm->cw, 16 );

		cc_crypt_cw( cli->nodeid, cli->ecm.cardid , buf);
		if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_REQUEST, 16, buf) ) {
			cc_disconnect_cli( cli );
			return;
		}
		cc_encrypt(&cli->sendblock, buf, 16); // additional crypto step
		debugf(getdbgflagpro(DBG_CCCAM,cli->parent->id,cli->id,ecm->cs->id)," => cw to CCcam client '%s' ch %04x:%06x:%04x (%dms)\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->ecm.recvtime);
	}
	else { //if (ecm->data->dcwstatus==STAT_DCW_FAILED)
		if (enablefreeze) {
			cli->freeze++;
		}
		cli->lastecm.dcwsrctype = DCW_SOURCE_NONE;
		cli->lastecm.dcwsrcid = 0;
		cli->lastecm.status=0;
		if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK1, 0, NULL) ) {
			cc_disconnect_cli( cli );
			return;
		}
		debugf(getdbgflagpro(DBG_CCCAM,cli->parent->id,cli->id,ecm->cs->id)," |> decode failed to CCcam client '%s' ch %04x:%06x:%04x (%dms)\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->ecm.recvtime);
	}
	cli->ecm.busy=0;
	cli->ecm.status = STAT_DCW_SENT;
}

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

// Check sending cw to clients
void cc_check_sendcw(ECM_DATA *ecm)
{
	struct cccam_server_data *cccam = cfg.cccam.server;
	while (cccam) {
		if ( !IS_DISABLED(cccam->flags) && (cccam->handle>0) ) {
			struct cc_client_data *cli = cccam->client;
			while (cli) {
				if ( !IS_DISABLED(cli->flags)&&(cli->connection.status>0)&&(cli->ecm.busy)&&(cli->ecm.request==ecm) ) {
					cc_senddcw_cli( cli );
				}
				cli = cli->next;
			}
		}
		cccam = cccam->next;
	}
}

///////////////////////////////////////////////////////////////////////////////
// CCCAM SERVER: RECEIVE MESSAGES FROM CLIENTS
///////////////////////////////////////////////////////////////////////////////

void cc_store_ecmclient(ECM_DATA *ecm, struct cc_client_data *cli)
{
	uint32_t ticks = GetTickCount();
	cli->ecm.recvtime = ticks;
	cli->ecm.request = ecm;
    cli->ecm.status = STAT_ECM_SENT;
	ecm_addip(ecm, cli->ip);
}


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

// Receive messages from client
void cc_cli_parsemsg(struct cc_client_data *cli, uint8_t *buf, int len)
{
	if (len>=CC_MAXMSGSIZE) return;

	uint8_t data[CC_MAXMSGSIZE]; // for other use
	uint8_t cw[16];
	unsigned int cardid;

	uint32_t ticks = GetTickCount();
	cli->lastactivity = ticks;
	switch (buf[1]) {
		 case CC_MSG_ECM_REQUEST:
			cli->ecmnb++;
			cli->lastecmtime = ticks;
			if (len<20) return; // Avoid malicious peers
/*
			if (cli->ecm.busy) {
				// send decode failed
				cli->ecmdenied++;
				if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK1, 0, NULL) ) {
					debugf(getdbgflag(DBG_ERROR,0,0)," Error encountred when sending failedcw to client '%s'\n", cli->user);

					cc_disconnect_cli(cli);
					return;
				}
				debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," <|> decode failed to CCcam client '%s', too many ecm requests\n", cli->user);

				break;
			}
*/
			cli->ecm.busy = 0;
			//Check for card availability
			memcpy( data, buf+17, len-17);
			cardid = buf[10]<<24 | buf[11]<<16 | buf[12]<<8 | buf[13];
			uint16_t caid = buf[4]<<8 | buf[5];
			uint16_t sid = buf[14]<<8 | buf[15];
			uint32_t provid = ecm_getprovid( data, caid );
			if (provid==0) provid = buf[6]<<24 | buf[7]<<16 | buf[8]<<8 | buf[9];

			// Check for Profile
			struct cardserver_data *cs=getcsbyid( cardid );
			if (!cs) {
				// check for cs by caid:prov
				cs = getcsbycaidprov(caid,provid);
				if (!cs) {
					cli->ecmdenied++;
					if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK1, 0, NULL) ) {
						debugf(getdbgflag(DBG_ERROR,0,0)," Error encountred when sending failedcw to client '%s'\n", cli->user);
						cc_disconnect_cli(cli);
						return;
					}
					debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," <|> decode failed to CCcam client '%s', card-id %x (%04x:%06x) not found\n",cli->user, cardid, caid,provid);
					break;
				}
			}
			// Check for Share
			if (!cs->option.fsharecccam) {
				cli->ecmdenied++;
				if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK1, 0, NULL) ) {
					debugf(getdbgflag(DBG_ERROR,0,0)," Error encountred when sending failedcw to client '%s'\n", cli->user);
					cc_disconnect_cli(cli);
					return;
				}
				debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," <|> decode failed to CCcam client '%s', Profile '%s' disabled to client\n",cli->user, cs->name);
				break;
			}
			// Chec for ECM
			uint8_t cw1cycle;
			char *error = cs_accept_ecm(cs,caid,provid,sid,ecm_getchid(data,caid), len-17, data, &cw1cycle);
			if (error) {
				cs->ecmdenied++;
				cli->ecmdenied++;
				if (!cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK1, 0, NULL)) {
					cc_disconnect_cli(cli);
					return;
				}
				debugf(getdbgflagpro(DBG_CCCAM,cli->parent->id,cli->id,cs->id)," <|> decode failed to CCcam client '%s' ch %04x:%06x:%04x, %s\n", cli->user, caid,provid,sid, error);
				break;
			}

			// ACCEPTED
			pthread_mutex_lock(&prg.lockecm);

			// Search for ECM
			ECM_DATA *ecm = search_ecmdata_any(cs, data,  len-17, sid, caid);
			int isnew =  ( ecm==NULL );
			if (ecm) {
				ecm->lastrecvtime = ticks;
				if (ecm->dcwstatus==STAT_DCW_FAILED) {
					if (ecm->period > cs->option.dcw.retry) {
						if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_ECM_NOK1, 0, NULL) ) {
							cc_disconnect_cli( cli );
							return;
						}
						debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," <|> decode failed to CCcam client '%s' ch %04x:%06x:%04x:%08x, already failed\n",cli->user, caid, provid, sid, ecm->hash);
					}
					else {
						ecm->period++; // RETRY
						cc_store_ecmclient(ecm, cli);
						debugf(getdbgflagpro(DBG_CCCAM,cli->parent->id,cli->id,cs->id)," <- ecm from CCcam client '%s' ch %04x:%06x:%04x:%08x**\n", cli->user, caid, provid, sid, ecm->hash);
						cli->ecm.busy=1;
						cli->ecm.hash = ecm->hash;
						cli->ecm.cardid = cardid;
						ecm->dcwstatus = STAT_DCW_WAIT;
						ecm->cachestatus = 0; //ECM_CACHE_NONE; // Resend Request
						ecm->checktime = 1; // Check NOW
						pipe_wakeup( prg.pipe.ecm[1] );
					}
				}
				else { // SUCCESS/WAIT
					cc_store_ecmclient(ecm, cli);
					debugf(getdbgflagpro(DBG_CCCAM,cli->parent->id,cli->id,cs->id)," <- ecm from CCcam client '%s' ch %04x:%06x:%04x:%08x*\n", cli->user, caid, provid, sid, ecm->hash);
					cli->ecm.busy=1;
					cli->ecm.hash = ecm->hash;
					cli->ecm.cardid = cardid;
					if (cli->dcwcheck) {
						if ( !ecm->lastdecode.ecm && (ecm->lastdecode.ecm!=ecm) ) {
							checkfreeze_checkECM( ecm, cli->lastecm.request);
							if (ecm->lastdecode.ecm) pipe_cache_find(ecm, cs);
						}
					}
					// Check for Success/Timeout
					if (!ecm->checktime) {
						if (cli->dcwcheck) {
							if ( (ecm->dcwstatus==STAT_DCW_SUCCESS) && !checkfreeze_setdcw(ecm,ecm->cw) ) { // ??? last ecm is wrong
								ecm->dcwstatus = STAT_DCW_WAIT;
								memset( ecm->cw, 0, 16 );
								ecm->checktime = 1; // Wakeup Now
								pipe_wakeup( prg.pipe.ecm[1] );
							}
							else {
								pthread_mutex_unlock(&prg.lockecm);
								cc_senddcw_cli(cli);
								break;
							}
						}
						else {
							pthread_mutex_unlock(&prg.lockecm);
							cc_senddcw_cli(cli);
							break;
						}
					}
				}
			}
			else {
				cs->ecmaccepted++;
				// Setup ECM Request for Server(s)
				ecm = store_ecmdata(cs, data, len-17, sid, caid, provid);
				cc_store_ecmclient(ecm, cli);
				debugf(getdbgflagpro(DBG_CCCAM,cli->parent->id,cli->id,cs->id)," <- ecm from CCcam client '%s' ch %04x:%06x:%04x:%08x\n",cli->user,caid,provid,sid, ecm->hash);
				cli->ecm.busy=1;
				cli->ecm.hash = ecm->hash;
				cli->ecm.cardid = cardid;
				ecm->cw1cycle = cw1cycle;
				ecm->dcwstatus = STAT_DCW_WAIT;

#ifdef CHECK_NEXTDCW
				if (cli->dcwcheck) checkfreeze_checkECM( ecm, cli->lastecm.request);
#endif
				if (cs->option.fallowcache) {
					ecm->waitcache = 1;
					ecm->dcwstatus = STAT_DCW_WAITCACHE;
					ecm->checktime = ecm->recvtime + cs->option.cachetimeout;
					pipe_cache_find(ecm, cs);
				}
				else ecm->checktime = 1; // Check NOW
				pipe_wakeup( prg.pipe.ecm[1] );

#ifdef TESTCHANNEL
				int testchannel = ( (ecm->caid==cfg.testchn.caid)&&(ecm->provid==cfg.testchn.provid)&&(!cfg.testchn.sid||(ecm->sid==cfg.testchn.sid)) );
				if (testchannel) {
					fdebugf(" <- ecm from CCcam client '%s' ch %04x:%06x:%04x %02x:%08x\n", cli->user, ecm->caid, ecm->provid, ecm->sid, ecm->ecm[0], ecm->hash);
				}
#endif
			}

			pthread_mutex_unlock(&prg.lockecm);
			if (isnew) wakeup_sendecm();
			break;

		 case CC_MSG_KEEPALIVE:
			if ( !cc_msg_send( cli->handle, &cli->sendblock, CC_MSG_KEEPALIVE, 0, NULL) ) cc_disconnect_cli( cli );
			//printf(" Keepalive from client '%s'\n",cli->user);
			break;

#ifdef CACHEEX
		 case CC_MSG_CACHE_PUSH:
			if (cli->cacheex_mode!=3) break;
			//if (buf[18]!=0) break; // Got CW
			memcpy( cw, buf+44, 16);
			if (!acceptDCW(cw)) break;
			struct cache_data cacheex;
			cacheex.caid = (buf[4]<<8) | buf[5];
			cacheex.provid = (buf[6]<<24) | (buf[7]<<16) | (buf[8]<<8) | buf[9];
			cacheex.sid = (buf[14]<<8) | buf[15];
			// Look for cardserver
			cs = getcsbycaprovid(cacheex.caid, cacheex.provid);
			if ( !cs || !cs->option.fallowcacheex ) {
				cli->cacheex.badcw++;
				break;
			}
			if ((buf[23]&0xFE)==0x80) cacheex.tag = buf[23]; else cacheex.tag = 0;
			memcpy( cacheex.ecmd5, buf+24, 16);
			//if ( !checkECMD5(cacheex.ecmd5) ) cli->cacheex.totalcsp++;
			cacheex.hash = (buf[43]<<24) | (buf[42]<<16) | (buf[41]<<8) | buf[40];
			if (!cacheex_check(&cacheex)) break;
			cli->cacheex.got[0]++;
			int uphop = buf[60];
			if (uphop<10) cli->cacheex.got[uphop]++;
			//
			//
			pthread_mutex_lock( &prg.lockcache );
			int res = cache_setdcw( &cacheex, cw, NO_CYCLE, PEER_CCCAM_CLIENT | cli->id );
			pthread_mutex_unlock( &prg.lockcache );
			if (res&DCW_ERROR) {
				if ( !(res&DCW_SKIP) ) cli->cacheex.badcw++;
			}
			else if (res&DCW_CYCLE) {
				if ( cs->option.cacheex.maxhop>uphop ) {
					uint8_t nodeid[8];
					memcpy( nodeid, buf+61, 8);
					pipe_send_cacheex_push_cache(&cacheex, cw, nodeid); //cacheex_push(&cacheex, cw, nodeid);
				}
			}
			//debugf( getdbgflag(DBG_CACHEEX, 0, 0)," CACHEEX PUSH from client '%s' %04x:%06x:%04x:%08x\n",cli->user,cacheex.caid,cacheex.provid,cacheex.sid,cacheex.hash);
			break;
#endif

		 case CC_MSG_BAD_ECM:
			break;

		default:
			debugf(0, " unknown message type\n"); debughex(buf, len);
	}
}




int cc_cli_peekmsg(struct cc_client_data *cli, uint8_t *buf)
{
	int len;
	if (cli->handle<=0) return 0; // disconnected

#ifdef RECVMSG_PEEK

	len = cc_msg_chkrecv(cli->handle,&cli->recvblock);
	uint32_t ticks = GetTickCount();
	if (len==0) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam: client '%s' read failed %d\n", cli->user,len);
		cc_disconnect_cli(cli);
	}
	else if (len<0) {
		if (!cli->chkrecvtime) cli->chkrecvtime = ticks;
		else if ( (cli->chkrecvtime+500)<ticks ) {
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam: client '%s' read failed %d\n", cli->user,len);
			cc_disconnect_cli(cli);
		}
	}
	else if (len>0) {
		cli->chkrecvtime = 0;
		len = cc_msg_recv( cli->handle, &cli->recvblock, buf, 3000);
		if (len==0) {
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam: client '%s' read failed %d\n", cli->user,len);
			cc_disconnect_cli(cli);
		}
		else if (len<0) {
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam: client '%s' read failed %d(%d)\n", cli->user,len,errno);
			cc_disconnect_cli(cli);
		}
		else return len;
	}

#else
	len = cc_peekmsg( cli->handle, &cli->recvblock, &cli->msg, buf );
	if (len==0) {
		debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam: client '%s' read failed %d\n", cli->user,len);
		cc_disconnect_cli(cli);
	}
	else if (len==-1) {
		uint32_t ticks = GetTickCount();
		if (!cli->chkrecvtime) cli->chkrecvtime = ticks;
		else if ( (cli->chkrecvtime+1000)<ticks ) {
			debugf(getdbgflag(DBG_CCCAM,cli->parent->id,cli->id)," CCcam: client '%s' read failed %d\n", cli->user,len);
			cc_disconnect_cli(cli);
		}
	} else return len;
#endif

	return 0;
}

// Receive messages from client
void *cc_cli_recvmsg(struct cc_client_data *cli)
{     
	if (cli->handle<=0) return NULL;
	//debugf(getdbgflag(DBG_CCCAM,0,0)," CCcam: client '%s' recv msg (len=%d)\n", cli->user, cli->msg.len);
	uint8_t buf[CC_MAXMSGSIZE];
	int len = cc_cli_peekmsg(cli, buf);

	if (len<=0) return NULL;
	if (len>=CC_MAXMSGSIZE) return NULL;

	cli->chkrecvtime = 0;
	cc_cli_parsemsg(cli, buf, len);
	return NULL;
}


#ifdef CACHEEX
// Receive messages from CacheEX client
void *cacheex_cc_cli_recvmsg(struct cc_client_data *cli)
{
	cli->pid = syscall(SYS_gettid);
	while (cli->connection.status>0) {
		struct pollfd pfd;
		pfd.fd = cli->handle;
		pfd.events = POLLIN | POLLPRI;
		int retval = poll(&pfd, 1, 3005); // for 3seconds
		if (retval==0) continue;
		else if (retval<0) { // error
			cc_disconnect_cli(cli);
			break;
		}
		else if ( pfd.revents & (POLLIN|POLLPRI) ) cc_cli_recvmsg(cli);
		else {
			cc_disconnect_cli(cli);
			break;
		}
	}

	cli->pid = 0;
	return NULL;
}
#endif

void cc_recv_pipe()
{
	struct cccam_server_data *cccam;
	struct cardserver_data *cs;

	struct cc_client_data *cli;

	//debugf(getdbgflag(DBG_CCCAM,0,0)," CCcam: recv_msg from pipe\n");

	uint8_t buf[1024];
	struct pollfd pfd[2];
	do {
		int len = pipe_recv( prg.pipe.cccam[0], buf);
		if (len>0) {
			//debugf(getdbgflag(DBG_CCCAM,0,0)," CCcam: recv_msg from pipe (%d)\n", buf[0]);
			switch(buf[0]) {
				case PIPE_WAKEUP:  // ADD NEW CLIENT
					//debugf(" wakeup csmsg\n");
					break;
#ifdef EPOLL_CCCAM
				case PIPE_CLI_CONNECTED:  // ADD NEW FD
					memcpy( &cli, buf+1, sizeof(void*) );
					// Add to events
					struct epoll_event ev; // epoll event
					ev.events = EPOLLIN;
					ev.data.fd = cli->handle;
					ev.data.ptr = cli;
					if ( epoll_ctl(prg.epoll.cccam, EPOLL_CTL_ADD, cli->handle, &ev) == -1 ) debugf(DBG_ERROR,"Err! EPOLL_CTL_ADD %s (%d) %d\n", cli->user, cli->handle, errno);
					//else debugf(0,"EPOLL_CTL_ADD %s (%d)\n", cli->user, cli->handle);
					break;
#endif
				case PIPE_CARD_DEL:
					memcpy( &cs, buf+1, sizeof(void*) );
					if (cs) {
						cccam = cfg.cccam.server;
						while (cccam) {
							if ( !IS_DISABLED(cccam->flags)&&(cccam->handle>0) ) {
								struct cc_client_data *cli = cccam->client;
								while (cli) {
									if ( !IS_DISABLED(cli->flags)&&(cli->handle>0) ) cc_sendcard_del(cs, cli);
									cli = cli->next;
								}
							}
							cccam = cccam->next;
						}
					}
					break;

				case PIPE_CARD_ADD:
					memcpy( &cs, buf+1, sizeof(void*) );
					if (cs) {
						cccam = cfg.cccam.server;
						while (cccam) {
							if ( !IS_DISABLED(cccam->flags)&&(cccam->handle>0) ) {
								struct cc_client_data *cli = cccam->client;
								while (cli) {
									if ( !IS_DISABLED(cli->flags)&&(cli->handle>0) ) cc_sendcard_cli(cs, cli, 0);
									cli = cli->next;
								}
							}
							cccam = cccam->next;
						}
					}
					break;
			}
		}
		pfd[0].fd = prg.pipe.cccam[0];
		pfd[0].events = POLLIN | POLLPRI;
	} while (poll(pfd, 1, 3)>0);
}


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

#ifdef EPOLL_CCCAM

void *cc_recvmsg_thread(void *param)
{
	int i;

	cfg.cccam.pid_recvmsg = syscall(SYS_gettid);
	prg.pid_cc_msg = syscall(SYS_gettid);
	prctl(PR_SET_NAME,"CCcam RecvMSG",0,0,0);

	struct epoll_event evlist[MAX_EPOLL_EVENTS]; // epoll recv events
	prg.epoll.cccam = epoll_create( MAX_EPOLL_EVENTS );
	// Add PIPE
	struct epoll_event ev; // epoll event
	ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP;
	ev.data.ptr = NULL;
	if ( epoll_ctl(prg.epoll.cccam, EPOLL_CTL_ADD, prg.pipe.cccam[0], &ev) == -1 ) printf("epoll_ctl");

	while (!prg.restart) {
		int ready = epoll_wait( prg.epoll.cccam, evlist, MAX_EPOLL_EVENTS, 1002);
		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

		usleep(cfg.delay.thread);

		for (i=0; i < ready; i++) {
			if ( evlist[i].events & (EPOLLIN|EPOLLPRI) ) {
				if (evlist[i].data.ptr == NULL) cc_recv_pipe();
				else cc_cli_recvmsg(evlist[i].data.ptr);
			}
			else if ( evlist[i].events & (EPOLLRDHUP | EPOLLHUP | EPOLLERR) ) { // EPOLLRDHUP
				if (evlist[i].data.ptr == NULL) debugf(DBG_ERROR,"Err! epoll_wait() pipe\n"); // error !!!
				else cc_disconnect_cli(evlist[i].data.ptr);
			}
		}
	}
	return NULL;
}

#else

void *cc_recvmsg_thread(void *param)
{
	struct pollfd pfd[MAX_PFD];
	int pfdcount;

	cfg.cccam.pid_recvmsg = syscall(SYS_gettid);
	prg.pid_cc_msg = syscall(SYS_gettid);
	prctl(PR_SET_NAME,"CCcam RecvMSG",0,0,0);

	while (!prg.restart) {
		pfdcount = 0;
		// PIPE
		pfd[pfdcount].fd = prg.pipe.cccam[0];
		pfd[pfdcount++].events = POLLIN | POLLPRI;

		struct cccam_server_data *cccam = cfg.cccam.server;
		while (cccam) {
			if ( !IS_DISABLED(cccam->flags)&&(cccam->handle>0) ) {
				if (cccam->clipfd.update||cccam->clipfd.count<0) {
					cccam->clipfd.count = 0;
					struct cc_client_data *cli = cccam->client;
					while (cli && (cccam->clipfd.count<CCCAM_MAX_PFD)) {
						if ( !IS_DISABLED(cli->flags) && (cli->handle>0) && !(cli->flags&FLAG_WORKTHREAD) ) {
							cli->ipoll = cccam->clipfd.count;
							cccam->clipfd.pfd[cccam->clipfd.count].fd = cli->handle;
							cccam->clipfd.pfd[cccam->clipfd.count++].events = POLLIN | POLLPRI;
						} else cli->ipoll = -1;
						cli = cli->next;
					}
					cccam->clipfd.update = 0;
					//debugf(getdbgflag(DBG_ERROR,0,0), " CCcam clients poll updated %d\n", cccam->clipfd.count);
				}
				cccam->clipfd.ipoll = pfdcount;
				if (cccam->clipfd.count>0) {
					memcpy( &pfd[pfdcount], cccam->clipfd.pfd, cccam->clipfd.count * sizeof(struct pollfd) );
					pfdcount += cccam->clipfd.count;
				}
			}
			cccam = cccam->next;
		}

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

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

			struct cccam_server_data *cccam = cfg.cccam.server;
			while (cccam) {
				if ( !IS_DISABLED(cccam->flags)&&(cccam->handle>0)&&(cccam->clipfd.count>0) ) {
					//pthread_mutex_lock(&prg.lockcccli);
					struct cc_client_data *cccli = cccam->client;
					while (cccli) {
						if ( !IS_DISABLED(cccli->flags)&&(cccli->handle>0)&&(cccli->ipoll>=0)&&(cccli->handle==pfd[cccam->clipfd.ipoll+cccli->ipoll].fd) ) {
							if ( pfd[cccam->clipfd.ipoll+cccli->ipoll].revents & (POLLHUP|POLLNVAL) ) cc_disconnect_cli(cccli);
							else if ( pfd[cccam->clipfd.ipoll+cccli->ipoll].revents & (POLLIN|POLLPRI) ) {
								cc_cli_recvmsg(cccli);
							}
							///else if ( (GetTickCount()-cccli->lastactivity) > 600000 ) cc_disconnect_cli(cccli);
						}
						cccli = cccli->next;
					}
					//pthread_mutex_unlock(&prg.lockcccli);
				}
				cccam = cccam->next;
			}
			//
			if ( pfd[0].revents & (POLLIN|POLLPRI) ) cc_recv_pipe();
		}
		else if ( retval<0 ) {
			debugf(getdbgflag(DBG_CCCAM,0,0), " thread receive messages: poll error %d(errno=%d)\n", retval, errno);
			usleep(99000);
		}
	}
	return NULL;
}

#endif


///////////////////////////////////////////////////////////////////////////////
// CCCAM SERVER: START/STOP
///////////////////////////////////////////////////////////////////////////////

int start_thread_cccam()
{
	pthread_t tid;
#ifndef MONOTHREAD_ACCEPT
	create_thread(&tid, cccam_accept_thread,NULL);
#endif

	create_thread(&cfg.cccam.tid_recvmsg, cc_recvmsg_thread,NULL);
	return 0;
}