/*## (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 java.text.*;
import javax.tv.service.*;
import javax.tv.locator.*;
import javax.tv.service.guide.*;
import javax.tv.service.selection.*;
import javax.tv.service.navigation.*;
import tv.osmosys.tv.service.selection.*;
import tv.osmosys.tci.service.db.*;
import tv.osmosys.tci.service.util.*;
import com.adb.tv.service.selection.*;
import org.davic.net.dvb.*;
import org.dvb.si.*;

public class SrvUtils {
 public static class ServiceDetailsRequestor implements SIRequestor {
  private ServiceDetails sdetails=null;
  private Object syncobj=new Object();
  
  public void has_result() {
   synchronized(syncobj) {
    syncobj.notify();
   }
  }

  public ServiceDetails getServiceDetails() throws Throwable {
   synchronized(syncobj) {
    syncobj.wait();
   }

   return sdetails;
  }
  
  public void notifyFailure(SIRequestFailureType reason) {
   has_result();
  }

  public void notifySuccess(SIRetrievable[] result) {
   try {
    sdetails=(ServiceDetails)result[0];
    has_result();
   } catch(Throwable t) {}
  }
 }

 public static class SIRetriever implements SIRetrievalListener {
  int reqtype;
  private org.dvb.si.PMTService pmtsi=null;
  private int ca_pid=0;
  private Object syncobj=new Object();

  private static Object si_syncobj=new Object();

  public SIRetriever(int type) {
   reqtype=type;
  }

  public void has_result() {
   synchronized(syncobj) {
    syncobj.notify();
   }
  }

  public org.dvb.si.PMTService getPMTService() throws Throwable {
   synchronized(syncobj) {
    syncobj.wait();
   }

   return pmtsi;
  }

  public int getCApid() throws Throwable {
   synchronized(syncobj) {
    syncobj.wait();
   }

   return ca_pid;
  }

  public void postRetrievalEvent(SIRetrievalEvent event) {
   try {
    if (event instanceof SISuccessfulRetrieveEvent) {
     SISuccessfulRetrieveEvent ev=(SISuccessfulRetrieveEvent)event;
      
     SIIterator it=ev.getResult();

     switch(reqtype) {
      case SrvUtilsDefs.PMTSI_INFO:
       pmtsi=(org.dvb.si.PMTService)it.nextElement();
       break;

      case SrvUtilsDefs.CAPID_INFO:
       while(it.hasMoreElements()) {
        Descriptor desc=(Descriptor)it.nextElement();

        if (desc.getTag()==CA_DESCRIPTOR) {
         byte cadata[]=desc.getContent();

         if (desc.getContentLength()>=4) {
          int id=((((int)cadata[0])&0xff)<<8)|(((int)cadata[1])&0xff);
          if (id==Conax.CA_ID) {
           ca_pid=(((((int)cadata[2])&0xff)<<8)|(((int)cadata[3])&0xff))&0x1fff;
           break;
          }
         }
        }
       }
       break;
     }
    }

    has_result();
   } catch(Throwable t) {}
  }
 }

 public static final int PAD  = 35;
 public static final int PAD2 = 25;
 public static final int PAD3 = 20;

 public static ServiceType srv_types[]={
  ServiceType.DIGITAL_TV,
  ServiceType.DIGITAL_RADIO,
  ServiceType.NVOD_REFERENCE,
  ServiceType.NVOD_TIME_SHIFTED,
  ServiceType.ANALOG_TV,
  ServiceType.ANALOG_RADIO,
  ServiceType.DATA_BROADCAST,
  ServiceType.DATA_APPLICATION
 };

 public static String srv_props[]={
  "p.id",
  "p.svid",
  "p.bq",
  "p.dnl",
  "p.vod",
  "p.nvod",
  "p.stamp",
  "p.custid",
  "p.hwver",
  "p.hwsubver",
  "p.swver",
  "p.avl",
  "p.bqid",
  "p.s",
  "p.pktbits",
  "p.flags",
  "p.cbkey",
  "p.catbqid",
  "p.maxbrate",
  "p.tblock",
  "p.emmcarousel",
  "p.weburl",
  "p.alt",
  "p.main",
  "p.altidx",
  "p.pst",
  "p.tag",
  "p.n",
  "p.par",
  "p.ch",
  "p.name",
  "p.key",
  "p.eitpid",
  "p.test",
  "p.vodver",
  "p.dnlid",
  "p.tid",
  "p.tmout",
  "svc.custom.packetlock",
  "svc.custom.userlock"
 };

 public static int CA_DESCRIPTOR = 0x09;

 private static Hashtable ca_pid_cache;

 static {
  ca_pid_cache=new Hashtable();  
 }

 public static ServiceContext current_service_context() {
  ServiceContext ctx=null;

  ServiceContextFactoryImpl scfi=(ServiceContextFactoryImpl)ServiceContextFactory.getInstance();
  MPEGServiceContext mpegsrvctx=scfi.getMainServiceContext();

  if (mpegsrvctx!=null) {
   ctx=(ServiceContext)mpegsrvctx;
  }

  return ctx;
 }

 public static Service current_service() {
  Service srv=null;

  ServiceContextFactoryImpl scfi=(ServiceContextFactoryImpl)ServiceContextFactory.getInstance();
  MPEGServiceContext mpegsrvctx=scfi.getMainServiceContext();

  if (mpegsrvctx!=null) {
   srv=mpegsrvctx.getService();
  }

  if (srv==null) {
   ServiceContext ctxtab[]=scfi.getServiceContexts();

   if ((ctxtab!=null)&&(ctxtab.length>0)) {
    srv=ctxtab[0].getService();
   }
  }

  return srv;
 }

 public static Service srv_by_channel(int channel) {
  Service res=null;

  ServiceView srvview=ServiceViewManager.createServiceView();
  
  try {
   srvview.setFilteringExpression("equ(sv:p.cbkey,"+channel+")");

   for(ServiceViewIterator svi=srvview.getServiceViewIterator();svi.getIndex()!=-1;svi.moveBy(1)) {
    Service srv=svi.nextServiceDoNotMove();

    if (srv.getServiceType()==ServiceType.DIGITAL_TV) {
     res=srv;
     break;
    }     
   }
  } catch(Throwable t) {}

  return res;
 }


 public static Service srv_by_locator(String locator) {
  Service srv=null;
 
  try {
   Locator loc=LocatorFactory.getInstance().createLocator(locator);
   srv=SIManager.createInstance().getService(loc);
  } catch(Throwable t) {}

  return srv;
 }

 public static int channel_by_srv(Service srv) {
  Session session=Database.getGlobalSession();
  Integer ival=(Integer)session.getServiceProperty(srv,"p.cbkey");

  int res=-1;
        
  if (ival==null) ival=(Integer)session.getServiceProperty(srv,"svc.cbkey");

  if (ival!=null) res=ival.intValue();
  
  return res;
 }

 public static Object get_service_property(Service srv,String prop) {
  Session session=Database.getGlobalSession();
  return session.getServiceProperty(srv,prop);
 }


 public static int get_int_service_property(Service srv,String prop) {
  int res=-1;

  Session session=Database.getGlobalSession();
  Integer ival=(Integer)session.getServiceProperty(srv,prop);

  if (ival!=null) {
   res=ival.intValue();
  }

  return res;
 }

 public static String get_str_service_property(Service srv,String prop) {
  Session session=Database.getGlobalSession();
  String res=(String)session.getServiceProperty(srv,prop);

  return res;
 }

 public static int get_transport_property(Service srv,String prop) {
  int res=-1;

  Session session=Database.getGlobalSession();
  Integer ival=(Integer)session.getServiceTransportProperty(srv,prop);

  if (ival!=null) {
   res=ival.intValue();
  }

  return res;
 }

 public static int get_svid(Service srv) {
  DvbLocator dvbloc=(DvbLocator)srv.getLocator();

  return dvbloc.getServiceId();
 }

 public static boolean srv_scrambled(Service srv) {
  boolean res=false;

  int prop=get_int_service_property(srv,"p.flags");

  if ((prop&SrvUtilsDefs.SCRAMBLED_BIT)!=0) res=true;

  return res;
 }

 public static void print_filtered(int subscription_bit) {
  ServiceView srvview=ServiceViewManager.createServiceView();
  
  try {
   int SCRAMBLED_BIT=1;

   srvview.setFilteringExpression("mask(sv:p.pktbits,"+subscription_bit+")");
   srvview.setSortingExpression("sv:p.cbkey");

   boolean first=true;
        
   for(ServiceViewIterator svi=srvview.getServiceViewIterator();svi.getIndex()!=-1;svi.moveBy(1)) {
    Service srv=svi.nextServiceDoNotMove();
 
    if (!srv_scrambled(srv)) continue;

    if (first) {
     Output.text("[services for bit 0x"+Utils.hex_value(subscription_bit,8)+" PKG \""+Subscription.pkgname(subscription_bit)+"\"]");
     first=false;
    }

    int num=channel_by_srv(srv);

    String s="- ["+Utils.padded_int(num,8)+"] "+Utils.padded_string(srv.getName(),PAD)+srv.getLocator();
    Output.text(s);
   }

  } catch(Throwable t) {}
 }

 public static void print_services(ServiceView srvview) throws Throwable {
  for(int i=0;i<srv_types.length;i++) {
   ServiceType srvtype=srv_types[i];
   boolean header=false;

   for(ServiceViewIterator svi=srvview.getServiceViewIterator();svi.getIndex()!=-1;svi.moveBy(1)) {
    Service srv=svi.nextServiceDoNotMove();

    if (srv.getServiceType().equals(srvtype)) {
     int num=channel_by_srv(srv);

     if (!header) {
      Output.text(""+srvtype);
      header=true;      
     }

     String s="- ["+Utils.padded_int(num,8)+"] "+Utils.padded_string(srv.getName(),PAD)+srv.getLocator();
     Output.text(s);
    }
   }
  }
 }

 public static void print_scrambled() {
  ServiceView srvview=ServiceViewManager.createServiceView();
  
  try {
   int SCRAMBLED_BIT=1;
   srvview.setFilteringExpression("mask(sv:p.flags,"+SCRAMBLED_BIT+")");
   srvview.setSortingExpression("sv:p.cbkey");

   Output.text("[scrambled services]");

   print_services(srvview);
  } catch(Throwable t) {}
 }

 public static void print_fta() {
  ServiceView srvview=ServiceViewManager.createServiceView();
  
  try {
   int SCRAMBLED_BIT=1;
   srvview.setFilteringExpression("not(mask(sv:p.flags,"+SCRAMBLED_BIT+"))");
   srvview.setSortingExpression("sv:p.cbkey");

   Output.text("[FTA services]");
        
   print_services(srvview);
  } catch(Throwable t) {}
 }

 public static String type(Service srv) {
  return srv.getServiceType().toString();
 }

 public static void print(Service srv) throws Throwable {
  Output.text("[service info]");

  String s="- "+Utils.padded_string("name",PAD2)+"\""+srv.getName()+"\"";
  Output.text(s);

  int num=channel_by_srv(srv);

  s="- "+Utils.padded_string("channel #",PAD2)+Utils.padded_int(num,4);
  Output.text(s);

  s="- "+Utils.padded_string("locator",PAD2)+srv.getLocator();
  Output.text(s);  

  s="- "+Utils.padded_string("type",PAD2)+type(srv);
  Output.text(s);  

  if (srv.getServiceType().equals(ServiceType.DIGITAL_TV)) {
   s="- "+Utils.padded_string("security",PAD2);
   if (srv_scrambled(srv)) {
    s+="SCRAMBLED, CA_PID=0x"+Utils.hex_value(get_ca_pid(srv),4);
   } else s+="FTA";

   if (srv_locked(srv)) s+=", LOCKED";
   Output.text(s);  

   int prop=get_int_service_property(srv,"p.pktbits");
   if (prop!=-1) {
    s="- "+Utils.padded_string("p.pktbits",PAD2)+"0x"+Utils.hex_value(prop,8);
    Output.text(s);  
   }
  }
 }

 public static void print_same_ts(Service current_srv) {
  ServiceView srvview=ServiceViewManager.createServiceView();
  
  try {
   int onid=get_transport_property(current_srv,"dvb.onid");
   int tsid=get_transport_property(current_srv,"dvb.tsid");

   int SCRAMBLED_BIT=1;
   srvview.setFilteringExpression("and(equ(tr:dvb.onid,"+onid+"),equ(tr:dvb.tsid,"+tsid+"),mask(sv:p.flags,"+SCRAMBLED_BIT+"))");
   srvview.setSortingExpression("sv:p.cbkey");

   Output.text("[same TS services]");
        
   print_services(srvview);
  } catch(Throwable t) {}
 }

 public static void print_props(Service srv) {
  try {
   Output.text("[service properties]");

   for(int i=0;i<srv_props.length;i++) {
    Object prop=get_service_property(srv,srv_props[i]);
    if (prop!=null) {
     String s=Utils.padded_string(srv_props[i],PAD3)+prop;
     Output.text(s);
    }
   }
  } catch(Throwable t) {}
 }

 public static void print_subscribed() {
  try {

   Entitlement enttab[]=Conax.get_entitlements();

   int subscription=0;

   Date current=Calendar.getInstance().getTime();

   SimpleDateFormat df=new SimpleDateFormat("dd/MM/yyyy");

   for(int i=0;i<enttab.length;i++) {
    Entitlement ent=enttab[i];
    if (ent.provider().equals(SrvUtilsDefs.DEFAULT_PROVIDER)) {
     Vector subs=ent.subscriptions();

     for(int j=0;j<subs.size();j++) {
      Subscription sub=(Subscription)subs.elementAt(j);

      String sfrom=Utils.padded_int(sub.subsperiod[Subscription.FROM][Subscription.DAY],2);
      sfrom+="/"+Utils.padded_int(sub.subsperiod[Subscription.FROM][Subscription.MONTH],2);
      sfrom+="/"+Utils.padded_int(sub.subsperiod[Subscription.FROM][Subscription.YEAR],4);

      Date from=df.parse(sfrom);

      String sto=Utils.padded_int(sub.subsperiod[Subscription.TO][Subscription.DAY],2);
      sto+="/"+Utils.padded_int(sub.subsperiod[Subscription.TO][Subscription.MONTH],2);
      sto+="/"+Utils.padded_int(sub.subsperiod[Subscription.TO][Subscription.YEAR],4);

      Date to=df.parse(sto);

      long lcur=current.getTime();
      long lfrom=from.getTime();
      long lto=to.getTime();


      if ((lcur>=lfrom)&&(lcur<=lto)) {
       subscription|=sub.bitmask();
      }
     }
    }
   }

   for(int i=0;i<32;i++) {
    int SUBSCRIPTION_BIT=1<<i;

    if ((subscription&SUBSCRIPTION_BIT)!=0) {
     print_filtered(SUBSCRIPTION_BIT);        
    }
   }
  } catch(Throwable t) {}
 }

 public static boolean srv_locked(Service srv) {
  boolean res=false;

  String locked=get_str_service_property(srv,"svc.block.time");

  if (locked!=null) res=true;

  return res;
 }

 public static boolean set_lock_status(int channel,boolean locked) {
  boolean res=false;

  try {
   Service srv=srv_by_channel(channel);

   if (srv!=null) {
    Session session=Database.beginSession();

    if (Config.isHERMES()) {
     if (locked) {
      session.setServiceProperty(srv,"svc.block.time","from=0,to=1440,system");
     } else {
      session.setServiceProperty(srv,"svc.block.time",null);
     }
    } else

    if (Config.isCARBO()) {
     if (locked) {
      session.setServiceProperty(srv,"svc.custom.packetlock","1");
     } else {
      session.setServiceProperty(srv,"svc.custom.packetlock",null);
     }
    }

    session.commit();
    res=true;
   }
  } catch(Throwable t) {}

  return res;
 }

 public static boolean select(int channel) {
  boolean res=false;

  try {
   Service srv=srv_by_channel(channel);

   if (srv!=null) {
    ServiceContext ctx=current_service_context();
    if (ctx!=null) {
     ctx.select(srv);
     res=true;
    }
   }
  } catch(Throwable t) {}

  return res;
 }

 public static boolean select(Service srv) {
  boolean res=false;

  try {
   if (srv!=null) {
    ServiceContext ctx=current_service_context();
    if (ctx!=null) {
     ctx.select(srv);
     res=true;
    }
   }
  } catch(Throwable t) {}

  return res;
 }

 public static ServiceDetails get_service_details(Service srv) {
  ServiceDetails sdetails=null;

  try {
   ServiceDetailsRequestor sdrequestor=new ServiceDetailsRequestor();
   javax.tv.service.SIRequest req=srv.retrieveDetails((SIRequestor)sdrequestor);

   sdetails=sdrequestor.getServiceDetails();
 
   req.cancel();

  } catch(Throwable t) {
  }

  return sdetails;
 }

 public static org.dvb.si.PMTService get_pmtservice(ServiceDetails sd) {
  org.dvb.si.PMTService pmtsi=null;

  try {
   org.dvb.si.SIService si=(org.dvb.si.SIService)sd;

   short desctags[]=new short[1];
   desctags[0]=-1;

   SIRetriever siretriever=new SIRetriever(SrvUtilsDefs.PMTSI_INFO);

   si.retrievePMTService(SIInformation.FROM_CACHE_OR_STREAM,null,(SIRetrievalListener)siretriever,desctags);

   pmtsi=siretriever.getPMTService();
  } catch(Throwable t) {}
  
  return pmtsi;
 }

 private static int get_cached_ca_pid(Service srv) {
  String locator=srv.getLocator().toString();

  Integer ival=(Integer)ca_pid_cache.get(locator);

  if (ival!=null) {
   return ival.intValue();
  } 

  return 0;  
 }

 private static void cache_ca_pid(Service srv,int pid) {
  String locator=srv.getLocator().toString();

  ca_pid_cache.put(locator,new Integer(pid));
 }

 public static int get_ca_pid(Service srv) {
  int ca_pid=get_cached_ca_pid(srv);

  if (ca_pid!=0) return ca_pid;

  try {
   ServiceDetails sd=get_service_details(srv);

   if (sd!=null) {
    org.dvb.si.PMTService pmtsrv=get_pmtservice(sd);

    if (pmtsrv!=null) {
     short desctags[]=new short[1];
     desctags[0]=-1;

     SIRetriever siretriever=new SIRetriever(SrvUtilsDefs.CAPID_INFO);

     pmtsrv.retrieveDescriptors(SIInformation.FROM_CACHE_OR_STREAM,null,(SIRetrievalListener)siretriever,desctags);

     ca_pid=siretriever.getCApid();
    }
   }
  } catch(Throwable t) {}

  if (ca_pid==0) {
   int svid=get_svid(srv);

   ca_pid=MPEG.get_ecm_pid(svid);
  }

  if (ca_pid!=0) {
   cache_ca_pid(srv,ca_pid);
  }

  return ca_pid;
 }

 public static Service get_invoice_service(int tsid) {
  Service srv=null;

  ServiceView srvview=ServiceViewManager.createServiceView();
  
  try {
   srvview.setFilteringExpression("and(not(isnull(sv:p.emmcarousel)),equ(tr:dvb.tsid,"+tsid+"))");
   ServiceViewIterator svi=srvview.getServiceViewIterator();
    
   srv=svi.nextServiceDoNotMove();
  } catch(Throwable t) {}

  return srv;
 }

 public static int get_invoice_pid(Service srv) {
  int invoice_pid=0;

  try {   
   if (srv!=null) {
    String s=get_service_property(srv,"p.emmcarousel").toString().trim();

    if (s!=null) {
     StringTokenizer tokenizer=new StringTokenizer(s," ");
                
     if (tokenizer.hasMoreTokens()) {
      invoice_pid=Integer.parseInt(tokenizer.nextToken(),16);
     }
    }
   }

  } catch(Throwable t) {}

  return invoice_pid;
 }
}
