./ MultiCS.r82 / srv-mgcamd.c
/////
// File: srv-mgcamd.c
// Modification date: 2011.11.11
/////


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

void mg_senddcw_cli(struct mg_client_data *cli);


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

struct mg_client_data *getmgcamdclientbyid(uint32_t id)
{
	struct mgcamdserver_data *mgcamd = cfg.mgcamd.server;
	while (mgcamd) {
		if (!(mgcamd->flags&FLAG_DELETE)) {
			struct mg_client_data *cli = mgcamd->client;
			while (cli) {
				if (!(cli->flags&FLAG_DELETE))
					if (cli->id==id) return cli;
				cli = cli->next;
			}
		}
		mgcamd = mgcamd->next;
	}
	return NULL;
}

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

///////////////////////////////////////////////////////////////////////////////
// DISCONNECT
///////////////////////////////////////////////////////////////////////////////

void mg_disconnect_cli(struct mg_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(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' disconnected\n", cli->user);
}


void mg_sendcard_add(struct cardserver_data *cs, struct mgcamdserver_data *mgcamd, struct mg_client_data *cli)
{
	uint8_t buf[4];
	struct cs_custom_data clicd; // Custom data
	if ( cs && cs->option.fsharemgcamd ) {
		int len;
		for ( len=0; len<cs->card.nbprov; len++) {
			clicd.sid = mgcamd->port;
			clicd.caid = cs->card.caid; 
			clicd.provid = cs->card.prov[len].id;
			buf[0] = EXT_ADD_CARD; buf[1]=0; buf[2]=0;
			if ( card_sharelimits(cli->sharelimits, clicd.caid, clicd.provid) )
				if ( !cs_message_send(cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
		}
	}
}

void mg_sendcard_del(struct cardserver_data *cs, struct mgcamdserver_data *mgcamd, struct mg_client_data *cli)
{
	uint8_t buf[4];
	struct cs_custom_data clicd; // Custom data
	if ( cs && cs->option.fsharemgcamd ) {
		int len;
		for ( len=0; len<cs->card.nbprov; len++) {
			clicd.sid = mgcamd->port;
			clicd.caid = cs->card.caid; 
			clicd.provid = cs->card.prov[len].id;
			buf[0] = EXT_REMOVE_CARD; buf[1]=0; buf[2]=0;
			if ( card_sharelimits(cli->sharelimits, clicd.caid, clicd.provid) )
				cs_message_send(cli->handle, &clicd, buf, 3, cli->sessionkey);
		}
	}
}

///////////////////////////////////////////////////////////////////////////////
// CONNECT
///////////////////////////////////////////////////////////////////////////////

void *mg_connect_cli(struct connect_cli_data *param)
{
    char passwdcrypt[120];
	unsigned char keymod[14];
	int i,index;
	unsigned char sessionkey[16];
	struct cs_custom_data clicd;
	unsigned char buf[CWS_NETMSGSIZE];

	struct mgcamdserver_data *mgcamd = param->server;
	int sock = param->sock;
	uint32_t ip = param->ip;
	free(param);

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

	// Create random deskey
	for (i=0; i<14; i++) keymod[i] = 0xff & rand();
	// Create Multics ID
	keymod[3] = (keymod[0]^'M') + keymod[1] + keymod[2];
	keymod[7] = keymod[4] + (keymod[5]^'C') + keymod[6];
	keymod[11] = keymod[8] + keymod[9] + (keymod[10]^'S');
	// send random des key
	if ( !send_nonb(sock, keymod, 14, 500) ) {
		debugf(getdbgflag(DBG_MGCAMD,0,0)," mgcamd: error sending init sequence\n");
		close(sock);
		return NULL;
	}

	// Calc SessionKey
	des_login_key_get(keymod, mgcamd->key, 14, sessionkey);

//STAT: LOGIN INFO
	// 3. login info
	i = cs_message_receive(sock, &clicd, buf, sessionkey,5000);
	if (i<=0) {
		if (i==-2) debugf(getdbgflag(DBG_MGCAMD,0,0)," mgcamd: (%s) new connection closed, wrong des key\n", ip2string(ip));
		else debugf(getdbgflag(DBG_MGCAMD,0,0)," mgcamd: (%s) new connection closed, receive timeout\n", ip2string(ip));
		close(sock);
		return NULL;
	}

	if (buf[0]!=MSG_CLIENT_2_SERVER_LOGIN) {
		close(sock);
		return NULL;
	}

	// Check username length
	if ( strlen( (char*)buf+3 )>63 ) {
		/*
		buf[0] = MSG_CLIENT_2_SERVER_LOGIN_NAK;
		buf[1] = 0;
		buf[2] = 0;
		cs_message_send(sock, NULL, buf, 3, sessionkey);
		*/
		debugf(getdbgflag(DBG_MGCAMD,0,0)," newcamd: wrong username length (%s)\n", ip2string(ip));
		close(sock);
		return NULL;
	}

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

	pthread_mutex_lock(&prg.lockclimg);
	index = 3;
	struct mg_client_data *cli = mgcamd->client;
	int found = 0;
	char *name = (char*)(buf+index);
	uint32_t hash = hashCode( (unsigned char *)name, strlen(name) );
	while (cli) {
		if (cli->userhash==hash)
		if (!strcmp(cli->user, name)) {
			if (IS_DISABLED(cli->flags)) { // Connect only enabled clients
				debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: connection refused for client '%s' (%s), client disabled\n", cli->user, ip2string(ip));
				pthread_mutex_unlock(&prg.lockclimg);
				close(sock);
				return NULL;
			}
			found=1;
			break;
		}
		cli = cli->next;
	}
	if (!found) {
		buf[0] = MSG_CLIENT_2_SERVER_LOGIN_NAK;
		buf[1] = 0;
		buf[2] = 0;
		//cs_message_send(sock, NULL, buf, 3, sessionkey);
		debugf(getdbgflag(DBG_MGCAMD,0,0)," mgcamd: unknown user '%s' (%s)\n", &buf[3], ip2string(ip));
		pthread_mutex_unlock(&prg.lockclimg);
		close(sock);
		return NULL;
	}

	// Check for Host
	if (cli->host) {
		struct host_data *host = cli->host;
		host->clip = ip;
		if ( host->ip && (host->ip!=ip) ) {
			uint sec = getseconds()+60;
			if ( host->checkiptime > sec ) host->checkiptime = sec;
			debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: Client '%s' (%s), ip refused\n",cli->user, ip2string(ip)); 
			cli->nbloginerror++;
			pthread_mutex_unlock(&prg.lockclimg);
			close(sock);
			return NULL;
		}
	}

	// Check password
	index += strlen(cli->user) +1;
	__md5_crypt(cli->pass, "$1$abcdefgh$",passwdcrypt);
	if (!strcmp(passwdcrypt,(char*)&buf[index])) {
		if (cli->ip!=ip) cli->nbdiffip++;
		//Check Reconnection
		if (cli->connection.status>0) {
			if ( (GetTickCount()-cli->connection.time) > 60000 ) {
				mg_disconnect_cli(cli);
				if (cli->ip==ip) debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: Client '%s' (%s) already connected\n",cli->user, ip2string(ip));
				else {
					debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: Client '%s' (%s) already connected with different ip, Connections closed (%s)\n", cli->user, ip2string(cli->ip), ip2string(ip));
					cli->nbloginerror++;
					pthread_mutex_unlock(&prg.lockclimg);
					close(sock);
					return NULL;
				}
			}
			else {
				debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: Client '%s' just connected, new connection closed (%s)\n", cli->user, ip2string(ip));
				cli->nbloginerror++;
				pthread_mutex_unlock(&prg.lockclimg);
				close(sock);
				return NULL;
			}
		}

		cli->nblogin++;

#ifdef IPLIST
		if (ipdata) iplist_goodlogin(ipdata);
#endif
		// Store program id
		cli->progid = clicd.sid;

		buf[0] = MSG_CLIENT_2_SERVER_LOGIN_ACK;
		buf[1] = 0;
		buf[2] = 0;

		//clicd.msgid = 0;
		clicd.sid = 0x6E73;
		clicd.caid = 0;
		clicd.provid = 0x14000000; // mgcamd protocol version?
		cs_message_send(sock, &clicd, buf, 3, sessionkey);
		//
		des_login_key_get( mgcamd->key, (unsigned char*)passwdcrypt, strlen(passwdcrypt),sessionkey);
		memcpy( &cli->sessionkey, &sessionkey, 16);
		// Setup User data
		cli->msg.len = 0;
		cli->handle = sock;
		cli->ip = ip;
		memset( &cli->ecm, 0, sizeof(cli->ecm) );
		cli->connection.status = 1;
		cli->connection.time = GetTickCount();
		cli->lastactivity = GetTickCount();
		cli->lastecmtime = 0;
		cli->chkrecvtime = 0;
		debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' connected (%s)\n", cli->user, ip2string(ip));
		pthread_mutex_unlock(&prg.lockclimg);
#ifdef EPOLL_MGCAMD
		pipe_pointer( prg.pipe.mgcamd[1], PIPE_CLI_CONNECTED, cli );
#else
		pipe_wakeup( prg.pipe.mgcamd[1] );
#endif
		// update pfd data
		cli->parent->clipfd.update = 1;
	}
	else {
		// send NAK
		buf[0] = MSG_CLIENT_2_SERVER_LOGIN_NAK;
		buf[1] = 0;
		buf[2] = 0;
		//cs_message_send(sock, NULL, buf, 3, sessionkey);
		debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' wrong password (%s)\n", cli->user, ip2string(ip));
		cli->nbloginerror++;
		pthread_mutex_unlock(&prg.lockclimg);
		close(sock);
	}
	return NULL;
}



void mgcamd_srv_accept(struct mgcamdserver_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_MGCAMD,0,0)," Mgcamd%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_MGCAMD,0,0)," Mgcamd%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)," Mgcamd%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_MGCAMD,0,0)," Mgcamd%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)mg_connect_cli,newdata) ) {
				free( newdata );
				close( newfd );
			}
		}
	}
}

#ifndef MONOTHREAD_ACCEPT

void *mgcamd_accept_thread(void *param)
{
	prctl(PR_SET_NAME,"MGcamd Accept Thread",0,0,0);
	sleep(5);

	while(!prg.restart) {

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

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

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

#endif

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


void mg_store_ecmclient( ECM_DATA *ecm, struct mg_client_data *cli, int climsgid)
{
	cli->ecm.recvtime = GetTickCount();
	cli->ecm.request = ecm;
    cli->ecm.climsgid = climsgid;
    cli->ecm.status = STAT_ECM_SENT;
	ecm_addip(ecm, cli->ip);
}



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

int mg_cli_getmsg(struct mg_client_data *cli, uint8_t *buf, struct cs_custom_data *clicd)
{
	int len;
	if (cli->handle<=0) return 0; // disconnected

#ifdef RECVMSG_PEEK

	len = cs_msg_chkrecv(cli->handle);
	if (len==0) {
		debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' read failed %d\n", cli->user,len);
		mg_disconnect_cli(cli);
	}
	else if (len==-1) {
		if (!cli->chkrecvtime) cli->chkrecvtime = GetTickCount();
		else if ( (cli->chkrecvtime+300)<GetTickCount() ) {
			debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' read failed %d\n", cli->user,len);
			mg_disconnect_cli(cli);
		}
	}
	else if (len>0) {
		cli->chkrecvtime = 0;
		len = cs_message_receive(cli->handle, clicd, buf, cli->sessionkey,3);
		if (len==0) {
			debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' read failed %d\n", cli->user,len);
			mg_disconnect_cli(cli);
		}
		else if (len<0) {
			debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' read failed %d(%d)\n", cli->user,len,errno);
			mg_disconnect_cli(cli);
		}
		else return len;
	}

#else 
	len = cs_peekmsg( cli->handle, &cli->msg, cli->sessionkey, clicd, buf );
	uint32_t ticks = GetTickCount();
	if (len==0) {
		debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' read failed %d\n", cli->user,len);
		mg_disconnect_cli(cli);
	}
	else if (len==-1) {
		if (!cli->chkrecvtime) cli->chkrecvtime = ticks;
		else if ( (cli->chkrecvtime+1000)<ticks ) {
			debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: client '%s' read failed %d\n", cli->user,len);
			mg_disconnect_cli(cli);
		}
	}
	else return len;
#endif

	return 0;
}


void mg_cli_recvmsg(struct mg_client_data *cli)
{
	struct cs_custom_data clicd; // Custom data
	struct cardserver_data *cs=NULL; 
	int len;
	unsigned char buf[CWS_NETMSGSIZE];

	unsigned char ecmdata[CWS_NETMSGSIZE]; // for other use
	int ecmlen;

	len = mg_cli_getmsg(cli, buf, &clicd);
	if (len<=0) return;

	uint32_t ticks = GetTickCount();
	cli->chkrecvtime = 0;
	cli->lastactivity = ticks;
	struct mgcamdserver_data *mgcamd = cli->parent;

	switch ( buf[0] ) {

		case EXT_GET_VERSION:
			memset( buf, 0, sizeof(buf) );
			buf[0] = EXT_GET_VERSION; buf[1]=0; buf[2]=0;
			buf[3] = 0x31;
			buf[4] = 0x2e;
			buf[5] = 0x36;
			buf[6] = 0x37;// 1.67
			if ( !cs_message_send(cli->handle, 0, buf, 7, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
			break;

		case MSG_CARD_DATA_REQ:
			memset( buf, 0, sizeof(buf) );
			buf[0] = MSG_CARD_DATA; buf[1]=0; buf[2]=0;
			buf[3] = 2;
			buf[4] = 0;
			buf[5] = 0;
			buf[14] = 1;
			if ( !cs_message_send(cli->handle, &clicd, buf, 15+11, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
			if (cli->csport[0]) {
				int i;
				for (i=0;i<MAX_CSPORTS;i++) {
					if (!cli->csport[i]) break;
					cs = getcsbyport(cli->csport[i]);
					if ( cs && cs->option.fsharemgcamd && !(cli->flags&FLAG_EXPIRED) )
					for (len=0; len<cs->card.nbprov; len++) {
						clicd.sid = mgcamd->port;
						clicd.caid = cs->card.caid; 
						clicd.provid = cs->card.prov[len].id;
						buf[0] = EXT_ADD_CARD; buf[1]=0; buf[2]=0;
						if ( card_sharelimits(cli->sharelimits, clicd.caid, clicd.provid) )
							if ( !cs_message_send(cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
					}
				}
			}
			else if (mgcamd->csport[0]) {
				int i;
				for(i=0;i<MAX_CSPORTS;i++) {
					if (!mgcamd->csport[i]) break;
					cs = getcsbyport(mgcamd->csport[i]);
					if ( cs && cs->option.fsharemgcamd && !(cli->flags&FLAG_EXPIRED) )
					for (len=0; len<cs->card.nbprov; len++) {
						clicd.sid = mgcamd->port;
						clicd.caid = cs->card.caid; 
						clicd.provid = cs->card.prov[len].id;
						buf[0]=EXT_ADD_CARD; buf[1]=0; buf[2]=0;
						if ( card_sharelimits(cli->sharelimits, clicd.caid, clicd.provid) )
							if ( !cs_message_send(cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
					}
				}
			}
			else {
				cs = cfg.cardserver;
				while (cs) {
					if ( cs->option.fsharemgcamd && !(cli->flags&FLAG_EXPIRED) )
					for (len=0; len<cs->card.nbprov; len++) {
						clicd.sid = mgcamd->port;
						clicd.caid = cs->card.caid; 
						clicd.provid = cs->card.prov[len].id;
						buf[0]=EXT_ADD_CARD; buf[1]=0; buf[2]=0;
						if ( card_sharelimits(cli->sharelimits, clicd.caid, clicd.provid) )
							if ( !cs_message_send(cli->handle, &clicd, buf, 3, cli->sessionkey) ) {	mg_disconnect_cli( cli ); return; }
					}
					cs = cs->next;
				}
			}

			debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: send card data to client '%s'\n", cli->user);
			cli->cardsent = 1;
			break;

		case 0x80:
		case 0x81:
					//debugdump(buf, len, "ECM: ");
					cli->lastecmtime = ticks;
					//cli->ecmnb++; new var for not accepted ecm's
					cli->ecmnb++;
					ecmlen = len;
					memcpy( ecmdata, buf, len);
					uint32_t provid = ecm_getprovid( ecmdata, clicd.caid );
					if (provid!=0) clicd.provid = provid;

					if (cli->ecm.busy) {
						cli->ecmdenied++;
						// send decode failed
						buf[1] = 0; buf[2] = 0;
						if ( !cs_message_send( cli->handle, &clicd, buf, 3, cli->sessionkey) )  { mg_disconnect_cli( cli ); return; }
						debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," <|> decode failed to mgcamd client '%s' ch %04x:%06x:%04x, too many ecm requests\n", cli->user,clicd.caid,clicd.provid,clicd.sid);
						break;
					}
					// Check for CAID&SID
					if ( !clicd.caid ) {
						cli->ecmdenied++;
						// send decode failed
						buf[1] = 0; buf[2] = 0;
						if ( !cs_message_send( cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
						debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," <|> decode failed to mgcamd client '%s' ch %04x:%06x:%04x Invalid CAID\n", cli->user,clicd.caid,clicd.provid,clicd.sid);
						break;
					}
					int i,j,port;
					if (cli->csport[0]) {
						for(i=0; i<MAX_CSPORTS; i++) {
							port = cli->csport[i];
							if (!port) break;
							cs = getcsbyport(port);
							if (cs)
							if (cs->option.fsharemgcamd)
							if (clicd.caid==cs->card.caid) {
								for (j=0; j<cs->card.nbprov;j++) if (clicd.provid==cs->card.prov[j].id) break;
								if (j<cs->card.nbprov) break;
							}
						}
						if (!port || !cs) {
							cli->ecmdenied++;
							// send decode failed
							buf[1] = 0; buf[2] = 0;
							if ( !cs_message_send( cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
							debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," <|> decode failed to client '%s' ch %04x:%06x:%04x, Invalid CAID/PROVIDER\n", cli->user,clicd.caid,clicd.provid,clicd.sid);
							break;
						}
					}
					else {
						cs = cfg.cardserver;
						while (cs) {
							if (cs->option.fsharemgcamd)
							if (clicd.caid==cs->card.caid) {
								for (j=0; j<cs->card.nbprov;j++) if (clicd.provid==cs->card.prov[j].id) break;
								if (j<cs->card.nbprov) break;
							}
							cs = cs->next;
						}
						if (!cs) {
							cli->ecmdenied++;
							// send decode failed
							buf[1] = 0; buf[2] = 0;
							if ( !cs_message_send( cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
							debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," <|> decode failed to client '%s' ch %04x:%06x:%04x, Invalid CAID/PROVIDER\n", cli->user,clicd.caid,clicd.provid,clicd.sid);
							break;
						}
					}
					// Check for Accepted sids
					uint8_t cw1cycle;
					if ( !accept_sid(cs, clicd.provid, clicd.sid, ecm_getchid(ecmdata,clicd.caid), ecmlen, &cw1cycle) ) {
						cli->ecmdenied++;
						cs->ecmdenied++;
						// send decode failed
						buf[1] = 0; buf[2] = 0;
						if ( !cs_message_send( cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,cs->id)," <|> decode failed to mgcamd client '%s' ch %04x:%06x:%04x SID not accepted\n", cli->user,clicd.caid,clicd.provid,clicd.sid);
						break;
					}

					if ( !accept_ecmlen(len) ) {
						cli->ecmdenied++;
						cs->ecmdenied++;
						// send decode failed
						buf[1] = 0; buf[2] = 0;
						if ( !cs_message_send( cli->handle, &clicd, buf, 3, cli->sessionkey) ) { mg_disconnect_cli( cli ); return; }
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,cs->id)," <|> decode failed to mgcamd client '%s' ch %04x:%06x:%04x, ecm length error(%d)\n", cli->user,clicd.caid,clicd.provid,clicd.sid,len);
						break;
					}

					// ACCEPTED
					pthread_mutex_lock(&prg.lockecm); //###

					// Search for ECM
					ECM_DATA *ecm = search_ecmdata_any(cs, ecmdata, ecmlen, clicd.sid, clicd.caid); // dont get failed ecm request from cache
					int isnew =  ( ecm==NULL );
					if (ecm) {
						ecm->lastrecvtime = ticks;
						if (ecm->dcwstatus==STAT_DCW_FAILED) {
							if (ecm->period > cs->option.dcw.retry) {
								// send decode failed
								buf[1] = 0; buf[2] = 0;
								cs_message_send(cli->handle, &clicd, buf, 3, cli->sessionkey);
								debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,cs->id)," <|> decode failed to mgcamd client '%s' ch %04x:%06x:%04x, already failed\n", cli->user,clicd.caid,clicd.provid,clicd.sid);
							}
							else {
								ecm->period++; // RETRY
								mg_store_ecmclient(ecm, cli, clicd.msgid);
								debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id, cs->id)," <- ecm from mgcamd client '%s' ch %04x:%06x:%04x:%08x**\n",cli->user,clicd.caid,clicd.provid,clicd.sid,ecm->hash);
								cli->ecm.busy=1;
								cli->ecm.hash = ecm->hash;
								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 {
							//TODO: Add another card for sending ecm
							mg_store_ecmclient(ecm, cli, clicd.msgid);
							debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id, cs->id)," <- ecm from mgcamd client '%s' ch %04x:%06x:%04x:%08x*\n",cli->user,clicd.caid,clicd.provid,clicd.sid,ecm->hash);
							cli->ecm.busy=1;
							cli->ecm.hash = ecm->hash;
							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); //###
										mg_senddcw_cli(cli);
										break;
									}
								}
								else {
									pthread_mutex_unlock(&prg.lockecm); //###
									mg_senddcw_cli(cli);
									break;
								}
							}
						}
					}
					else {
						cs->ecmaccepted++;
						// Setup ECM Request for Server(s)
						ecm = store_ecmdata(cs, ecmdata, ecmlen, clicd.sid,clicd.caid,clicd.provid);
						mg_store_ecmclient(ecm, cli, clicd.msgid);
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id, cs->id)," <- ecm from mgcamd client '%s' ch %04x:%06x:%04x:%08x\n",cli->user,clicd.caid,clicd.provid,clicd.sid,ecm->hash);
						cli->ecm.busy=1;
						cli->ecm.hash = ecm->hash;
						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 Mgcamd 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;

#ifdef SRV_CSCACHE
				// Incoming DCW from client
		case 0xC0:
		case 0xC1:
					cli->lastecmtime = ticks;
					if (!cli->ecm.busy) break;

					pthread_mutex_lock(&prg.lockecm); //###

					ecm = cli->ecm.request;
					if (!ecm) {
						debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," <!= wrong cw from Client '%s' ch %04x:%06x:%04x\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->lastecmtime);
						pthread_mutex_unlock(&prg.lockecm);
						break;
					}
					if (ecm->hash!=cli->ecm.hash) {
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," <!= wrong cw from Client '%s' ch %04x:%06x:%04x\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->lastecmtime);
						pthread_mutex_unlock(&prg.lockecm);
						break;
					}
					if ( (ecm->caid!=clicd.caid) || (ecm->sid!=clicd.sid) || (ecm->provid!=clicd.provid) || (cli->ecm.climsgid!=clicd.msgid) ) {
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," <!= wrong cw from Client '%s' ch %04x:%06x:%04x\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->lastecmtime);
						pthread_mutex_unlock(&prg.lockecm);
						break;
					}
					if ( (buf[0]&0x81)!=ecm->ecm[0] ) {
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," <!= wrong cw from Client '%s' ch %04x:%06x:%04x\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->lastecmtime);
						pthread_mutex_unlock(&prg.lockecm);
						break;
					}
					if (buf[2]!=0x10) {
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," <!= wrong cw from Client '%s' ch %04x:%06x:%04x\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->lastecmtime);
						pthread_mutex_unlock(&prg.lockecm);
						break;
					}
					// Check for DCW
					if (!acceptDCW(&buf[3])) {
						debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," <!= wrong cw from Client '%s' ch %04x:%06x:%04x\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->lastecmtime);
						pthread_mutex_unlock(&prg.lockecm);
						break;
					}
					debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," <= cw from mgcamd client '%s' ch %04x:%06x:%04x (%dms)\n", cli->user, ecm->caid,ecm->provid,ecm->sid, ticks-cli->lastecmtime);
					if (ecm->dcwstatus!=STAT_DCW_SUCCESS) {
						static char msg[] = "Good dcw from mgcamd Client";
						ecm->statusmsg = msg;
						// Store ECM Answer
						ecm_setdcw( ecm, &buf[3], DCW_SOURCE_MGCLIENT, cli->id );
					}
					else {	//TODO: check same dcw between cards
						if ( memcmp(&ecm->cw, &buf[3],16) ) debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," !!! different dcw from newcamd client '%s'\n", cli->user);
					}

					pthread_mutex_unlock(&prg.lockecm); //###
					break;
#endif

		default:
					if (buf[0]==MSG_KEEPALIVE) {
#ifdef SRV_CSCACHE
						// Check for Cache client??
						if (clicd.sid==(('C'<<8)|'H')) { clicd.caid = ('O'<<8)|'K'; cli->cachedcw++; }
#endif
						if ( !cs_message_send(cli->handle, &clicd, buf, 3, cli->sessionkey) ) mg_disconnect_cli( cli );
					}
					else {
						debugf(getdbgflag(DBG_MGCAMD,0,cli->id)," mgcamd: unknown message type '%02x' from client '%s'\n",buf[0],cli->user);
						buf[1]=0; buf[2]=0;
						if ( !cs_message_send( cli->handle, &clicd, buf, 3, cli->sessionkey) ) mg_disconnect_cli( cli );
					}

	} // Switch
}

///////////////////////////////////////////////////////////////////////////////
// MGCAMD SERVER:  SEND DCW TO CLIENTS
///////////////////////////////////////////////////////////////////////////////

void mg_senddcw_cli(struct mg_client_data *cli)
{
	unsigned char buf[CWS_NETMSGSIZE];
	struct cs_custom_data clicd; // Custom data

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

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

	clicd.msgid = cli->ecm.climsgid;
	clicd.sid = ecm->sid;
	clicd.caid = ecm->caid;
	clicd.provid = ecm->provid;

	if ( (ecm->hash==cli->ecm.hash)&&(ecm->dcwstatus==STAT_DCW_SUCCESS) ) {
		cli->lastecm.dcwsrctype = ecm->dcwsrctype;
		cli->lastecm.dcwsrcid = ecm->dcwsrcid;
		cli->lastecm.status=1;
		cli->ecmok++;
		cli->ecmoktime += GetTickCount()-cli->ecm.recvtime;
		buf[0] = ecm->ecm[0];
		buf[1] = 0;
		buf[2] = 0x10;
		memcpy( &buf[3], ecm->cw, 16 );
		if ( !cs_message_send( cli->handle, &clicd, buf, 19, cli->sessionkey) ) mg_disconnect_cli( cli );
		else debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," => cw to mgcamd client '%s' ch %04x:%06x:%04x (%dms)\n", cli->user, ecm->caid,ecm->provid,ecm->sid, GetTickCount()-cli->ecm.recvtime);
		cli->lastdcwtime = GetTickCount();
	}
	else { //if (ecm->data->dcwstatus==STAT_DCW_FAILED)
		if (enablefreeze) cli->freeze++;
		cli->lastecm.status=0;
		cli->lastecm.dcwsrctype = DCW_SOURCE_NONE;
		cli->lastecm.dcwsrcid = 0;
		buf[0] = ecm->ecm[0];
		buf[1] = 0;
		buf[2] = 0;
		if ( !cs_message_send(  cli->handle, &clicd, buf, 3, cli->sessionkey) ) mg_disconnect_cli( cli );
		else debugf(getdbgflagpro(DBG_MGCAMD,0,cli->id,ecm->cs->id)," |> decode failed to mgcamd client '%s' ch %04x:%06x:%04x (%dms)\n", cli->user, ecm->caid,ecm->provid,ecm->sid, GetTickCount()-cli->ecm.recvtime);
	}
	cli->ecm.busy=0;
	cli->ecm.status = STAT_DCW_SENT;
}

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

// Check sending cw to clients
void mg_check_sendcw(ECM_DATA *ecm)
{
	struct mgcamdserver_data *mgcamd = cfg.mgcamd.server;
	while (mgcamd) {
		if ( !IS_DISABLED(mgcamd->flags) && (mgcamd->handle>0) ) {
			struct mg_client_data *cli = mgcamd->client;
			while (cli) {
				if ( !IS_DISABLED(cli->flags)&&(cli->handle!=INVALID_SOCKET)&&(cli->ecm.busy)&&(cli->ecm.request==ecm)&&(cli->ecm.status==STAT_ECM_SENT) ) {
					mg_senddcw_cli( cli );
				}
				cli = cli->next;
			}
		}
		mgcamd = mgcamd->next;
	}
}


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

void mg_recv_pipe()
{
	struct mgcamdserver_data *mgcamd;
	struct cardserver_data *cs;

	struct mg_client_data *cli;

	uint8_t buf[1024];
	struct pollfd pfd[2];
	do {
		int len = pipe_recv( prg.pipe.mgcamd[0], buf);
		if (len>0) {
			switch(buf[0]) {
				case PIPE_WAKEUP:  // ADD NEW CLIENT
					//debugf(" wakeup csmsg\n");
					break;
#ifdef EPOLL_MGCAMD
				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.mgcamd, EPOLL_CTL_ADD, cli->handle, &ev) == -1 ) debugf(DBG_ERROR,"Err! EPOLL_CTL_ADD %s (%d)\n", cli->user, cli->handle);
					//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) {
						mgcamd = cfg.mgcamd.server;
						while (mgcamd) {
							if ( !IS_DISABLED(mgcamd->flags)&&(mgcamd->handle>0) ) {
								struct mg_client_data *cli = mgcamd->client;
								while (cli) {
									if ( !IS_DISABLED(cli->flags)&&(cli->handle>0) ) {
										mg_sendcard_del(cs, mgcamd, cli);
									}
									cli = cli->next;
								}
							}
							mgcamd = mgcamd->next;
						}
					}
					break;

				case PIPE_CARD_ADD:
					memcpy( &cs, buf+1, sizeof(void*) );
					if (cs) {
						mgcamd = cfg.mgcamd.server;
						while (mgcamd) {
							if ( !IS_DISABLED(mgcamd->flags)&&(mgcamd->handle>0) ) {
								struct mg_client_data *cli = mgcamd->client;
								while (cli) {
									if ( !IS_DISABLED(cli->flags)&&(cli->handle>0) ) {
										mg_sendcard_add(cs, mgcamd, cli);
									}
									cli = cli->next;
								}
							}
							mgcamd = mgcamd->next;
						}
					}
					break;

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

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

#ifdef EPOLL_MGCAMD

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

	cfg.mgcamd.pid_recvmsg = syscall(SYS_gettid);
	prg.pid_mg_msg = syscall(SYS_gettid);
	prctl(PR_SET_NAME,"Mgcamd RecvMSG",0,0,0);

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

	prg.epoll.mgcamd = 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.mgcamd, EPOLL_CTL_ADD, prg.pipe.mgcamd[0], &ev) == -1 ) printf("epoll_ctl");

	while(1) {
		int ready = epoll_wait( prg.epoll.mgcamd, evlist, MAX_EPOLL_EVENTS, 1003);
		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) mg_recv_pipe();
				else mg_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 mg_disconnect_cli(evlist[i].data.ptr);
			}
		}
	}
	return NULL;
}

#else

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

	cfg.mgcamd.pid_recvmsg = syscall(SYS_gettid);
	prg.pid_mg_msg = syscall(SYS_gettid);
	prctl(PR_SET_NAME,"MGcamd RecvMSG",0,0,0);

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

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

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

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

			struct mgcamdserver_data *mgcamd = cfg.mgcamd.server;
			while (mgcamd) {
				if ( !IS_DISABLED(mgcamd->flags)&&(mgcamd->handle>0)&&(mgcamd->clipfd.count>0) ) {
					pthread_mutex_lock(&prg.lockclimg);
					struct mg_client_data *mgcli = mgcamd->client;
					while (mgcli) {
						if ( !IS_DISABLED(mgcli->flags)&&(mgcli->handle>0)&&(mgcli->ipoll>=0)&&(mgcli->handle==pfd[mgcamd->clipfd.ipoll+mgcli->ipoll].fd) ) {
							if ( pfd[mgcamd->clipfd.ipoll+mgcli->ipoll].revents & (POLLHUP|POLLNVAL) ) mg_disconnect_cli(mgcli);
							else if ( pfd[mgcamd->clipfd.ipoll+mgcli->ipoll].revents & (POLLIN|POLLPRI) ) {
								mg_cli_recvmsg(mgcli);
							}
							///else if ( (GetTickCount()-mgcli->lastactivity) > 600000 ) mg_disconnect_cli(mgcli);
						}
						mgcli = mgcli->next;
					}
					pthread_mutex_unlock(&prg.lockclimg);
				}
				mgcamd = mgcamd->next;
			}
			//
			if ( pfd[0].revents & (POLLIN|POLLPRI) ) mg_recv_pipe();
		}
		else if ( retval<0 ) {
			debugf(0, " thread receive messages: poll error %d(errno=%d)\n", retval, errno);
			usleep(99000);
		}

	}
	return NULL;
}

#endif

///////////////////////////////////////////////////////////////////////////////
// MGCAMD SERVER: START/STOP
///////////////////////////////////////////////////////////////////////////////

int start_thread_mgcamd()
{
	pthread_t tid;
#ifndef MONOTHREAD_ACCEPT
	create_thread(&tid, mgcamd_accept_thread,NULL);
#endif

	create_thread(&cfg.mgcamd.tid_recvmsg, mg_recvmsg_thread,NULL);
	return 0;
}