./ MultiCS.r82 / th-ecm.c
void clients_check_sendcw(ECM_DATA *ecm)
{
	cs_check_sendcw(ecm);
#ifdef MGCAMD_SRV
	mg_check_sendcw(ecm);
#endif
#ifdef CCCAM_SRV
	cc_check_sendcw(ecm);
#endif
#ifdef FREECCCAM_SRV
	freecc_check_sendcw(ecm);
#endif
#ifdef CS378X_SRV
	cs378x_check_sendcw(ecm);
#endif
#ifdef CAMD35_SRV
	camd35_check_sendcw(ecm);
#endif
}


#include "loadbalance.c"
#include "setdcw.c"

///////////////////////////////////////////////////////////////////////////////
// Check sending ecm to servers
///////////////////////////////////////////////////////////////////////////////

void wakeup_sendecm() // not needed in mono-thread
{
	pipe_wakeup( prg.pipe.ecm[1] );
}

void ecm_faileddcw( ECM_DATA *ecm )
{
	ecm->dcwstatus = STAT_DCW_FAILED;
	ecm->checktime = 0;
	ecm->waitserver = 0;
	sid_newecm(ecm);
	clients_check_sendcw(ecm); // send decode failed to clients
#ifdef TESTCHANNEL
	int testchannel = ( (ecm->caid==cfg.testchn.caid) && (ecm->provid==cfg.testchn.provid) && (ecm->sid==cfg.testchn.sid) );
	if (testchannel) {
		fdebugf(" <Decode_Failed> ch %04x:%06x:%04x/%02x:%08x\n", ecm->caid, ecm->provid, ecm->sid, ecm->ecm[0], ecm->hash);
	}
#endif
}

void check_ecm(ECM_DATA *ecm, uint32_t ticks)
{
	ecm->checktime = 0; // invalid
	ecm->waitserver = 0;

	// CACHE(fallowcache = 1)
	if (ecm->dcwstatus==STAT_DCW_WAITCACHE) {
		struct cardserver_data *cs = ecm->cs;
		if (!cs) {
			ecm->statusmsg = "Invalid profile id";
			ecm_faileddcw( ecm );
			return;
		}
		if (cs->option.fallowcache) {
			if (!ecm->waitcache) { // Not done
				if (cs->option.fallowcache) {
					pipe_cache_find(ecm, cs);
					ecm->waitcache = 1;
					ecm->checktime = ecm->recvtime + cs->option.cachetimeout; // wait for cache
				}
				else ecm->dcwstatus = STAT_DCW_WAIT;
			}
			else {
				if ( ( (ecm->recvtime+cs->option.cachetimeout)<=ticks ) ) ecm->dcwstatus = STAT_DCW_WAIT;
				else {
					//debugf(" Wait for cache\n");
					ecm->checktime = ecm->recvtime+cs->option.cachetimeout;
				}
			}
		}
		else ecm->dcwstatus = STAT_DCW_WAIT;
	}

	// SEND ECM
	if ( (ecm->dcwstatus==STAT_DCW_WAIT) ) {
		// Check Profile
		struct cardserver_data *cs = ecm->cs;
		if (!cs) {
			ecm->statusmsg = "Invalid profile id";
			ecm_faileddcw( ecm );
			return;
		}
		//check for decode failed
		// Check for Max used Servers
		if ( (cs->option.server.max>0) && (ecm->server_totalsent>=cs->option.server.max) ) {
			if (!ecm->server_totalwait) {
				ecm->statusmsg = "Decode failed, max servers is reached and no more servers to wait";
				ecm_faileddcw( ecm );
				// Send DCW to Cache if not sent
				if ( cs->option.fallowcache && cs->option.cachesendrep && (ecm->cachestatus!=ECM_CACHE_REP) ) {
					//pipe_send_cache_reply(ecm,cs); // Send failed Cache Reply
					ecm->cachestatus = ECM_CACHE_REP;
				}
			}
			else ecm->checktime = ecm->recvtime + cs->option.dcw.timeout*ecm->period;
		}
		// Check for ECM TimeOut
		else if ( (ticks-ecm->recvtime) >= cs->option.dcw.timeout*ecm->period ) {
			ecm->statusmsg = "Decode failed, dcw timeout";
			ecm_faileddcw( ecm );
			// Send DCW to Cache if not sent
			if ( cs->option.fallowcache && cs->option.cachesendrep && (ecm->cachestatus!=ECM_CACHE_REP) ) {
				//pipe_send_cache_reply(ecm,cs); // Send failed Cache Reply
				ecm->cachestatus = ECM_CACHE_REP;
			}
		}
		// Check for ECM sending
		else if ( (ticks-ecm->recvtime) <= (cs->option.server.timeout*ecm->period) ) { // ~ cfg.cardserver.option.dcw.timeout*2/3 is the cardserver timeout
			// Check for cache request
			if ( (ecm->cachestatus==ECM_CACHE_NONE) && cs->option.fallowcache && cs->option.cachesendreq ) {
				pipe_cache_request(ecm,cs);
				ecm->cachestatus = ECM_CACHE_REQ;
			}
			// check for decode failed with no remaining server to wait, send to new cardserver
			if ( !ecm->server_totalwait
				|| (cs->option.server.first>ecm->server_totalsent)
				|| ((ticks-ecm->lastsendtime)>=cs->option.server.interval)
			) {
				//debugf(" check_sendecm[%s] ch %04x:%06x:%04x\n", cs->name,ecm->caid,ecm->provid,ecm->sid);
				struct server_data *newsrv = NULL;
				if ( srvtab_arrange(cs, ecm, ecm->server_totalsent > 0 )==-1 ) { // No more servers to decode
					//debugf(0," sendecm: no servers found to decode\n");
					ecm->checktime = ecm->recvtime + cs->option.dcw.timeout*ecm->period;
					return;
				}
				else if (psrvlist[0] && psrvlist[0]->srv) {
					// SEND ECM
					newsrv = psrvlist[0]->srv;
					newsrv->busycard = psrvlist[0]->card; // dont save pointers!!!
					newsrv->busycardid = psrvlist[0]->shareid;
					//debugf(0," sendecm: selected server to decode (%s:%d)\n", newsrv->host->name, newsrv->port);
					if (newsrv->type==TYPE_NEWCAMD) {
						if (cs_sendecm_srv(cs, newsrv, ecm)>0) {
							ecm->lastsendtime = ticks;
							debugf(getdbgflagpro(DBG_SERVER,0,newsrv->id,cs->id)," -> ecm to Newcamd server%d (%s:%d) ch %04x:%06x:%04x:%08x\n",(1+ecm->server_totalsent),newsrv->host->name,newsrv->port,ecm->caid,ecm->provid,ecm->sid,ecm->hash);
							newsrv->lastecmtime = ticks;
							newsrv->ecmnb++;
							newsrv->busy=1;
							newsrv->ecm.request = ecm;
							newsrv->ecm.hash = ecm->hash;
							newsrv->retry=0;
							ecm_addsrv(ecm, newsrv->id);
							ecm_addsrvip(ecm, newsrv->host->ip);
						}
					}
#ifdef CCCAM_CLI
					else if (newsrv->type==TYPE_CCCAM) {
						if (cc_sendecm_srv(newsrv, ecm)>0) {
							ecm->lastsendtime = ticks;
							debugf(getdbgflagpro(DBG_SERVER,0,newsrv->id,cs->id)," -> ecm to CCcam server%d (%s:%d) ch %04x:%06x:%04x:%08x\n",(1+ecm->server_totalsent),newsrv->host->name,newsrv->port,ecm->caid,ecm->provid,ecm->sid,ecm->hash);
							newsrv->lastecmtime = ticks;
							newsrv->ecmnb++;
							struct cs_card_data *card = cc_getcardbyid( newsrv, newsrv->busycardid );
							if (card) card->ecmnb++;
							newsrv->busy=1;
							newsrv->ecm.request = ecm;
							newsrv->ecm.hash = ecm->hash;
							newsrv->retry=0;
							ecm_addsrv(ecm, newsrv->id);
							ecm_addsrvip(ecm, newsrv->host->ip);
						}
					}
#endif

#ifdef RADEGAST_CLI
					else if (newsrv->type==TYPE_RADEGAST) {
						if (rdgd_sendecm_srv(newsrv, ecm)>0) {
							ecm->lastsendtime = ticks;
							debugf(getdbgflagpro(DBG_SERVER,0,newsrv->id,cs->id)," -> ecm to Radegast server%d (%s:%d) ch %04x:%06x:%04x:%08x\n",(1+ecm->server_totalsent),newsrv->host->name,newsrv->port,ecm->caid,ecm->provid,ecm->sid,ecm->hash);
							newsrv->lastecmtime = ticks;
							newsrv->ecmnb++;
							newsrv->busy=1;
							newsrv->ecm.request = ecm;
							newsrv->ecm.hash = ecm->hash;
							newsrv->retry=0;
							ecm_addsrv(ecm, newsrv->id);
							ecm_addsrvip(ecm, newsrv->host->ip);
						}
					}
#endif
#ifdef CAMD35_CLI
					else if (newsrv->type==TYPE_CAMD35) {
						if (camd35_sendecm_srv(newsrv, ecm)>0) {
							ecm->lastsendtime = ticks;
							debugf(getdbgflagpro(DBG_SERVER,0,newsrv->id,cs->id)," -> ecm to camd35 server%d (%s:%d) ch %04x:%06x:%04x:%08x\n",(1+ecm->server_totalsent),newsrv->host->name,newsrv->port,ecm->caid,ecm->provid,ecm->sid,ecm->hash);
							newsrv->lastecmtime = ticks;
							newsrv->ecmnb++;
							newsrv->busy=1;
							newsrv->ecm.request = ecm;
							newsrv->ecm.hash = ecm->hash;
							newsrv->retry=0;
							ecm_addsrv(ecm, newsrv->id);
							ecm_addsrvip(ecm, newsrv->host->ip);
						}
					}
#endif
#ifdef CS378X_CLI
					else if (newsrv->type==TYPE_CS378X) {
						if (cs378x_sendecm_srv(newsrv, ecm)>0) {
							ecm->lastsendtime = ticks;
							debugf(getdbgflagpro(DBG_SERVER,0,newsrv->id,cs->id)," -> ecm to cs378x server%d (%s:%d) ch %04x:%06x:%04x:%08x\n",(1+ecm->server_totalsent),newsrv->host->name,newsrv->port,ecm->caid,ecm->provid,ecm->sid,ecm->hash);

							newsrv->lastecmtime = ticks;
							newsrv->ecmnb++;
							newsrv->busy=1;
							newsrv->ecm.request = ecm;
							newsrv->ecm.hash = ecm->hash;
							newsrv->retry=0;
							ecm_addsrv(ecm, newsrv->id);
							ecm_addsrvip(ecm, newsrv->host->ip);
						}
					}
#endif
					ecm->statusmsg = "Waiting for servers...";
					if ( cs->option.server.first > (ecm->server_totalsent+1) ) ecm->checktime = ticks + 10;
					else {
						ecm->checktime = ticks + cs->option.server.interval;
						if ( (ecm->checktime-ecm->recvtime) > (cs->option.server.timeout*ecm->period) ) ecm->checktime = ecm->recvtime + cs->option.dcw.timeout*ecm->period;
					}
				}
				else {
					ecm->statusmsg = "Wait for available servers";

					//debugf( getdbgflag(DBG_NEWCAMD,cs->id,0), " check_sendecm[%s] ch %04x:%06x:%04x, Wait for available server...\n", cs->name,ecm->caid,ecm->provid,ecm->sid);

					// PROBLEM HERE ??????
					ecm->waitserver = 1; // XXX
					ecm->checktime = ecm->recvtime + cs->option.dcw.timeout*ecm->period; // till the end if there is no server

#ifdef BUSY_SERVER
					if (!ecm->server_totalwait && !ecm->server_totalsent) {
						ecm->statusmsg = "Decode failed, no free server";
						ecm_faileddcw( ecm );
						cs->ecmbusysrv++;
					}
#endif
					return;
				}
			}
			else {
				ecm->checktime = ecm->lastsendtime + cs->option.server.interval;
				if ( (ecm->checktime-ecm->recvtime) > (cs->option.server.timeout*ecm->period) ) ecm->checktime = ecm->recvtime + cs->option.dcw.timeout*ecm->period;
			}
		}
		else if (!ecm->server_totalwait) {
			ecm->statusmsg = "Decode failed, no server open this channel";
			ecm_faileddcw( ecm );
			// Send DCW to Cache if not sent
			if ( cs->option.fallowcache && cs->option.cachesendrep && (ecm->cachestatus!=ECM_CACHE_REP) ) {
				//pipe_send_cache_reply(ecm,cs); //Send Failed Cache Reply
				ecm->cachestatus = ECM_CACHE_REP;
			}
		}
		else if ( cs->option.fallowcache && cs->option.cacheresendreq && (ecm->cachestatus==ECM_CACHE_REQ) ) {
			pipe_cache_resendreq(ecm, cs);
			ecm->cachestatus = ECM_CACHE_REQ2;
			ecm->checktime = ecm->recvtime + cs->option.dcw.timeout*ecm->period;
		}
		else ecm->checktime = ecm->recvtime + cs->option.dcw.timeout*ecm->period;
	}
}

inline void check_sendecm(int newserver)
{
	uint32_t ticks = GetTickCount();
	struct ecm_request *req = ecmdata;
	while (req) {
		if ( (req->recvtime+TIME_ECMALIVE) < ticks ) break;

		if (req->checktime) { // (checktime==0) --> do nothing
			if ( (req->checktime < ticks) || (req->waitserver && newserver) ) {
				check_ecm(req, ticks);
				ticks = GetTickCount();
			}
		}

		req = req->next;
		if (req==ecmdata) break;
	}
}

/// recalculate ecmcheck wakeup time
uint32_t getecmwakeuptime()
{
	uint32_t ticks = GetTickCount();
	uint32_t waketime = ticks+10000;
	struct ecm_request *req = ecmdata;
	while (req) {
		if ( (req->recvtime+TIME_ECMALIVE) < ticks ) break;

		if ( req->checktime )
		if ( waketime > req->checktime ) waketime = req->checktime;

		req = req->next;
		if (req==ecmdata) break;
	}
	return waketime;
}


///////////////////////////////////////////////////////////////////////////////
// CACHE PIPE RECV MESSAGES
///////////////////////////////////////////////////////////////////////////////
int newserver = 0;

void recv_ecm_pipe()
{
	uint8_t buf[1024];
	struct cache_data req;
	uint8_t cw[16];
	int peerid;

	ECM_DATA *ecm;
	struct pollfd pfd;

	do {
		int len = pipe_recv( prg.pipe.ecm[0], buf);
		if (len>0) {
			switch(buf[0]) {

				case PIPE_WAKEUP:
					break;

				case PIPE_LOCK:
					pthread_mutex_lock(&prg.lockmain);
					pthread_mutex_unlock(&prg.lockmain);
					break;

				case PIPE_CACHE_FIND_FAILED:
					get_cache2ecm(buf, &req, NULL);
					pthread_mutex_lock(&prg.lockecm);
					ecm = req.ecm; //search_ecmdata_byhash( req.caid, req.sid, req.hash );
					if (ecm) {
						if ( (ecm->caid==req.caid)&&(ecm->hash==req.hash)&&(ecm->sid==req.sid)&&(ecm->dcwstatus==STAT_DCW_WAITCACHE) ) {
							struct cardserver_data *cs = ecm->cs;
							if ( cs && (!cs->option.cachestatic) )
							ecm->dcwstatus = STAT_DCW_WAIT;
							ecm->checktime = ecm->recvtime;
						}
					}
					pthread_mutex_unlock(&prg.lockecm);
					break;

				case PIPE_CACHE_FIND_SUCCESS:  // SET DCW
					peerid = get_cache2ecm(buf, &req, cw);
					pthread_mutex_lock(&prg.lockecm);
					ecm = req.ecm; //search_ecmdata_byhash( req.caid, req.sid, req.hash );
					if (ecm) {
						if ( (ecm->caid==req.caid)&&(ecm->hash==req.hash)&&(ecm->sid==req.sid) ) {
							struct cardserver_data *cs = ecm->cs;
							if (cs) if (cs->option.fallowcache) ecm_setdcw( ecm, cw, DCW_SOURCE_CACHE, peerid );
						}
					}
					pthread_mutex_unlock(&prg.lockecm); 
					break;

				case PIPE_SRV_CONNECTED:
					newserver = 1;
#ifdef EPOLL_ECM
					struct server_data *srv;
					memcpy( &srv, buf+1, sizeof(void*) );
					// Add to events
					struct epoll_event ev; // epoll event
					ev.events = EPOLLIN;
					ev.data.fd = srv->handle;
					ev.data.ptr = srv;
					if ( epoll_ctl(prg.epoll.ecm, EPOLL_CTL_ADD, srv->handle, &ev) == -1 ) debugf(DBG_ERROR,"Err! EPOLL_CTL_ADD %s (%d)\n", srv->user, srv->handle);
					//else debugf(0,"EPOLL_CTL_ADD %s (%d)\n", cli->user, cli->handle);
#endif
					break;

				case PIPE_SRV_AVAILABLE:
					newserver = 1;
					break;
			}
		}

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


///////////////////////////////////////////////////////////////////////////////
// RECEIVE MESSAGES THREAD
///////////////////////////////////////////////////////////////////////////////


inline void srv_recvmsg( struct server_data *srv )
{
	if (srv->type==TYPE_NEWCAMD) cs_srv_recvmsg(srv);
#ifdef CCCAM_CLI
	else if (srv->type==TYPE_CCCAM) cc_srv_recvmsg(srv);
#endif
#ifdef RADEGAST_CLI
	else if (srv->type==TYPE_RADEGAST) rdgd_srv_recvmsg(srv);
#endif
#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
}


#ifdef EPOLL_ECM

void *recv_msg_thread(void *param)
{
	prg.pid_msg = syscall(SYS_gettid);
	prg.tid_msg = pthread_self();
	prctl(PR_SET_NAME,"ECM Thread",0,0,0);

	struct epoll_event evlist[MAX_EPOLL_EVENTS]; // epoll recv events
	prg.epoll.ecm = 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.ecm, EPOLL_CTL_ADD, prg.pipe.ecm[0], &ev) == -1 ) printf("epoll_ctl");

	while (!prg.restart) {
		newserver = 0;
		// getmintime
		uint32_t mintime = ecm_check_time;
		uint32_t ticks = GetTickCount();
		uint32_t ms;
		if (mintime>(ticks+3)) ms = mintime-ticks; else ms = 3;

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

		int i;
		for (i=0; i < ready; i++) {
			if ( evlist[i].events & (EPOLLIN|EPOLLPRI) ) {
				if (evlist[i].data.ptr == NULL) recv_ecm_pipe();
				else {
					pthread_mutex_lock(&prg.locksrv);
					srv_recvmsg(evlist[i].data.ptr);
					pthread_mutex_unlock(&prg.locksrv);
				}
			}
			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 disconnect_srv(evlist[i].data.ptr);
			}
		}

		pthread_mutex_lock(&prg.locksrv);
		pthread_mutex_lock(&prg.lockecm);
		check_sendecm( newserver );
		ecm_check_time = getecmwakeuptime();
		pthread_mutex_unlock(&prg.lockecm);
		pthread_mutex_unlock(&prg.locksrv);

		//usleep(cfg.delay.thread*2);
	}
	return NULL;
}

#else

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

	prg.pid_msg = syscall(SYS_gettid);
	prg.tid_msg = pthread_self();
	prctl(PR_SET_NAME,"ECM Thread",0,0,0);

	while (!prg.restart) {
		// getmintime
		uint32_t mintime = ecm_check_time;
		newserver = 0;

		uint32_t ticks = GetTickCount();
		uint32_t ms;
		if (mintime>(ticks+3)) ms = mintime-ticks; else ms = 3;

		pfdcount = 0;

		// Cache Data// Clients/Servers WakeUP
		pfd[pfdcount].fd = prg.pipe.ecm[0];
		pfd[pfdcount++].events = POLLIN | POLLPRI;

		//Servers
		struct server_data *srv = cfg.server;
		while (srv && (pfdcount<SERVER_MAX_PFD)) {
			if ( !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, ms);


		if ( retval>0 ) {
			/// SERVERS
			pthread_mutex_lock(&prg.locksrv);
			struct server_data *srv = cfg.server;
			while (srv) {
				if ( !IS_DISABLED(srv->flags)&&(srv->handle>0)&&(srv->ipoll>=0)&&(srv->handle==pfd[srv->ipoll].fd) ) {
					if ( pfd[srv->ipoll].revents & (POLLHUP|POLLNVAL) ) disconnect_srv(srv);
					else if ( pfd[srv->ipoll].revents & (POLLIN|POLLPRI) ) srv_recvmsg(srv);
				}
				srv = srv->next;
			}
			pthread_mutex_unlock(&prg.locksrv);
			//
			if ( pfd[0].revents & (POLLIN|POLLPRI) ) recv_ecm_pipe();
		}
		else if ( (retval<0) && (errno!=EINTR) ) {
			debugf(0," thread receive messages: poll error %d(errno=%d)\n", retval, errno);
			usleep(91000);
		}

		pthread_mutex_lock(&prg.locksrv);
		pthread_mutex_lock(&prg.lockecm);
		check_sendecm( newserver );
		ecm_check_time = getecmwakeuptime();
		pthread_mutex_unlock(&prg.lockecm);
		pthread_mutex_unlock(&prg.locksrv);

		usleep(cfg.delay.thread*2);
	}
	return NULL;
}

#endif


// Check for keepalive with servers/clients
void *thread_keepalive(void *param)
{
	while (1) {
		sleep(5);
		uint32_t ticks = GetTickCount();


		// Check Servers
		struct server_data *srv = cfg.server;
		while (srv) {
			if ( !IS_DISABLED(srv->flags)&&(srv->connection.status>0) ) {
				if (srv->type==TYPE_NEWCAMD) cs_check_keepalive(srv);

				else if (srv->type==TYPE_CCCAM) {
					if ( !srv->keepalive.status && ((srv->keepalive.time+75000)<ticks) ) {
						srv->keepalive.status = 1; // Sent and waiting for reply
						srv->keepalive.time = ticks;
						if ( !cc_msg_send( srv->handle, &srv->sendblock, CC_MSG_KEEPALIVE, 0, NULL) ) disconnect_srv( srv );
					}
				}

#ifdef CAMD35_CLI
				else if (srv->type==TYPE_CAMD35) {
					if ( !srv->keepalive.status && ((srv->keepalive.time+30000)<ticks) ) {
						camd35_send_keepalive(srv);
						srv->keepalive.status = 1; // Sent and waiting for reply
						srv->keepalive.time = ticks;
					}
					else if ( (srv->keepalive.status==1) && ((srv->keepalive.time+10000)<ticks) ) {
						camd35_send_keepalive(srv);
						srv->keepalive.status = 2;
						srv->keepalive.time = ticks;
					}
					else if ( (srv->keepalive.status>1) && ((srv->keepalive.time+10000)<ticks) ) {
						debugf(getdbgflag(DBG_SERVER,0,srv->id)," ??? no keepalive response from camd35 server (%s:%d)\n",srv->host->name,srv->port);
						disconnect_srv( srv );
					}
				}
#endif
			}
			srv = srv->next;
		}


		// Check Cacheex Servers
		srv = cfg.cacheexserver;
		while (srv) {
			if ( !IS_DISABLED(srv->flags)&&(srv->connection.status>0) ) {
				if (srv->type==TYPE_CCCAM) {
					if ( !srv->keepalive.status && ((srv->keepalive.time+75000)<ticks) ) {
						srv->keepalive.status = 1; // Sent and waiting for reply
						srv->keepalive.time = ticks;
						if ( !cc_msg_send( srv->handle, &srv->sendblock, CC_MSG_KEEPALIVE, 0, NULL) ) disconnect_srv( srv );
					}
				}

#ifdef CAMD35_CLI
				else if (srv->type==TYPE_CAMD35) {
					if ( !srv->keepalive.status && ((srv->keepalive.time+30000)<ticks) ) {
						camd35_send_keepalive(srv);
						srv->keepalive.status = 1; // Sent and waiting for reply
						srv->keepalive.time = ticks;
					}
					else if ( (srv->keepalive.status==1) && ((srv->keepalive.time+10000)<ticks) ) {
						camd35_send_keepalive(srv);
						srv->keepalive.status = 2;
						srv->keepalive.time = ticks;
					}
					else if ( (srv->keepalive.status>1) && ((srv->keepalive.time+10000)<ticks) ) {
						debugf(getdbgflag(DBG_SERVER,0,srv->id)," ??? no keepalive response from camd35 server (%s:%d)\n",srv->host->name,srv->port);
						disconnect_srv( srv );
					}
				}
#endif
			}
			srv = srv->next;
		}


#ifdef CAMD35_SRV
		sleep(1);
		// Check camd35 Clients
		ticks = GetTickCount();
		struct camd35_server_data *camd35 = cfg.camd35.server;
		while (camd35) {
			struct camd35_client_data *cli = camd35->client;
			while (cli) {
				if (cli->connection.status>0) {
					if ( (cli->lastactivity+300000) < ticks) {
						camd35_disconnect_cli(cli);
					}
				}
				cli = cli->next;
			}
			camd35 = camd35->next;
		}
#endif

	}
}


int start_thread_recv_msg()
{
	create_thread(&prg.tid_msg, (threadfn)recv_msg_thread,NULL);
	create_thread(&prg.tid_msg, (threadfn)thread_keepalive,NULL);
#ifdef THREAD_DCW
	create_thread(&prg.tid_msg, (threadfn)setdcw_thread,NULL);
#endif
	return 0;
}