/*## (c) SECURITY EXPLORATIONS    2018 poland                                #*/
/*##     http://www.security-explorations.com                                #*/

/* THIS SOFTWARE IS PROTECTED BY DOMESTIC AND INTERNATIONAL COPYRIGHT LAWS    */
/* UNAUTHORISED COPYING OF THIS SOFTWARE IN EITHER SOURCE OR BINARY FORM IS   */
/* EXPRESSLY FORBIDDEN. ANY USE, INCLUDING THE REPRODUCTION, MODIFICATION,    */
/* DISTRIBUTION, TRANSMISSION, RE-PUBLICATION, STORAGE OR DISPLAY OF ANY      */
/* PART OF THE SOFTWARE, FOR COMMERCIAL OR ANY OTHER PURPOSES REQUIRES A      */
/* VALID LICENSE FROM THE COPYRIGHT HOLDER.                                   */

/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS    */
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,*/
/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
/* SECURITY EXPLORATIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
/* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF  */
/* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     */
/* SOFTWARE.                                                                  */

import java.lang.*;
import java.util.*;
import javax.tv.service.*;

public class ECMServer {
 public static class Monitor extends ECMProvider {
  ECMFilter filter;

  public Monitor(int channel) {
   super(channel);

   filter=ECMFilter.create(channel);
  }

  public void run(Object args[]) {
   try {
    filter.start();
    running=true;

    while(running) {
     byte data[]=filter.get_data();
     ecm_stamp=calc_stamp(data);

     ApiMonitor.log("ECM channel "+channel+" stamp "+Utils.hex_value(ecm_stamp,8));
    }
   } catch(Throwable t) {}
  }

  public void stop() {
   super.stop();
   filter.stop();
  }
 }

 public static final int STAMP_OFFSET = 0x07;

 private static Hashtable providers;
 private static RemoteECMReader remote_provider;
 private static VodECMReader vod_provider;
 private static Monitor ecm_monitor;

 static int ecm_stamp;

 static {
  providers=new Hashtable();
  ecm_stamp=0;
 }

 public static int calc_stamp(byte ecm[]) {
  int stamp=((((int)ecm[STAMP_OFFSET])&0xff)<<24)|((((int)ecm[STAMP_OFFSET+1])&0xff)<<16)|((((int)ecm[STAMP_OFFSET+2])&0xff)<<8)|(((int)ecm[STAMP_OFFSET+3])&0xff);

  return stamp;
 }

 public static int ecm_stamp() {
  return ecm_stamp;
 }

 private static ECMProvider get_provider(int channel) {
  ECMProvider prov=(ECMProvider)providers.get(new Integer(channel));

  return prov;
 }

 private static void add_provider(int channel,ECMProvider provider) {
  providers.put(new Integer(channel),provider);
 }

 private static void del_provider(int channel,ECMProvider provider) {
  providers.remove(new Integer(channel));
 }

 public static Monitor ECMMonitor(int channel) {
  Monitor monitor=null;

  try {
   if (ecm_monitor==null) {
    Monitor tmpprov=new Monitor(channel);
    ecm_monitor=tmpprov;
   }
  } catch(Throwable t) {}

  return ecm_monitor;
 }

 public static ECMProvider localECMProvider(int channel) {
  ECMProvider provider=null;

  try {
   if (get_provider(channel)==null) {
    ECMProvider tmpprov=new LocalECMReader(channel);
    add_provider(channel,tmpprov);
    provider=tmpprov;
   }
  } catch(Throwable t) {}

  return provider;
 }

 public static void stop_provider(int channel) {
  ECMProvider provider=get_provider(channel);

  if (provider!=null) {
   provider.stop();
   del_provider(channel,provider);
  }
 }

 public static RemoteECMReader remoteECMProvider() {
  RemoteECMReader provider=null;

  try {
   if (remote_provider==null) {
    RemoteECMReader tmpprov=new RemoteECMReader();
    provider=tmpprov;
   }
  } catch(Throwable t) {}

  return provider;
 }

 public static VodECMReader vodECMProvider() {
  VodECMReader provider=null;

  try {
   if (vod_provider==null) {
    VodECMReader tmpprov=new VodECMReader();
    provider=tmpprov;
   }
  } catch(Throwable t) {}

  return provider;
 }

 public static int start_receiver(int channel) {
  if (remote_provider==null) {
   remote_provider=remoteECMProvider();

   ECMSetter ecmsetter=new ECMSetter();
   remote_provider.register_handler(ecmsetter);   

   remote_provider.start();
  }

  if (remote_provider.enabled()) return ECMServerDefs.ALREADY_STARTED;

  Service srv=null;

  if (channel!=-1) {
   srv=SrvUtils.srv_by_channel(channel);
  } else {
   srv=SrvUtils.current_service();
   channel=SrvUtils.channel_by_srv(srv);
  }

  if (srv==null) return ECMServerDefs.UNKNOWN_CHANNEL;

  remote_provider.enable(channel);

  ECMServer.start_monitor(channel);

  if (!CWFaker.status()) {
   CWFaker.start(CWFaker.M_FAKE_ALL_ECM);
  }

  CW.start_decrypt_handler();

  return ECMServerDefs.RES_OK;
 }

 public static int stop_receiver() {
  if ((remote_provider==null)||(!remote_provider.enabled())) return ECMServerDefs.NOT_RUNNING;

  if (remote_provider!=null) {
   remote_provider.disable();
  }

  ECMServer.stop_monitor();

  if (CWFaker.status()) {
   CWFaker.stop();
  }

  return ECMServerDefs.RES_OK;
 }

 public static int start_sender(int channel) {
  Service srv=null;

  if (channel!=-1) {
   srv=SrvUtils.srv_by_channel(channel);
  } else {
   srv=SrvUtils.current_service();
   channel=SrvUtils.channel_by_srv(srv);
  }

  if (srv==null) return ECMServerDefs.UNKNOWN_CHANNEL;

  if (get_provider(channel)!=null) return ECMServerDefs.ALREADY_STARTED;

  ECMProvider sender=ECMServer.localECMProvider(channel);

  ECMServer.start_monitor(channel);

  if (sender!=null) {
   ECMForwarder ecmfwd=new ECMForwarder();
   sender.register_handler(ecmfwd);   

   sender.start();
  }

  return ECMServerDefs.RES_OK;
 }

 public static int stop_sender(int channel) {
  Service srv=null;

  if (channel!=-1) {
   srv=SrvUtils.srv_by_channel(channel);
  } else {
   srv=SrvUtils.current_service();
   channel=SrvUtils.channel_by_srv(srv);
  }

  if (srv==null) return ECMServerDefs.UNKNOWN_CHANNEL;

  if (get_provider(channel)==null) return ECMServerDefs.NOT_RUNNING;

  ECMServer.stop_monitor();

  stop_provider(channel);

  return ECMServerDefs.RES_OK;
 }

 public static int start_vodreceiver() {
  if (vod_provider==null) {
   vod_provider=vodECMProvider();

   ECMSetter ecmsetter=new ECMSetter();
   vod_provider.register_handler(ecmsetter);   

   vod_provider.start();
  }

  if (vod_provider.enabled()) return ECMServerDefs.ALREADY_STARTED;

  vod_provider.enable();

  if (!ECMInjector.status()) {
   ECMInjector.start();
  }

  CW.start_decrypt_handler();

  return ECMServerDefs.RES_OK;
 }

 public static int stop_vodreceiver() {
  if ((vod_provider==null)||(!vod_provider.enabled())) return ECMServerDefs.NOT_RUNNING;

  if (vod_provider!=null) {
   vod_provider.disable();
  }

  if (ECMInjector.status()) {
   ECMInjector.stop();
  }

  return ECMServerDefs.RES_OK;
 }

 public static int start_monitor(int channel) {
  Service srv=null;

  if (channel!=-1) {
   srv=SrvUtils.srv_by_channel(channel);
  } else {
   srv=SrvUtils.current_service();
   channel=SrvUtils.channel_by_srv(srv);
  }

  if (srv==null) return ECMServerDefs.UNKNOWN_CHANNEL;

  if (ecm_monitor!=null) return ECMServerDefs.ALREADY_STARTED;

  ecm_monitor=ECMMonitor(channel);
  ecm_monitor.start();

  return ECMServerDefs.RES_OK;
 }

 public static int stop_monitor() {
  if (ecm_monitor==null) return ECMServerDefs.NOT_RUNNING;

  ecm_monitor.stop();
  ecm_monitor=null;

  return ECMServerDefs.RES_OK;
 }

}
