/*
 * Softcam plugin to VDR (C++)
 *
 * This code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
 */

#include "stdafx.h"

#include <string.h>

#include "common.h"
#include "cc.h"
#include "data.h"
#include "network.h"

// -- cCardClientRadegast ------------------------------------------------------

class cCardClientRadegast : public cCardClient {
private:
	cNetSocket so;
protected:
	virtual bool Login(void);
public:
	cCardClientRadegast(const char *Name);
	virtual bool Init(const char *config);
	virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw);
};

static cCardClientLinkReg<cCardClientRadegast> __rdg("Radegast");

cCardClientRadegast::cCardClientRadegast(const char *Name)
:cCardClient(Name)
,so(DEFAULT_CONNECT_TIMEOUT,7,DEFAULT_IDLE_TIMEOUT)
{}

bool cCardClientRadegast::Init(const char *config)
{
	cMutexLock lock(this);
	so.Disconnect();
	return ParseStdConfig(config);
}

bool cCardClientRadegast::Login(void)
{
	so.Disconnect();
	if(!so.Connect(hostname,port)) return false;
	dc(printf("cc-radegast: connected to %s:%d\n",hostname,port))
	return true;
}

bool cCardClientRadegast::ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw)
{
	cMutexLock lock(this);
	so.Flush();
	const int len=SCT_LEN(source);
	int keynr;
	switch(ecm->caId>>8) {
		case 0x01: // Seca
			keynr=source[7]&0x0F; break;
		case 0x05: // Viaccess
			keynr=source[4]&0x0F; break;
		default:
			keynr=0; break;
	}
	unsigned char buff[256+32];
	buff[0]='\1';                        // CMD_ECM_KEY_ASK
	buff[1]=len+28;                      // len
	buff[2]=2;                           // ECM_SYSTEM_ID
	buff[3]=1;                           // len
	buff[4]=(ecm->caId >> 8);
	buff[5]=6;                           // ECM_PROVIDER_ID
	buff[6]=8;                           // len
	sprintf((char *)&buff[7],"%08X",ecm->provId);
	buff[15]=7;                          // ECM_KEY_NUMBER
	buff[16]=4;                          // len
	sprintf((char *)&buff[17],"%04X",keynr);
	buff[21]=8;                          // ECM_PROCESS_ID ?
	buff[22]=1;                          // len
	buff[23]=1;                          // don't know, inc. with every "no access"
	buff[24]=10;                         // ECM_SYSTEM_ID
	buff[25]=2;                          // len
	buff[26]=(ecm->caId >> 8);
	buff[27]=(ecm->caId & 0xff);
	buff[28]=3;                          // ECM_CA_DATA
	buff[29]=len;                        // len
	memcpy(&buff[30],source,len);

	if(!SendMsg(&so,buff,len+30)) return false;
	int n=RecvMsg(&so,buff,sizeof(buff));
	if(n>0) {
		if(buff[0]==2 && buff[1]==(n-2)) {
			if(buff[2]==5) {
				if(buff[3]==16) {
					// check for zero cw, as someone may not send both cw's every time
					if(!CheckNull(buff+4+0,8)) memcpy(cw+0,buff+4+0,8);
					if(!CheckNull(buff+4+8,8)) memcpy(cw+8,buff+4+8,8);
					return true;
				}
				else dc(printf("cc-radegast: wrong cw length from server %02x\n",buff[3]))
			}
			else if(buff[2]==4) dc(printf("cc-radegast: key not available from server %02x\n",buff[2]))
			else dc(printf("cc-radegast: bad nano from server %02x\n",buff[2]))
		}
		else dc(printf("cc-radegast: bad ECM response from server %02x %02x (n=%x)\n",buff[0],buff[1],n))
    }
	return false;
}
