Package com.aelitis.azureus.core.subs.impl

Source Code of com.aelitis.azureus.core.subs.impl.SubscriptionImpl

/*
* Created on Jul 11, 2008
* Created by Paul Gardner
*
* Copyright 2008 Vuze, Inc.  All rights reserved.
*
* This program 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; version 2 of the License only.
*
* This program 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.
*/


package com.aelitis.azureus.core.subs.impl;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.security.KeyPair;
import java.util.*;

import org.bouncycastle.util.encoders.Base64;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentCreator;
import org.gudy.azureus2.core3.torrent.TOTorrentFactory;
import org.gudy.azureus2.core3.util.AEThread2;
import org.gudy.azureus2.core3.util.BDecoder;
import org.gudy.azureus2.core3.util.BEncoder;
import org.gudy.azureus2.core3.util.Base32;
import org.gudy.azureus2.core3.util.ByteFormatter;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.FileUtil;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.IndentWriter;
import org.gudy.azureus2.core3.util.LightHashMap;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.TorrentUtils;
import org.json.simple.JSONObject;

import com.aelitis.azureus.core.lws.LightWeightSeed;
import com.aelitis.azureus.core.lws.LightWeightSeedAdapter;
import com.aelitis.azureus.core.lws.LightWeightSeedManager;
import com.aelitis.azureus.core.metasearch.Engine;
import com.aelitis.azureus.core.metasearch.MetaSearchManagerFactory;
import com.aelitis.azureus.core.security.CryptoECCUtils;
import com.aelitis.azureus.core.subs.Subscription;
import com.aelitis.azureus.core.subs.SubscriptionException;
import com.aelitis.azureus.core.subs.SubscriptionHistory;
import com.aelitis.azureus.core.subs.SubscriptionListener;
import com.aelitis.azureus.core.subs.SubscriptionManager;
import com.aelitis.azureus.core.subs.SubscriptionPopularityListener;
import com.aelitis.azureus.core.subs.SubscriptionResult;
import com.aelitis.azureus.core.util.CopyOnWriteList;
import com.aelitis.azureus.core.vuzefile.VuzeFile;
import com.aelitis.azureus.core.vuzefile.VuzeFileHandler;
import com.aelitis.azureus.util.ImportExportUtils;
import com.aelitis.azureus.util.JSONUtils;

public class
SubscriptionImpl
  implements Subscription
{
  public static final int  ADD_TYPE_CREATE    = 1;
  public static final int  ADD_TYPE_IMPORT    = 2;
  public static final int  ADD_TYPE_LOOKUP    = 3;
   
  private static final int MAX_ASSOCIATIONS        = 256;
  private static final int MIN_RECENT_ASSOC_TO_RETAIN    = 16;
   
  //private static final byte[] GENERIC_PUBLIC_KEY     = {(byte)0x04,(byte)0xd0,(byte)0x1a,(byte)0xd9,(byte)0xb9,(byte)0x99,(byte)0xd8,(byte)0x49,(byte)0x15,(byte)0x5f,(byte)0xe9,(byte)0x6b,(byte)0x3c,(byte)0xd8,(byte)0x18,(byte)0x81,(byte)0xf7,(byte)0x92,(byte)0x15,(byte)0x3f,(byte)0x24,(byte)0xaa,(byte)0x35,(byte)0x6f,(byte)0x52,(byte)0x01,(byte)0x79,(byte)0x2e,(byte)0x93,(byte)0xf6,(byte)0xf1,(byte)0x57,(byte)0x13,(byte)0x2a,(byte)0x3c,(byte)0x31,(byte)0x66,(byte)0xa5,(byte)0x34,(byte)0x9f,(byte)0x79,(byte)0x62,(byte)0x04,(byte)0x31,(byte)0x68,(byte)0x37,(byte)0x8f,(byte)0x77,(byte)0x5c};
  // private static final byte[] GENERIC_PRIVATE_KEY   = {(byte)0x71,(byte)0xc3,(byte)0xe8,(byte)0x6c,(byte)0x56,(byte)0xbb,(byte)0x30,(byte)0x14,(byte)0x9e,(byte)0x19,(byte)0xa5,(byte)0x3d,(byte)0xcb,(byte)0x47,(byte)0xbb,(byte)0x6d,(byte)0x57,(byte)0x57,(byte)0xd3,(byte)0x59,(byte)0xce,(byte)0x8f,(byte)0x79,(byte)0xe5};

  protected static byte[]
  intToBytes(
    int    version )
  {
    return( new byte[]{ (byte)(version>>24), (byte)(version>>16),(byte)(version>>8),(byte)version } );
  }
 
  protected static int
  bytesToInt(
    byte[]    bytes )
  {
    return( (bytes[0]<<24)&0xff000000 | (bytes[1] << 16)&0x00ff0000 | (bytes[2] << 8)&0x0000ff00 | bytes[3]&0x000000ff );
  }
   
  private SubscriptionManagerImpl    manager;
 
  private byte[]      public_key;
  private byte[]      private_key;
 
  private String      name;
  private String      name_ex;
 
  private int        version;
  private int        az_version;
 
  private boolean      is_public;
  private Map        singleton_details;
 
  private byte[]      hash;
  private byte[]      sig;
  private int        sig_data_size;
 
  private int        add_type;
  private long      add_time;
 
  private boolean      is_subscribed;
 
  private int        highest_prompted_version;
 
  private byte[]      short_id;

  private String      id;

  private List      associations = new ArrayList();
 
  private int        fixed_random;
 
  private long      popularity        = -1;
 
  private long      last_auto_upgrade_check  = -1;
  private boolean      published;
 
  private boolean      server_published;
  private boolean      server_publication_outstanding;
 
  private boolean      singleton_sp_attempted;
 
  private LightWeightSeed  lws;
  private int        lws_skip_check;
 
  private boolean      destroyed;
 
  private Map        history_map;
  private Map        schedule_map;
 
  private Map        user_data = new LightHashMap();
 
  private final       SubscriptionHistoryImpl  history;
 
  private String      referer;
 
  private CopyOnWriteList  listeners = new CopyOnWriteList();
 
  private Map        verify_cache_details;
  private boolean      verify_cache_result;
 
  private String      creator_ref;
  private String      category;
 
  protected static String
  getSkeletonJSON(
    Engine    engine,
    int      check_interval_mins )
  {
    JSONObject  map = new JSONObject();
   
    map.put( "engine_id", new Long( engine.getId()));
   
    map.put( "search_term", "" );

    map.put( "filters", new HashMap());
   
    map.put( "options", new HashMap());
   
    Map schedule = new HashMap();
   
    schedule.put( "interval", new Long( check_interval_mins ));
   
    List  days = new ArrayList();
   
    for (int i=1;i<=7;i++){
     
      days.add( String.valueOf(i));
    }
   
    schedule.put( "days", days );
   
    map.put( "schedule", schedule );
   
    embedEngines( map, engine );
   
    return( JSONUtils.encodeToJSON( map ));
  }
 
 
    // new subs constructor
 
  protected
  SubscriptionImpl(
    SubscriptionManagerImpl    _manager,
    String            _name,
    boolean            _public,
    Map              _singleton_details,
    String            _json_content,
    int              _add_type )
 
    throws SubscriptionException
  {
    manager  = _manager;
   
    history_map  = new HashMap();

    history = new SubscriptionHistoryImpl( manager, this );
   
    name        = _name;
    is_public      = _public;
    singleton_details  = _singleton_details;
   
    version        = 1;
    az_version      = AZ_VERSION;
   
    add_type      = _add_type;
    add_time      = SystemTime.getCurrentTime();
   
    is_subscribed    = true;

    try{
      KeyPair  kp = CryptoECCUtils.createKeys();
       
      public_key       = CryptoECCUtils.keyToRawdata( kp.getPublic());
      private_key     = CryptoECCUtils.keyToRawdata( kp.getPrivate());
           
     
      fixed_random  = new Random().nextInt();
     
      init();
     
      String json_content = embedEngines( _json_content );
     
      SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, name, is_public, json_content, public_key, version, az_version, singleton_details );
           
      syncToBody( body );
     
    }catch( Throwable e ){
     
      throw( new SubscriptionException( "Failed to create subscription", e ));
    }
  }
 
    // cache detail constructor
 
  protected
  SubscriptionImpl(
    SubscriptionManagerImpl    _manager,
    Map              map )
 
    throws IOException
  {
    manager  = _manager;
       
    fromMap( map );
   
    history = new SubscriptionHistoryImpl( manager, this );

    init();
  }

    // import constructor
 
  protected
  SubscriptionImpl(
    SubscriptionManagerImpl    _manager,
    SubscriptionBodyImpl    _body,
    int              _add_type,
    boolean            _is_subscribed )
 
    throws SubscriptionException
  {
    manager  = _manager;
     
    history_map  = new HashMap();
   
    history = new SubscriptionHistoryImpl( manager, this );
   
    syncFromBody( _body );
   
    add_type    = _add_type;
    add_time    = SystemTime.getCurrentTime();
   
    is_subscribed  = _is_subscribed;
   
    fixed_random  = new Random().nextInt();
   
    init();
       
    syncToBody( _body );
  }
 
  protected void
  syncFromBody(
    SubscriptionBodyImpl  body )
 
    throws SubscriptionException
  {
    public_key      = body.getPublicKey();
    version        = body.getVersion();
    az_version      = body.getAZVersion();
       
    name        = body.getName();
    is_public      = body.isPublic();
    singleton_details  = body.getSingletonDetails();
   
    if ( az_version > AZ_VERSION ){
     
      throw( new SubscriptionException( MessageText.getString( "subscription.version.bad", new String[]{ name })));
    }
  }
 
  protected void
  syncToBody(
    SubscriptionBodyImpl    body )
 
    throws SubscriptionException
  {
      // this picks up latest values of version, name + is_public from here
   
    body.writeVuzeFile( this );
   
    hash       = body.getHash();
    sig        = body.getSig();
    sig_data_size  = body.getSigDataSize();
  }
 
  protected Map
  toMap()
 
    throws IOException
  {
    synchronized( this ){
     
      Map  map = new HashMap();
     
      map.put( "name", name.getBytes( "UTF-8" ));
     
      map.put( "public_key", public_key );
           
      map.put( "version", new Long( version ));
     
      map.put( "az_version", new Long( az_version ));
     
      map.put( "is_public", new Long( is_public?1:0 ));
     
      if ( singleton_details != null ){
       
        map.put( "sin_details", singleton_details );
        map.put( "spa", new Long( singleton_sp_attempted?1:0 ));
      }
     
        // body data
     
      map.put( "hash", hash );
      map.put( "sig", sig );
      map.put( "sig_data_size", new Long( sig_data_size ));
     
        // local data
     
      if ( private_key != null ){
       
        map.put( "private_key", private_key );
      }

      map.put( "add_type", new Long( add_type ));
      map.put( "add_time", new Long( add_time ));
     
      map.put( "subscribed", new Long( is_subscribed?1:0 ));
     
      map.put( "pop", new Long( popularity ));
     
      map.put( "rand", new Long( fixed_random ));
     
      map.put( "hupv", new Long( highest_prompted_version ));
     
      map.put( "sp", new Long( server_published?1:0 ));
      map.put( "spo", new Long( server_publication_outstanding?1:0 ));
           
      if ( associations.size() > 0 ){
       
        List  l_assoc = new ArrayList();
       
        map.put( "assoc", l_assoc );
       
        for (int i=0;i<associations.size();i++){
         
          association assoc = (association)associations.get(i);
         
          Map m = new HashMap();
         
          l_assoc.add( m );
         
          m.put( "h", assoc.getHash());
          m.put( "w", new Long( assoc.getWhen()));
        }
      }
     
      map.put( "history", history_map );
     
      if ( creator_ref != null ){
       
        map.put( "cref", creator_ref.getBytes( "UTF-8" ));
      }
     
      if ( category != null ){
       
        map.put( "cat", category.getBytes( "UTF-8" ));
      }
     
      return( map );
    }
  }
 
  protected void
  fromMap(
    Map    map )
 
    throws IOException
  {
    name        = new String((byte[])map.get( "name"), "UTF-8" );
    public_key      = (byte[])map.get( "public_key" );
    private_key      = (byte[])map.get( "private_key" );
    version        = ((Long)map.get( "version" )).intValue();
    az_version      = (int)ImportExportUtils.importLong( map, "az_version", AZ_VERSION );
    is_public      = ((Long)map.get( "is_public")).intValue() == 1;
    singleton_details  = (Map)map.get( "sin_details" );
   
    hash      = (byte[])map.get( "hash" );
    sig        = (byte[])map.get( "sig" );
    sig_data_size  = ((Long)map.get( "sig_data_size" )).intValue();
   
    fixed_random  = ((Long)map.get( "rand" )).intValue();
   
    add_type    = ((Long)map.get( "add_type" )).intValue();   
    add_time    = ((Long)map.get( "add_time" )).longValue();
   
    is_subscribed  = ((Long)map.get( "subscribed" )).intValue()==1;
       
    popularity    = ((Long)map.get( "pop" )).longValue();
   
    highest_prompted_version = ((Long)map.get( "hupv" )).intValue();
   
    server_published = ((Long)map.get( "sp" )).intValue()==1;
    server_publication_outstanding = ((Long)map.get( "spo" )).intValue()==1;
   
    Long  l_spa = (Long)map.get( "spa" );
   
    if ( l_spa != null ){
      singleton_sp_attempted = l_spa.longValue()==1;
    }
   
    List  l_assoc = (List)map.get( "assoc" );
   
    if ( l_assoc != null ){
     
      for (int i=0;i<l_assoc.size();i++){
       
        Map  m = (Map)l_assoc.get(i);
       
        byte[]    hash   = (byte[])m.get("h");
        long    when  = ((Long)m.get( "w" )).longValue();
       
        associations.add( new association( hash, when ));
      }
    }
   
    history_map = (Map)map.get( "history" );
   
    if ( history_map == null ){
     
      history_map = new HashMap();
    }
   
    byte[] b_cref = (byte[])map.get( "cref" );
   
    if ( b_cref != null ){
     
      creator_ref = new String( b_cref, "UTF-8" );
    }
   
    byte[] b_cat = (byte[])map.get( "cat" );
   
    if ( b_cat != null ){
     
      category = new String( b_cat, "UTF-8" );
    }
  }
 
  protected Map
  getScheduleConfig()
  {
    if ( schedule_map == null ){
     
      try{   
        Map map = JSONUtils.decodeJSON( getJSON());

        schedule_map = (Map)map.get( "schedule" );
       
        if ( schedule_map == null ){
         
          schedule_map = new HashMap();
        }
      }catch( Throwable e ){
       
        log( "Failed to load schedule", e );
       
        schedule_map = new HashMap();
      }
    }
   
    return( schedule_map );
  }
 
  protected Map
  getHistoryConfig()
  {
    return( history_map );
  }
 
  protected void
  updateHistoryConfig(
    Map    _history_map )
  {
    history_map = _history_map;
   
    fireChanged();
  }
 
  protected void
  upgrade(
    SubscriptionBodyImpl    body )
 
    throws SubscriptionException
  {
      // pick up details from the body (excluding json that is maintained in body only)
   
    syncFromBody( body );
   
      // write to file
   
    syncToBody(body);
   
    fireChanged();
  }
 
  protected void
  init()
  {
    short_id = SubscriptionBodyImpl.deriveShortID( public_key, singleton_details );
    id = null;
  }
 
  public boolean
  isSingleton()
  {
    return( singleton_details != null );
  }
 
  public boolean
  isShareable()
  {
    try{
      return( getEngine().isShareable() && !isSingleton());
     
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
     
      return( false );
    }
  }
 
  public boolean
  isSearchTemplate()
  {
    return( getName().startsWith( "Search Template:" ));
  }
 
  protected Map
  getSingletonDetails()
  {
    return( singleton_details );
  }
 
  protected boolean
  getSingletonPublishAttempted()
  {
    return( singleton_sp_attempted );
  }
 
  protected void
  setSingletonPublishAttempted()
  {
    if ( !singleton_sp_attempted ){
     
      singleton_sp_attempted = true;
   
      manager.configDirty( this );
    }
  }
 
  public String
  getName()
  {
    return( name );
  }
 
  public void
  setName(
    String    _name )
 
    throws SubscriptionException
  {
    if ( !name.equals( _name )){
     
      boolean  ok = false;
     
      String  old_name   = name;
      int    old_version  = version;
     
      try{
        name  = _name;
       
        version++;
       
        SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this );
         
        syncToBody( body );
       
        versionUpdated( body, false );
       
        ok  = true;
       
      }finally{
       
        if ( !ok ){
         
          name   = old_name;
          version  = old_version;
        }
      }
     
      fireChanged();
    }
  }
 
  public String
  getNameEx()
  {
    if ( name_ex == null ){
     
      try{
        Map map = JSONUtils.decodeJSON( getJSON());
       
        String  search_term  = (String)map.get( "search_term" );
        Map    filters    = (Map)map.get( "filters" );
 
        Engine engine = manager.getEngine( this, map, true );

        String  engine_name = engine.getNameEx();
       
        if ( name.startsWith( engine_name )){
         
          name_ex = name;
         
        }else if ( engine_name.startsWith( name )){
         
          name_ex = engine_name;
         
        }else{
         
          name_ex = name + ": " + engine.getNameEx();
        }
       
        if ( search_term != null && search_term.length() > 0 ){
         
          name_ex += ", query=" + search_term;
        }
       
        if ( filters != null && filters.size() > 0 ){
         
          name_ex += ", filters=" + new SubscriptionResultFilter(filters).getString();
        }
       
      }catch( Throwable e ){
       
        name_ex = name + ": " + Debug.getNestedExceptionMessage(e);
      }
    }
   
    return( name_ex );
  }
 
  public long
  getAddTime()
  {
    return( add_time );
  }
 
  public boolean
  isPublic()
  {
    return( is_public );
  }
 
  public void
  setPublic(
    boolean    _is_public )
 
    throws SubscriptionException
  {
    if ( is_public != _is_public ){
       
      boolean  ok = false;
     
      boolean  old_public  = is_public;
      int    old_version  = version;
     
      try{
        is_public  = _is_public;
       
        version++;
               
        SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this );
       
        syncToBody( body );
       
        versionUpdated( body, false );

        ok = true;
       
      }finally{
       
        if ( !ok ){
       
          version    = old_version;
          is_public  = old_public;
        }
      }
     
      fireChanged();
    }
  }
 
  protected boolean
  getServerPublicationOutstanding()
  {
    return( server_publication_outstanding );
  }
 
  protected void
  setServerPublicationOutstanding() 
  {
    if ( !server_publication_outstanding ){
     
      server_publication_outstanding = true;
   
      fireChanged();
    }
  }
 
  protected void
  setServerPublished()
  {
    if ( server_publication_outstanding || !server_published ){
     
      server_published         = true;
      server_publication_outstanding  = false;
     
      fireChanged();
    }
  }
 
  protected boolean
  getServerPublished()
  {
    return( server_published );
  }
 
  public String
  getJSON()
 
    throws SubscriptionException
  {
    try{
      SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this );

      return( body.getJSON());
     
    }catch( Throwable e ){
     
      history.setFatalError( Debug.getNestedExceptionMessage(e));
     
      if ( e instanceof SubscriptionException ){
       
        throw((SubscriptionException)e );
      }
     
      throw( new SubscriptionException( "Failed to read subscription", e ));
    }
  }
 
  public boolean
  setJSON(
    String    _json )
 
    throws SubscriptionException
  {
    String json = embedEngines( _json );
   
    SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this );   
   
    String  old_json = body.getJSON();
   
    if ( !json.equals( old_json )){
     
      boolean  ok = false;
     
      int    old_version  = version;
     
      try{       
        version++;
                         
        body.setJSON( json );
       
        syncToBody( body );
       
        versionUpdated( body, true );

        referer = null;
       
        ok  = true;
       
      }finally{
       
        if ( !ok ){
         
          version  = old_version;
        }
      }
     
      fireChanged();
     
      return( true );
    }
   
    return( false );
  }
 
  protected String
  embedEngines(
    String    json_in )
  {
      // see if we need to embed private search templates
   
    Map map = JSONUtils.decodeJSON( json_in );
   
    long   engine_id   = ((Long)map.get( "engine_id" )).longValue();

    String  json_out  = json_in;
   
    if ( engine_id >= Integer.MAX_VALUE || engine_id < 0 ){
     
      Engine engine = MetaSearchManagerFactory.getSingleton().getMetaSearch().getEngine( engine_id );

      if ( engine == null ){
       
        log( "Private search template with id '" + engine_id + "' not found!!!!" );
       
      }else{
       
        try{               
          embedEngines( map, engine );
         
          json_out = JSONUtils.encodeToJSON( map );
         

          log( "Embedded private search template '" + engine.getName() + "'" );
         
        }catch( Throwable e ){
         
          log( "Failed to embed private search template", e );
        }
      }
    }
   
    return( json_out );
  }
 
  protected static void
  embedEngines(
    Map      map,
    Engine    engine )
  {
    Map  engines = new HashMap();
   
    map.put( "engines", engines );
   
    Map  engine_map = new HashMap();
   
    try{
   
      String  engine_str = new String( Base64.encode( BEncoder.encode( engine.exportToBencodedMap())), "UTF-8" );
   
      engine_map.put( "content", engine_str );
   
      engines.put( String.valueOf( engine.getId()), engine_map );
     
    }catch( Throwable e ){
     
      Debug.out( e );
    }
  }

  protected Engine
  extractEngine(
    Map    json_map,
    long  id )
  {
    Map engines = (Map)json_map.get( "engines" );
   
    if ( engines != null ){
     
      Map  engine_map = (Map)engines.get( String.valueOf( id ));
     
      if ( engine_map != null ){
       
        String  engine_str = (String)engine_map.get( "content" );
       
        try{
       
          Map map = BDecoder.decode( Base64.decode( engine_str.getBytes( "UTF-8" )));
           
          return( MetaSearchManagerFactory.getSingleton().getMetaSearch().importFromBEncodedMap(map));
         
        }catch( Throwable e ){
         
          log( "failed to import engine", e );
        }
      }
    }
   
    return( null );
  }
 
  public Engine
  getEngine()
 
    throws SubscriptionException
  {
    return( getEngine( true ));
  }
 
  protected Engine
  getEngine(
    boolean    local_only )
 
    throws SubscriptionException
  {
    Map map = JSONUtils.decodeJSON( getJSON());
         
    return( manager.getEngine( this, map, local_only ));
  }
 
  protected void
  engineUpdated(
    Engine    engine )
  {
    try{
      String  json = getJSON();
     
      Map map = JSONUtils.decodeJSON( json );

      long  id = ((Long)map.get( "engine_id" )).longValue();
     
      if ( id == engine.getId()){
               
        if ( setJSON( json )){
         
          log( "Engine has been updated, saved" );
        }
      }
    }catch( Throwable e ){
     
      log( "Engine update failed", e );
    }
  }
 
  public boolean
  setDetails(
    String    _name,
    boolean    _is_public,
    String    _json )
 
    throws SubscriptionException
  {
    _json = embedEngines( _json );
   
    SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this );   
   
    String  old_json = body.getJSON();
   
    boolean  json_changed = !_json.equals( old_json );
   
    if (   !_name.equals( name ) ||
        _is_public != is_public ||
        json_changed ){
     
      boolean  ok = false;
     
      String  old_name  = name;
      boolean  old_public  = is_public;
      int    old_version  = version;
     
      try{
        is_public  = _is_public;     
        name    = _name;

        body.setJSON( _json );
       
        version++;
                       
        syncToBody( body );
       
        versionUpdated( body, json_changed );

        ok = true;
       
      }finally{
       
        if ( !ok ){
       
          version    = old_version;
          is_public  = old_public;
          name    = old_name;
        }
      }
     
      fireChanged();
     
      return( true );
    }
   
    return( false );
  }
 
  protected void
  versionUpdated(
    SubscriptionBodyImpl    body,
    boolean            json_changed )
  {
    if ( json_changed ){
     
      try{   
        Map map = JSONUtils.decodeJSON( body.getJSON());

        schedule_map = (Map)map.get( "schedule" );
       
      }catch( Throwable e ){
      }
    }
   
    name_ex = null;
   
    if ( is_public ){
     
      manager.updatePublicSubscription( this );
     
      setPublished( false );
     
      synchronized( this ){

        for (int i=0;i<associations.size();i++){
         
          ((association)associations.get(i)).setPublished( false );
        }
      }
    } 
  }
 
  public byte[]
  getPublicKey()
  {
    return( public_key );
  }
 
  public byte[]
  getShortID()
  {
    return( short_id );
  }
 
  public String
  getID()
  {
    if (id == null) {
      id = Base32.encode(getShortID());
    }
    return( id );
  }
 
  protected byte[]
  getPrivateKey()
  {
    return( private_key );
  }
 
  protected int
  getFixedRandom()
  {
    return( fixed_random );
  }
 
  public int
  getVersion()
  {
    return( version );
  }
 
  public int
  getAZVersion()
  {
    return( az_version );
  }
 
  protected void
  setHighestUserPromptedVersion(
    int    v )
  {
    if ( v < version ){
     
      v  = version;
    }
   
    if ( highest_prompted_version != v ){
     
      highest_prompted_version = v;
     
      fireChanged();
    }
  }
 
  protected int
  getHighestUserPromptedVersion()
  {
    return( highest_prompted_version );
  }
 
  public int
  getHighestVersion()
  {
    return( Math.max( version, highest_prompted_version ));
  }
 
  public void
  resetHighestVersion()
  {
    if ( highest_prompted_version > 0 ){
     
      highest_prompted_version = 0;
     
      fireChanged();
     
      manager.checkUpgrade(this);
    }
  }
 
  public boolean
  isMine()
  {
    if ( private_key == null ){
     
      return( false );
    }
   
    if ( isSingleton() && add_type != ADD_TYPE_CREATE ){
     
      return( false );
    }
   
    return( true );
  }
 
  public boolean
  isUpdateable()
  {
    return( private_key != null );
  }
 
  public boolean
  isSubscribed()
  {
    return( is_subscribed );
  }
 
  public void
  setSubscribed(
    boolean      s )
  {
    if ( is_subscribed != s ){
     
      is_subscribed = s;
     
      if ( is_subscribed ){
       
        manager.setSelected( this );
       
      }else{
       
        reset();
      }
     
      fireChanged();
    }
  }
 
  public boolean
  isAutoDownloadSupported()
  {
    return( history.isAutoDownloadSupported());
  }
 
  public void
  getPopularity(
    final SubscriptionPopularityListener  listener )
 
    throws SubscriptionException
  {
    new AEThread2( "subs:popwait", true )
    {
      public void
      run()
      {   
        try{
          manager.getPopularity(
            SubscriptionImpl.this,
            new SubscriptionPopularityListener()
            {
              public void
              gotPopularity(
                long            pop )
              {
                if ( pop != popularity ){
                 
                  popularity = pop;
                 
                  fireChanged();
                }
               
                listener.gotPopularity( popularity );
              }
             
              public void
              failed(
                SubscriptionException    e )
              {
                if ( popularity == -1 ){
                 
                  listener.failed( new SubscriptionException( "Failed to read popularity", e ));
                 
                }else{
                 
                  listener.gotPopularity( popularity );
                }
              }
            });
         
        }catch( Throwable e ){
         
          if ( popularity == -1 ){
         
            listener.failed( new SubscriptionException( "Failed to read popularity", e ));
           
          }else{
           
            listener.gotPopularity( popularity );
          }
        }
      }
    }.start();
  }
 
  public long
  getCachedPopularity()
  {
    return( popularity );
  }
 
  protected void
  setCachedPopularity(
    long    pop )
  {
    if ( pop != popularity ){
     
      popularity    = pop;
     
      fireChanged();
    }
  }
 
  public String
  getReferer()
  {
    if ( referer == null ){
     
      try{
        Map map = JSONUtils.decodeJSON( getJSON());
           
        Engine engine = manager.getEngine( this, map, false );
       
        if ( engine != null ){
                   
          referer = engine.getReferer();
        }
      }catch( Throwable e ){
       
        log( "Failed to get referer", e );
      }
     
      if ( referer == null ){
       
        referer = "";
      }
    }
   
    return( referer );
  }
 
  protected void
  checkPublish()
  {
    synchronized( this ){
     
      if ( destroyed ){
       
        return;
      }
       
        // singleton's not available for upgrade
     
      if ( isSingleton()){
       
        return;
      }
     
        // nothing to do for unsubscribed ones
     
      if ( !isSubscribed()){
       
        return;
      }
     
      if ( popularity > 100 ){
     
          // one off test on whether to track so we have around 100 active
       
        if ( lws_skip_check == 2 ){
         
          return;
         
        }else if ( lws_skip_check == 0 ){
                 
          if ( new Random().nextInt((int)(( popularity + 99 ) / 100 )) == 0 ){
           
            lws_skip_check = 1;
           
          }else{
           
            lws_skip_check = 2;
           
            return;
          }
        }
      }
     
      if ( hash != null ){
       
        boolean  create = false;

        if ( lws == null ){
         
          create = true;
         
        }else{
         
          if ( !Arrays.equals( lws.getHash().getBytes(), hash )){
     
            lws.remove();
           
            create = true;
          }
        }
       
        if ( create ){
                   
          try{
            File original_data_location = manager.getVuzeFile( this );

            if ( original_data_location.exists()){
             
                // make a version based filename to avoid issues regarding multiple
                // versions
             
              final File  versioned_data_location = new File( original_data_location.getParent(), original_data_location.getName() + "." + getVersion());
             
              if ( !versioned_data_location.exists()){
               
                if ( !FileUtil.copyFile( original_data_location, versioned_data_location )){
                 
                  throw( new Exception( "Failed to copy file to '" + versioned_data_location + "'" ));
                }
              }
             
              lws = LightWeightSeedManager.getSingleton().add(
                  getName(),
                  new HashWrapper( hash ),
                  TorrentUtils.getDecentralisedEmptyURL(),
                  versioned_data_location,
                  new LightWeightSeedAdapter()
                  {
                    public TOTorrent
                    getTorrent(
                      byte[]     hash,
                      URL     announce_url,
                      File     data_location)
                   
                      throws Exception
                    {
                      log( " - generating torrent: " + Debug.getCompressedStackTrace());
                     
                      TOTorrentCreator creator =
                        TOTorrentFactory.createFromFileOrDirWithFixedPieceLength(
                            data_location,
                            announce_url,
                            256*1024 );
                 
                      TOTorrent t = creator.create();
                     
                      t.setHashOverride( hash );
                     
                      return( t );
                    }
                  });
            }
               
          }catch( Throwable e ){
           
            log( "Failed to create light-weight-seed", e );
          }
        }
      }
    }
  }
 
  protected synchronized boolean
  canAutoUpgradeCheck()
  {
    if ( isSingleton()){
     
      return( false );
    }
   
    long  now = SystemTime.getMonotonousTime();
   
    if ( last_auto_upgrade_check == -1 || now - last_auto_upgrade_check > 4*60*60*1000 ){
     
      last_auto_upgrade_check = now;
     
      return( true );
    }
   
    return( false );
  }
 
  public void
  addAssociation(
    byte[]    hash )
  {
    synchronized( this ){
 
      for (int i=0;i<associations.size();i++){
       
        association assoc = (association)associations.get(i);
       
        if ( Arrays.equals( assoc.getHash(), hash )){
         
          return;
        }
      }
     
      associations.add( new association( hash, SystemTime.getCurrentTime()));
     
      if ( associations.size() > MAX_ASSOCIATIONS ){
       
        associations.remove( new Random().nextInt( MAX_ASSOCIATIONS - MIN_RECENT_ASSOC_TO_RETAIN ));
      }
    }
   
    fireChanged();
   
    manager.associationAdded( this, hash);
  }
 
  public boolean
  hasAssociation(
    byte[]    hash )
  {
    synchronized( this ){
 
      for (int i=0;i<associations.size();i++){
       
        association assoc = (association)associations.get(i);
       
        if ( Arrays.equals( assoc.getHash(), hash )){
         
          return( true );
        }
      }
    }
     
    return( false )
  }
 
  public void
  addPotentialAssociation(
    String    result_id,
    String    key )
  {
    manager.addPotentialAssociation( this, result_id, key );
  }
 
  public int
  getAssociationCount()
  {
    synchronized( this ){
     
      return( associations.size());
    }
  }
 
  protected association
  getAssociationForPublish()
  {
    synchronized( this ){
     
      int  num_assoc = associations.size();
     
        // first set in order of most recent
     
      for (int i=num_assoc-1;i>=Math.max( 0, num_assoc-MIN_RECENT_ASSOC_TO_RETAIN);i--){
       
        association assoc = (association)associations.get(i);
       
        if ( !assoc.getPublished()){
         
          assoc.setPublished( true );
         
          return( assoc );
        }
      }
     
        // remaining randomised
     
      int  rem = associations.size() - MIN_RECENT_ASSOC_TO_RETAIN;
     
      if ( rem > 0 ){
       
        List l = new ArrayList( associations.subList( 0, rem ));
       
        Collections.shuffle( l );
       
        for (int i=0;i<l.size();i++){
         
          association assoc = (association)l.get(i);

          if ( !assoc.getPublished()){
           
            assoc.setPublished( true );
           
            return( assoc );
          }
        }
      }
    }
   
    return( null );
  }
 
  protected boolean
  getPublished()
  {
    return( published );
  }
 
  protected void
  setPublished(
    boolean    b )
  {
    published = b;
  }
 
  protected int
  getVerifiedPublicationVersion(
    Map    details )
  {
      // singleton versions always 1 and each instance has separate private key so
      // verification will always fail so save to just return current version
   
    if ( isSingleton()){
     
      return( getVersion());
    }
   
    if ( !verifyPublicationDetails( details )){
     
      return( -1 );
    }

    return( getPublicationVersion( details ));
  }
 
  protected static int
  getPublicationVersion(
    Map    details )
  {
    return(((Long)details.get("v")).intValue());
  }
 
  protected byte[]
  getPublicationHash()
  {
    return( hash );
  }
 
  protected static byte[]
  getPublicationHash(
    Map    details )
  {
    return((byte[])details.get( "h" ));
  }
 
  protected static int
  getPublicationSize(
    Map    details )
  {
    return(((Long)details.get("z")).intValue());
  }
 
  protected Map
  getPublicationDetails()
  {
    Map  result = new HashMap();
   
    result.put( "v", new Long( version ));
     
    if ( singleton_details == null ){
     
      result.put( "h", hash );
      result.put( "z", new Long( sig_data_size ));
      result.put( "s", sig );

    }else{
     
      result.put( "x", singleton_details );
    }
   
    return( result );
  }
 
  protected boolean
  verifyPublicationDetails(
    Map    details )
  {
    synchronized( this ){
     
      if ( BEncoder.mapsAreIdentical( verify_cache_details, details )){
               
        return( verify_cache_result );
      }
    }
       
    byte[]  hash   = (byte[])details.get( "h" );
    int    version  = ((Long)details.get( "v" )).intValue();
    int    size  = ((Long)details.get( "z" )).intValue();
    byte[]  sig    = (byte[])details.get( "s" );
   
    boolean  result = SubscriptionBodyImpl.verify( public_key, hash, version, size, sig );
   
    synchronized( this ){
     
      verify_cache_details   = details;
      verify_cache_result    = result;
    }
   
    return( result );
  }
 
  public void
  setCreatorRef(
    String  ref )
  {
    creator_ref = ref;
   
    fireChanged();
  }
 
  public String
  getCreatorRef()
  {
    return( creator_ref );
  }
 
  public void
  setCategory(
    String  _category )
  {
    if ( _category == null && category == null ){
     
      return;
    }
   
    if ( _category != null && category != null && _category.equals( category )){
     
      return;
    }
       
    manager.setCategoryOnExisting( this, category, _category );
   
    category = _category;

    fireChanged();
  }
 
  public String
  getCategory()
  {
    return( category );
  }

  protected void
  fireChanged()
  {
    manager.configDirty( this );
   
    Iterator it = listeners.iterator();
   
    while( it.hasNext()){
     
      try{
        ((SubscriptionListener)it.next()).subscriptionChanged( this );
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
      }
    }
  }

  protected void
  fireDownloaded(
    boolean  was_auto )
  {

    Iterator it = listeners.iterator();
   
    while( it.hasNext()){
     
      try{
        ((SubscriptionListener)it.next()).subscriptionDownloaded( this, was_auto );
       
      }catch( Throwable e ){
       
        Debug.printStackTrace(e);
      }
    }
  }
 
  public void
  addListener(
    SubscriptionListener  l )
  {
    listeners.add( l );
  }
 
  public void
  removeListener(
    SubscriptionListener  l )
  {
    listeners.remove( l );
  }
 
  public SubscriptionHistory
  getHistory()
  {
    return( history );
  }
 
  public SubscriptionManager
  getManager()
  {
    return( manager );
  }
 
  public VuzeFile
  getVuzeFile()
 
    throws SubscriptionException
  {
    try{
      return( VuzeFileHandler.getSingleton().loadVuzeFile( manager.getVuzeFile( this ).getAbsolutePath()));
     
    }catch( Throwable e ){
     
      throw( new SubscriptionException( "Failed to get Vuze file", e ));
    }
  }
 
  protected void
  destroy()
  {
    LightWeightSeed l;
   
    synchronized( this ){
     
      destroyed  = true;
     
      l = lws;
    }
   
    if ( l != null ){
     
      l.remove();
    }
  }
 
  public void
  reset()
  {
    getHistory().reset();
   
    try{
      getEngine().reset();
     
    }catch( Throwable e ){
     
      Debug.printStackTrace(e);
    }
  }
 
  public void
  remove()
  {
    destroy();
   
    manager.removeSubscription( this );
  }
 
  protected boolean
  isRemoved()
  {
    synchronized( this ){

      return( destroyed );
    }
  }
 
  public SubscriptionResult[]
    getResults(
      boolean    include_deleted )
  {
    return( getHistory().getResults( include_deleted ));
  }
 
  public void
  setUserData(
    Object    key,
    Object    data )
  {
    synchronized( user_data ){
     
      if ( data == null ){
       
        user_data.remove( key );
       
      }else{
       
        user_data.put( key, data );
      }
    }
  }
 
  public Object
  getUserData(
    Object    key )
  {
    synchronized( user_data ){

      return( user_data.get( key ));
    }
  }
 
  protected void
  log(
    String    str )
  {
    manager.log( getString() + ": " + str );
  }
 
  protected void
  log(
    String    str,
    Throwable  e )
  {
    manager.log( getString() + ": " + str, e );
  }
 
  public String
  getString()
  {
    return( "name=" + name +
          ",sid=" + ByteFormatter.encodeString( short_id ) +
          ",ver=" + version +
          ",pub=" + is_public +
          ",mine=" + isMine() +
          ",sub=" + is_subscribed +
          (is_subscribed?(",hist={" + history.getString() + "}"):"") +
          ",pop=" + popularity +
          (server_publication_outstanding?",spo=true":""));
  }
 
  protected void
  generate(
    IndentWriter    writer )
  {
    String  engine_str;
   
    try{
     
      engine_str = "" + getEngine().getId();
     
    }catch( Throwable e ){
     
      engine_str = Debug.getNestedExceptionMessage(e);
    }
   
    writer.println( getString() + ": engine=" + engine_str );
     
    try{
      writer.indent();
     
      synchronized( this ){

        for (int i=0;i<associations.size();i++){
         
          ((association)associations.get(i)).generate( writer );
        }
      }
    }finally{
     
      writer.exdent();
    }
  }
 
  protected static class
  association
  {
    private byte[]  hash;
    private long  when;
    private boolean  published;
   
    protected
    association(
      byte[]    _hash,
      long    _when )
    {
      hash    = _hash;
      when    = _when;
    }
   
    protected byte[]
    getHash()
    {
      return( hash );
    }
   
    protected long
    getWhen()
    {
      return( when );
    }
   
    protected boolean
    getPublished()
    {
      return( published );
    }
   
    protected void
    setPublished(
      boolean    b )
    {
      published = b;
    }
   
    protected String
    getString()
    {
      return( ByteFormatter.encodeString( hash ) + ", pub=" + published );
    }
   
    protected void
    generate(
      IndentWriter    writer )
    {
      writer.println( getString());
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.subs.impl.SubscriptionImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.