/*## (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.io.*;
import java.util.*;
import org.ocap.shared.dvr.*;

public class VOD {
 public static class Property {
  String name;

  private static String bin_props[]={
   "dvrsec.conax.ecm",
   "dvr_rm.pvod.ecm",
   "dvr_rm.clientdata.appdata.VIEWED_BK",
   "dvr_rm.clientdata.appdata.FLAGS"
  };

  public static boolean is_binary(String name) {
   for(int i=0;i<bin_props.length;i++) {
    if (bin_props[i].equals(name)) return true;
   }

   if (name.startsWith("dvr_rm.pvod.completion.map.")) return true;
 
   return false;
  }

  public Property(String name) {
   this.name=name;
  }

  public String name() {
   return name;
  }

  public Object value() {
   return null;
  }

  public static Property create(String key,byte val[]) {
   if (is_binary(key)) {
    return new BinProperty(key,val);
   } else {
    if (val[val.length-1]==0) {
     return new StrProperty(key,new String(val,0,val.length-1));
    } else {
     return new StrProperty(key,new String(val));
    }
   }
  }

  public void print() {
  }
 }

 public static class StrProperty extends Property {
  String value;

  public StrProperty(String name,String value) {
   super(name);
   this.value=value;
  }

  public Object value() {
   return value;
  }

  public void print() {
   try {
    Output.text("- "+Utils.padded_string(name,PAD)+" = "+value);
   } catch(Throwable t) {}
  }
 }

 public static class BinProperty extends Property {
  byte value[];

  public BinProperty(String name,byte value[]) {
   super(name);
   this.value=value;
  }

  public Object value() {
   return value;
  }

  public void print() {
   try {
    Output.text("- "+Utils.padded_string(name,PAD));

    MPEG.print_buf(3,value);
   } catch(Throwable t) {}
  }
 }

 public static class PDFile {
  public static final int CHUNK_HDR_SIZE       = 0x0200;

  public static final int CHUNK_TYPE_OFFSET    = 0x0000;
  public static final int CHUNK_TAG_OFFSET     = 0x0080;
  public static final int CHUNK_NEXTPOS_OFFSET = 0x0180;
  public static final int CHUNK_DICT_OFFSET    = 0x0200;

  public static final int CHUNK_DATA_SIZE      = 0x1c;
  public static final int TOTAL_CHUNK_SIZE     = 0x28;

  public static final int MAX_CHUNK_COUNT      = 0x80;

  public static final int SECTION_SIZE         = CHUNK_DICT_OFFSET+MAX_CHUNK_COUNT*TOTAL_CHUNK_SIZE;
  
  public static final int DICT_MAGIC           = 0xcdefba13;

  String filename;
  byte data[];
  Object secdata[][];

  private byte[] read_types(int sec_idx,int cnt) {
   byte types[]=new byte[cnt];

   int off=CHUNK_HDR_SIZE+sec_idx*SECTION_SIZE+CHUNK_TYPE_OFFSET;

   System.arraycopy(data,off,types,0,cnt);

   return types;
  }

  private byte[] read_nextpos(int sec_idx,int cnt) {
   byte nextpos[]=new byte[cnt];

   int off=CHUNK_HDR_SIZE+sec_idx*SECTION_SIZE+CHUNK_NEXTPOS_OFFSET;
   System.arraycopy(data,off,nextpos,0,cnt);

   return nextpos;
  }

  private short[] read_tags(int sec_idx,int cnt) {
   short tags[]=new short[cnt];

   for(int i=0;i<cnt;i++) {
    int off=CHUNK_HDR_SIZE+sec_idx*SECTION_SIZE+CHUNK_TAG_OFFSET+2*i;

    int val=((int)(data[off])&0xff)|(((int)(data[off+1])&0xff)<<8);
    tags[i]=(short)val;
   }

   return tags;
  }

  private int get_items_cnt(int sec_idx) {
   int cnt=0;

   int pos=CHUNK_HDR_SIZE+sec_idx*SECTION_SIZE+CHUNK_DICT_OFFSET;
   int endpos=pos+MAX_CHUNK_COUNT*TOTAL_CHUNK_SIZE;

   while(pos<endpos) {
    int tag=read_word(data,pos);

    if (tag!=DICT_MAGIC) break;

    pos+=TOTAL_CHUNK_SIZE;
    cnt++;
   }

   return cnt;
  }

  private byte[] read_data(int sec_idx,int idx,byte nextpos[]) {
   int pos=CHUNK_HDR_SIZE+sec_idx*SECTION_SIZE+CHUNK_DICT_OFFSET;
   int off=pos+idx*TOTAL_CHUNK_SIZE+4;

   int size=read_word(data,off);

   byte item[]=new byte[size];

   if (size<CHUNK_DATA_SIZE) {
    System.arraycopy(data,off+8,item,0,size);
   } else {
    int cnt=0;

    while(cnt<size) {     
     int len=size-cnt;
     if (len>CHUNK_DATA_SIZE) len=CHUNK_DATA_SIZE;

     off=pos+idx*TOTAL_CHUNK_SIZE+0x0c;

     System.arraycopy(data,off,item,cnt,len);

     cnt+=len;

     int next_idx=nextpos[idx];

     if (idx==next_idx) break;
      else idx=next_idx;
    }
   }

   return item;
  }

  public Object[] load_section(int idx) {
   Object sobj[]=null;

   try {
    int cnt=get_items_cnt(idx);

    byte types[]=read_types(idx,cnt);
    byte nextpos[]=read_nextpos(idx,cnt);
    short tags[]=read_tags(idx,cnt);

    sobj=new Object[cnt];

    for(int i=0;i<cnt;i++) {
     if (tags[i]==0) continue;

     byte item[]=read_data(idx,i,nextpos);

     switch(types[i]) {
      case 0x03:
       String key=(new String(item)).trim();
       sobj[i]=key;
       break;
      case 0x01:
       sobj[i]=item;
       break;
     }
    }
   } catch(Throwable t) {
ApiMonitor.log_stacktrace(t);
}

   return sobj;
  }

  public void parse_data() {
   if (data!=null) {
    int size=data.length;

    int sec_cnt=(size-CHUNK_HDR_SIZE)/SECTION_SIZE;

    secdata=new Object[sec_cnt][];

    for(int i=0;i<sec_cnt;i++) {    
     secdata[i]=load_section(i);
    }
   }
  }

  public int section_cnt() {
   return secdata.length;
  }

  public int section_size(int idx) {
   return secdata[idx].length;
  }

  public Object get_item(int sec_idx,int idx) {
   return secdata[sec_idx][idx];
  }

  public PDFile(String filename) {
   this.filename=filename;
   data=load_file(filename);
   parse_data();
  }
 }

 public static class PBFile {
  public static final int DATA_OFFSET          = 0x0100;
  public static final int DEFAULT_CHAIN_OFFSET = 0x0184;

  public static final int RECORD_SIZE = 0x21;
  public static final int NULL_PTR    = 0x80000000;

  String filename;
  byte data[];
  int offsets[];

  private int count_chain(int off) {
   int cnt=0;

   if (off==NULL_PTR) return 0;

   while(true) {
    byte tag=data[off];

    int key_idx=read_word(data,off+1+0x08);
    int val_idx=read_word(data,off+1+0x0c);

    cnt++;

    int next_off=read_word(data,off+1+0x1c);
    if (next_off==NULL_PTR) break;

    off=next_off;
   }

   return cnt;
  }

  private int get_items_cnt() {
   int cnt=count_chain(DEFAULT_CHAIN_OFFSET);

   int off=DATA_OFFSET;

   while(true) {
    byte tag=data[off];

    int chain_off=0;

    if (tag!=7) {
     if (tag==0x10) chain_off=read_word(data,off+1+0x18);
      else
     if (tag==0x11) chain_off=read_word(data,off+1+0x1c);
      else
     if (tag==0x12) chain_off=read_word(data,off+1+0x1c);

     if ((chain_off==0)||(chain_off==NULL_PTR)) break;

     cnt+=count_chain(chain_off);

     off+=RECORD_SIZE;
    } else break;
   }

   return cnt;
  }

  public int add_offsets(int off,int idx) {
   while(true) {
    byte tag=data[off];

    int key_idx=read_word(data,off+1+0x08);
    int val_idx=read_word(data,off+1+0x0c);

    offsets[(idx<<1)]=key_idx;
    offsets[(idx<<1)+1]=val_idx;

    idx++;

    int next_off=read_word(data,off+1+0x1c);
    if (next_off==NULL_PTR) break;

    off=next_off;
   }

   return idx;
  }

  public void parse_data() {
   int cnt=get_items_cnt();

   offsets=new int[2*cnt];

   int off=DATA_OFFSET;

   int idx=0;

   idx=add_offsets(DEFAULT_CHAIN_OFFSET,idx);

   while(true) {
    byte tag=data[off];

    int chain_off=0;

    if (tag!=7) {
     if (tag==0x10) chain_off=read_word(data,off+1+0x18);
      else
     if (tag==0x11) chain_off=read_word(data,off+1+0x1c);
      else
     if (tag==0x12) chain_off=read_word(data,off+1+0x1c);

     if (chain_off==0) break;

     idx=add_offsets(chain_off,idx);

     off+=RECORD_SIZE;
    } else break;
   }
  }

  public int item_cnt() {
   return offsets.length>>1;
  }

  public int key_idx(int idx) {
   return offsets[(idx<<1)];
  }

  public int val_idx(int idx) {
   return offsets[(idx<<1)+1];
  }

  public PBFile(String filename) {
   this.filename=filename;
   data=load_file(filename);
   parse_data();
  }
 }

 public static class SFile {
  public static final int DATA_START_OFFSET    = 0x0400;
  public static final int DATA_SECTION_SIZE    = 0x0200;

  public static final int DATA_START_MAGIC     = 0x7eeae20b;
  public static final int DATA_END_MAGIC       = 0xffffffff;

  public static final String PDPB_NAME         = "pdpb_name";

  String filename;
  byte data[];

  Vector records;

  boolean is_pvod;

  private static Hashtable cache;

  static {
   cache=new Hashtable();
  }

  public void load_props(int off) {
   Vector rec=null;

   while(off<data.length) {
    int tag=read_word_be(data,off);

    if (tag==DATA_START_MAGIC) {
     int cnt1=read_word_be(data,off+0x04);

     if (cnt1==DATA_END_MAGIC) {
      off+=0x18;

      off=(off+(DATA_SECTION_SIZE-1))&(~(DATA_SECTION_SIZE-1));
      continue;
     }

     int cnt2=read_word_be(data,off+0x0c);
     int cnt3=read_word_be(data,off+0x10);
     int pdpb_idx=read_word_be(data,off+0x14);

     off+=0x18;

     if ((rec!=null)&&(rec.size()>0)) {
      records.addElement(rec);
     }

     rec=new Vector();
     rec.addElement(new StrProperty(PDPB_NAME,Utils.hex_value(pdpb_idx,8)));
    }

    int klen=read_word_be(data,off);

    if (klen==DATA_END_MAGIC) {
     off=(off+(DATA_SECTION_SIZE-1))&(~(DATA_SECTION_SIZE-1));
     continue;
    }

    int vlen=read_word_be(data,off+0x04);

    byte kdata[]=new byte[klen];
    byte val[]=new byte[vlen];

    System.arraycopy(data,off+8,kdata,0,klen);
    System.arraycopy(data,off+8+klen,val,0,vlen);

    String key=(new String(kdata)).trim();

    if (!is_pvod) {
     if (key.startsWith("dvr_rm.pvod")) {
      is_pvod=true;
      pvod_dir=filename.substring(0,filename.length()-8);
     }
    }

    Property p=Property.create(key,val);

    rec.addElement(p);
    
    off=(off+8+klen+vlen+0x03)&0xfffffffc;
   }

   if ((rec!=null)&&(rec.size()>0)) {
    records.addElement(rec);
   }
  }

  public void parse_data() {
   if (data!=null) {
    records=new Vector();
    load_props(DATA_START_OFFSET);
   }
  }

  public Vector props_by_asset_id(int id) {
   if (records==null) return null;

   for(int i=0;i<records.size();i++) {
    Vector rec=(Vector)records.elementAt(i);

    for(int j=0;j<rec.size();j++) {
     Property p=(Property)rec.elementAt(j);
     String name=p.name();

     if (name.equals("dvr_rm.pvod.asset.id")) {
      Object val=p.value();
      int assetid=Integer.parseInt(val.toString());

      if (assetid==id) return rec;
     }
    }
   }

   return null;  
  }

  public static Object lookup_prop(Vector props,String pname) {
   if (props==null) return null;

   for(int i=0;i<props.size();i++) {
    Property p=(Property)props.elementAt(i);
    String name=p.name();

    if (name.equals(pname)) {
     return p.value();
    }
   }

   return null;
  }

  public Vector all_props() {
   return records;
  }

  public boolean is_pvod() {
   return is_pvod;
  }

  public SFile(String filename) {
   this.filename=filename;
   is_pvod=false;
   data=load_file(filename);
   parse_data();
   data=null;
  }

  public static SFile get(String name) {
   SFile sf=(SFile)cache.get(name);

   if (sf==null) {
    sf=new SFile(name);
    cache.put(name,sf);
   }

   return sf;
  }
 }

 public static class ECMData {
  byte ecm[];
  int cw[];
  int handler_cw;
  int stamp;

  public ECMData(byte ecm[],int cw[]) {
   this.ecm=ecm;
   this.cw=cw;
  }

  public byte[] ecm() {
   return ecm;
  }

  public int[] cw() {
   return cw;
  }

  public int handler_cw() {
   return handler_cw;
  }

  public void handler_cw(int val) {
   handler_cw=val;
  }  

  public int stamp() {
   return stamp;
  }

  public void stamp(int val) {
   stamp=val;
  }
 }

 public static final String HOTPLUG_DIR  = "/hotplug";
 public static final String STATICS_FILE = "statics";

 public static final int PAD = 0x20;

 private static String pvod_dir;

 static {
  try {
   for(int i=0;i<10;i++) {
    SFile sf=SFile.get(HOTPLUG_DIR+"/"+i+".1/"+STATICS_FILE);
   }
  } catch(Throwable t) {}

  try {
   new File(VODDefs.ECM_DIR).mkdirs();
  } catch(Throwable t) {}
 }

 public static int read_short(byte data[],int pos) {
  return (((int)data[pos])&0xff)|((((int)data[pos+1])&0xff)<<8);
 }

 public static int read_word(byte data[],int pos) {
  return (((int)data[pos])&0xff)|((((int)data[pos+1])&0xff)<<8)|((((int)data[pos+2])&0xff)<<16)|((((int)data[pos+3])&0xff)<<24);
 }

 public static int read_word_be(byte data[],int pos) {
  return (((int)data[pos+3])&0xff)|((((int)data[pos+2])&0xff)<<8)|((((int)data[pos+1])&0xff)<<16)|((((int)data[pos])&0xff)<<24);
 }

 public static byte[] load_file(String filename) {
  byte data[]=null;

  try {
   File f=new File(filename);

   int size=(int)f.length();

   data=new byte[size];

   FileInputStream fis=new FileInputStream(filename);

   fis.read(data);

   fis.close();
  } catch(Throwable t) {
   data=null;
  }

  return data;
 }

 public static void print_properties(String pdfile,String pbfile) throws Throwable {
  PDFile pdf=new PDFile(pdfile);
  PBFile pbf=new PBFile(pbfile);

  int cnt=pbf.item_cnt();

  for(int i=0;i<cnt;i++) {
   int key_idx=pbf.key_idx(i);
   int val_idx=pbf.val_idx(i);

   int ksec_idx=(key_idx>>8)&0xff;
   int vsec_idx=(val_idx>>8)&0xff;

   String key=(String)pdf.get_item(ksec_idx,key_idx&0xff);
   byte val[]=(byte[])pdf.get_item(vsec_idx,val_idx&0xff);

   Property p=Property.create(key,val);
   p.print();
  }
 }

 public static Vector get_pdpb_properties(String pdfile,String pbfile,String pname) {
  Vector res=null;

  ApiMonitor.log("get_pdpb_properties: "+pdfile+" "+pbfile);

  try {
   PDFile pdf=new PDFile(pdfile);
   PBFile pbf=new PBFile(pbfile);

   int cnt=pbf.item_cnt();

   Vector v=new Vector();

   for(int i=0;i<cnt;i++) {
    int key_idx=pbf.key_idx(i);
    int val_idx=pbf.val_idx(i);

    int ksec_idx=(key_idx>>8)&0xff;
    int vsec_idx=(val_idx>>8)&0xff;

    String key=(String)pdf.get_item(ksec_idx,key_idx&0xff);

    if (key.equals(pname)) {
     byte val[]=(byte[])pdf.get_item(vsec_idx,val_idx&0xff);
     v.addElement(val);
    }
   }

   res=v;
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return res;
 }

 public static Vector get_props(int recid) {
  Vector props=null;

  try {
   int asset_id=RecMan.recording_id2asset_id(recid);

   if (asset_id!=-1) {
    SFile sf=SFile.get(pvod_dir+"/"+STATICS_FILE);

    props=sf.props_by_asset_id(asset_id);    
   }
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);   
  }

  return props;
 }

 public static String get_pdpb_name(int recid) {
  String name=null;

  try {
   int asset_id=RecMan.recording_id2asset_id(recid);

   if (asset_id!=-1) {
    SFile sf=SFile.get(pvod_dir+"/"+STATICS_FILE);
    Vector props=sf.props_by_asset_id(asset_id);

    name=(String)SFile.lookup_prop(props,SFile.PDPB_NAME);
   }
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);   
  }

  return name;
 }

 public static void print_props(int recid) {
  try {
   RecordingRequest rr=RecMan.find_recording(recid);

   if (rr!=null) {
    RecMan.print_recinfo(rr);
   }
 
   Vector props=get_props(recid);

   if (props!=null) {
    for(int i=0;i<props.size();i++) {
     Property p=(Property)props.elementAt(i);
     p.print();
    }
   }
  } catch(Throwable t) {}
 }

 public static void print_all_props() {
  try {
   SFile sf=SFile.get(pvod_dir+"/"+STATICS_FILE);

   Vector records=sf.all_props();    
 
   if (records!=null) {
    int idx=0;

    for(int j=0;j<records.size();j++) {
     Output.text("PVOD "+Utils.padded_int(idx,3));

     Vector props=(Vector)records.elementAt(j);

     for(int k=0;k<props.size();k++) {
      Property p=(Property)props.elementAt(k);
      p.print();
     }

     idx++;
    }
   }
  } catch(Throwable t) {}
 }

 public static Vector get_pdpb_property(int recid,String pname) {
  Vector res=null;

  try {
   ApiMonitor.log("get_pdpb_property: "+pname);

   String name=get_pdpb_name(recid);
   ApiMonitor.log("get_pdpb_name: "+name);

   if (name!=null) {
    String pdfile=name+".pd";
    String pbfile=name+".pb";

    Vector v=get_pdpb_properties(pvod_dir+"/"+pdfile,pvod_dir+"/"+pbfile,pname);

    ApiMonitor.log("get_pdpb_properties: "+v);

    if (v!=null) {
     ApiMonitor.log("get_pdpb_properties: "+v.size());
    }

    if (v!=null) {
     res=v;
    }
   }
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return res;
 }

 public static Vector get_ecms(int recid) {
  Vector ecms=null;

  try {
   Vector v=get_pdpb_property(recid,"dvrsec.conax.ecm");

   if (v!=null) {
    ecms=new Vector();

    for(int i=v.size()-1;i>=0;i--) {
     byte mpeg_ecm[]=(byte[])v.elementAt(i);

     byte ecm[]=new byte[mpeg_ecm.length-5];

     System.arraycopy(mpeg_ecm,5,ecm,0,mpeg_ecm.length-5);

     ecms.addElement(ecm);
    }
   }
  } catch(Throwable t) {}

  return ecms;
 }

 public static byte[] get_pmt(int recid) {
  byte pmt[]=null;

  try {
   Vector v=get_pdpb_property(recid,"cmps");

   if (v!=null) {
    byte val[]=(byte[])v.elementAt(0);    

    String cmps=new String(val);

    int idx=cmps.indexOf("pmt.base64=");

    if (idx>=0) {
     int idx2=cmps.indexOf(',');

     String pmt_base64=cmps.substring(idx+11,idx2);

     ApiMonitor.log("pmt_base64 "+pmt_base64);
     pmt=Utils.base64_decode(pmt_base64); 
     ApiMonitor.log_buf(pmt);
    }
    
   }
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return pmt;
 }

 public static int ecm_store(int recid,String name) {
  try {
   ApiMonitor.log("ECM STORE: "+name);

   Vector ecms=get_ecms(recid);

   if (ecms!=null) {
    FileOutputStream fos=new FileOutputStream(VODDefs.ECM_DIR+"/"+name,false);
    DataOutputStream dos=new DataOutputStream(fos);

    for(int i=0;i<ecms.size();i++) {
     byte ecm[]=(byte[])ecms.elementAt(i);

     try {
      int cw[]=Conax.decrypt_cw(ecm);

      dos.writeInt(ecm.length);
      dos.write(ecm);

      for(int j=0;j<4;j++) {
       dos.writeInt(cw[j]);
      }
     } catch(Throwable t) {}
    }

    dos.close();
    fos.close();
   }

  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return VODDefs.RES_OK;
 }

 private static int get_size(String name) {
  int size=-1;

  try {
   File f=new File(VODDefs.ECM_DIR+"/"+name);
   if (f.exists()) {
    size=(int)f.length();
   }
  } catch(Throwable t) {}

  return size;
 }

 private static void add_ecmdata(Hashtable ecms,ECMData ecmdata) {
  int stamp=ECMServer.calc_stamp(ecmdata.ecm());

  ecms.put(new Integer(stamp),ecmdata);
 }

 public static int ecm_load(String name) {
  try {
   int size=get_size(name);

   ApiMonitor.log("ecm_load "+size);

   if (size<0) return EMMStoreDefs.CANNOT_OPEN;

   byte data[]=new byte[size];

   FileInputStream fis=new FileInputStream(VODDefs.ECM_DIR+"/"+name);
   fis.read(data);
   fis.close();

   Hashtable ecms=new Hashtable();

   DataInputStream dis=new DataInputStream(new ByteArrayInputStream(data));

   int pos=0;

   while(pos<size) {
    int ecm_size=dis.readInt();
    ApiMonitor.log("ecm_size "+ecm_size);

    byte ecm[]=new byte[ecm_size];
    dis.read(ecm);

    int cw[]=new int[4];

    for(int j=0;j<4;j++) {
     cw[j]=dis.readInt();
    }

    ECMData ecmdata=new ECMData(ecm,cw);
    add_ecmdata(ecms,ecmdata);
 
    pos+=4+ecm_size+4*4;
   }

   VodECMReader.set_ecms(ecms);

   ECMInjector.start_injecting();
  } catch(Throwable t) {
   ApiMonitor.log_stacktrace(t);
  }

  return VODDefs.RES_OK;
 }

 public static int ecm_unload() {
  VodECMReader.set_ecms(new Hashtable());
  ECMInjector.stop_injecting();

  return VODDefs.RES_OK;
 }

 public static void ecm_view(int recid) {
  Vector ecms=get_ecms(recid);

  if (ecms!=null) {
   for(int i=0;i<ecms.size();i++) {
    byte ecm[]=(byte[])ecms.elementAt(i);

    try {
     Output.text("- ECM "+Utils.hex_value(i,4));

     MPEG.print_buf(3,ecm);

     int cw[]=Conax.decrypt_cw(ecm);

     Output.text("- CUR CW plaintext: "+Utils.hex_value(cw[0],8)+" "+Utils.hex_value(cw[1],8));
     Output.text("- NXT CW plaintext: "+Utils.hex_value(cw[2],8)+" "+Utils.hex_value(cw[3],8));

    } catch(Throwable t) {}
   }
  }
 }
}
