Package com.aelitis.azureus.core.pairing.impl

Source Code of com.aelitis.azureus.core.pairing.impl.PairingManagerImpl$PairedService2Impl

/*
* Created on Oct 5, 2009
* Created by Paul Gardner
*
* Copyright 2009 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.pairing.impl;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.AESemaphore;
import org.gudy.azureus2.core3.util.AEThread2;
import org.gudy.azureus2.core3.util.AEVerifier;
import org.gudy.azureus2.core3.util.AsyncDispatcher;
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.Constants;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.DelayedEvent;
import org.gudy.azureus2.core3.util.FileUtil;
import org.gudy.azureus2.core3.util.SimpleTimer;
import org.gudy.azureus2.core3.util.SystemProperties;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.TimerEvent;
import org.gudy.azureus2.core3.util.TimerEventPerformer;
import org.gudy.azureus2.core3.util.TimerEventPeriodic;
import org.gudy.azureus2.core3.util.UrlUtils;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.ui.UIManager;
import org.gudy.azureus2.plugins.ui.UIManagerEvent;
import org.gudy.azureus2.plugins.ui.config.ActionParameter;
import org.gudy.azureus2.plugins.ui.config.BooleanParameter;
import org.gudy.azureus2.plugins.ui.config.ConfigSection;
import org.gudy.azureus2.plugins.ui.config.HyperlinkParameter;
import org.gudy.azureus2.plugins.ui.config.InfoParameter;
import org.gudy.azureus2.plugins.ui.config.LabelParameter;
import org.gudy.azureus2.plugins.ui.config.Parameter;
import org.gudy.azureus2.plugins.ui.config.ParameterListener;
import org.gudy.azureus2.plugins.ui.config.StringParameter;
import org.gudy.azureus2.plugins.ui.model.BasicPluginConfigModel;
import org.gudy.azureus2.plugins.utils.DelayedTask;
import org.gudy.azureus2.plugins.utils.StaticUtilities;
import org.gudy.azureus2.pluginsimpl.local.PluginInitializer;
import org.gudy.azureus2.pluginsimpl.local.utils.resourcedownloader.ResourceDownloaderFactoryImpl;
import org.json.simple.parser.JSONParser;

import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.core.AzureusCoreRunningListener;
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdmin;
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdminHTTPProxy;
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdminNetworkInterface;
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdminNetworkInterfaceAddress;
import com.aelitis.azureus.core.networkmanager.admin.NetworkAdminSocksProxy;
import com.aelitis.azureus.core.pairing.*;
import com.aelitis.azureus.core.security.CryptoManager;
import com.aelitis.azureus.core.security.CryptoManagerFactory;
import com.aelitis.azureus.core.util.CopyOnWriteList;
import com.aelitis.azureus.plugins.upnp.UPnPPlugin;
import com.aelitis.azureus.plugins.upnp.UPnPPluginService;
import com.aelitis.azureus.util.JSONUtils;
import com.aelitis.net.upnp.UPnPRootDevice;

public class
PairingManagerImpl
  implements PairingManager
{
  private static final boolean DEBUG  = false;
 
  private static final String  SERVICE_URL;
 
  static{
    String url = System.getProperty( "az.pairing.url", "" );
   
    if ( url.length() == 0 ){
     
      SERVICE_URL = Constants.PAIRING_URL;
     
    }else{
     
      SERVICE_URL = url;
    }
  }
 
  private static final PairingManagerImpl  singleton = new PairingManagerImpl();
 
  public static PairingManager
  getSingleton()
  {
    return( singleton );
  }
 
  private static final int  GLOBAL_UPDATE_PERIOD  = 60*1000;
  private static final int  CD_REFRESH_PERIOD    = 23*60*60*1000;
  private static final int  CD_REFRESH_TICKS    = CD_REFRESH_PERIOD / GLOBAL_UPDATE_PERIOD;
 
  private static final int  CONNECT_TEST_PERIOD_MILLIS  = 30*60*1000;
 
  private AzureusCore  azureus_core;
 
  private BooleanParameter   param_enable;

 
  private InfoParameter    param_ac_info;
  private InfoParameter    param_status_info;
  private InfoParameter    param_last_error;
  private HyperlinkParameter  param_view;
 
  private BooleanParameter   param_e_enable;
  private StringParameter    param_public_ipv4;
  private StringParameter    param_public_ipv6;
  private StringParameter    param_host;
 
  private StringParameter    param_local_ipv4;
  private StringParameter    param_local_ipv6;

  private Map<String,PairedServiceImpl>    services = new HashMap<String, PairedServiceImpl>();
 
  private AESemaphore  init_sem = new AESemaphore( "PM:init" );
 
  private TimerEventPeriodic  global_update_event;
 
  private InetAddress    current_v4;
  private InetAddress    current_v6;
 
  private String      local_v4  = "";
  private String      local_v6  = "";
 
  private boolean  update_outstanding;
  private boolean  updates_enabled;

  private static final int MIN_UPDATE_PERIOD_DEFAULT  = 10*1000;
  private static final int MAX_UPDATE_PERIOD_DEFAULT  = 60*60*1000;
   
  private int min_update_period  = MIN_UPDATE_PERIOD_DEFAULT;
  private int max_update_period  = MAX_UPDATE_PERIOD_DEFAULT;
 
 
  private AsyncDispatcher  dispatcher = new AsyncDispatcher();
 
  private boolean      must_update_once;
  private boolean      update_in_progress;
  private TimerEvent    deferred_update_event;
  private long      last_update_time    = -1;
  private int        consec_update_fails;
 
  private String      last_message;
 
  private Map<String,Object[]>  local_address_checks = new HashMap<String, Object[]>();

  private CopyOnWriteList<PairingManagerListener>  listeners = new CopyOnWriteList<PairingManagerListener>();
 
  protected
  PairingManagerImpl()
  {
    must_update_once = COConfigurationManager.getBooleanParameter( "pairing.updateoutstanding" );

    PluginInterface default_pi = PluginInitializer.getDefaultInterface();
   
    final UIManager  ui_manager = default_pi.getUIManager();
   
    BasicPluginConfigModel configModel = ui_manager.createBasicPluginConfigModel(
        ConfigSection.SECTION_CONNECTION, "Pairing");

    configModel.addHyperlinkParameter2( "ConfigView.label.please.visit.here", MessageText.getString( "ConfigView.section.connection.pairing.url" ));

    param_enable = configModel.addBooleanParameter2( "pairing.enable", "pairing.enable", false );
   
    String  access_code = readAccessCode();
   
    param_ac_info = configModel.addInfoParameter2( "pairing.accesscode", access_code);
   
    param_status_info   = configModel.addInfoParameter2( "pairing.status.info", "" );
   
    param_last_error  = configModel.addInfoParameter2( "pairing.last.error", "" );
   
    param_view = configModel.addHyperlinkParameter2( "pairing.view.registered", SERVICE_URL + "/web/view?ac=" + access_code);

    if ( access_code.length() == 0 ){
     
      param_view.setEnabled( false );
    }
   
    final ActionParameter ap = configModel.addActionParameter2( "pairing.ac.getnew", "pairing.ac.getnew.create" );
   
    ap.addListener(
      new ParameterListener()
      {
        public void
        parameterChanged(
          Parameter   param )
        {
          try{
            ap.setEnabled( false );
           
            allocateAccessCode( false );
           
            SimpleTimer.addEvent(
              "PM:enabler",
              SystemTime.getOffsetTime(30*1000),
              new TimerEventPerformer()
              {
                public void
                perform(
                  TimerEvent event )
                {
                  ap.setEnabled( true );
                }
              });
           
          }catch( Throwable e ){
           
            ap.setEnabled( true );
           
            String details = MessageText.getString(
                "pairing.alloc.fail",
                new String[]{ Debug.getNestedExceptionMessage( e )});
           
            ui_manager.showMessageBox(
                "pairing.op.fail",
                "!" + details + "!",
                UIManagerEvent.MT_OK );
          }
        }
      });
   
    LabelParameter  param_e_info = configModel.addLabelParameter2( "pairing.explicit.info" );
   
    param_e_enable = configModel.addBooleanParameter2( "pairing.explicit.enable", "pairing.explicit.enable", false );
   
    param_public_ipv4  = configModel.addStringParameter2( "pairing.ipv4", "pairing.ipv4", "" );
    param_public_ipv6  = configModel.addStringParameter2( "pairing.ipv6", "pairing.ipv6", "" );
    param_host      = configModel.addStringParameter2( "pairing.host", "pairing.host", "" );
   
    LabelParameter spacer = configModel.addLabelParameter2( "blank.resource" );
   
    param_local_ipv4  = configModel.addStringParameter2( "pairing.local.ipv4", "pairing.local.ipv4", "" );
    param_local_ipv6  = configModel.addStringParameter2( "pairing.local.ipv6", "pairing.local.ipv6", "" );

   
    param_public_ipv4.setGenerateIntermediateEvents( false );
    param_public_ipv6.setGenerateIntermediateEvents( false );
    param_host.setGenerateIntermediateEvents( false );
   
    ParameterListener change_listener =
      new ParameterListener()
      {
        public void
        parameterChanged(
          Parameter param )
        {
          updateNeeded();
         
          if ( param == param_enable ){
           
            fireChanged();
          }
        }
      };
     
    param_enable.addListener( change_listener );
    param_e_enable.addListenerchange_listener );
    param_public_ipv4.addListenerchange_listener );
    param_public_ipv6.addListenerchange_listener );
    param_local_ipv4.addListenerchange_listener );
    param_local_ipv6.addListenerchange_listener );
    param_host.addListenerchange_listener );
   
    param_e_enable.addEnabledOnSelection( param_public_ipv4 );
    param_e_enable.addEnabledOnSelection( param_public_ipv6 );
    param_e_enable.addEnabledOnSelection( param_local_ipv4 );
    param_e_enable.addEnabledOnSelection( param_local_ipv6 );
    param_e_enable.addEnabledOnSelection( param_host );
   
    configModel.createGroup(
      "pairing.group.explicit",
      new Parameter[]{
        param_e_info,
        param_e_enable,
        param_public_ipv4, 
        param_public_ipv6,
        param_host,
        spacer,
        param_local_ipv4, 
        param_local_ipv6,
      });
   
    AzureusCoreFactory.addCoreRunningListener(
      new AzureusCoreRunningListener()
      {
        public void
        azureusCoreRunning(
          AzureusCore core )
        {
          initialise( core );
        }
      });
  }
 
 
  protected void
  initialise(
    AzureusCore    _core )
  {
    synchronized( this ){
     
      azureus_core  = _core;
    }
   
    try{
      PluginInterface default_pi = PluginInitializer.getDefaultInterface();

      DelayedTask dt = default_pi.getUtilities().createDelayedTask(
        new Runnable()
        {
          public void
          run()
          {
            new DelayedEvent(
              "PM:delayinit",
              10*1000,
              new AERunnable()
              {
                public void
                runSupport()
                {
                  enableUpdates();
                }
              });
          }
        });
     
      dt.queue();
     
    }finally{
     
      init_sem.releaseForever();
    }
  }
 
  protected void
  waitForInitialisation()
 
    throws PairingException
  {
    if ( !init_sem.reserve( 30*1000 )){
   
      throw( new PairingException( "Timeout waiting for initialisation" ));
    }
  }
 
  public boolean
  isEnabled()
  {
    return( param_enable.getValue());
  }
 
  public void
  setEnabled(
    boolean  enabled )
  {
    param_enable.setValue( enabled );
  }
 
  public void
  setGroup(
    String group )
  {
    COConfigurationManager.setParameter( "pairing.groupcode", group );
   
    updateNeeded();
  }
 
  public String
  getGroup()
  {
    return( COConfigurationManager.getStringParameter( "pairing.groupcode", null ));
  }

  public List<PairedNode>
  listGroup()
 
    throws PairingException
  {
    try{
      URL url = new URL( SERVICE_URL + "/remote/listGroup?gc=" + getGroup());
     
     
      InputStream is =  new ResourceDownloaderFactoryImpl().create( url ).download();
     
      Map json = JSONUtils.decodeJSON( new String( FileUtil.readInputStreamAsByteArray( is ), "UTF-8" ));
     
      List<Map>  list = (List<Map>)json.get( "result" );
     
      List<PairedNode>  result = new ArrayList<PairedNode>();
     
      String my_ac = peekAccessCode();

      if ( list != null ){
       
        for ( Map m: list ){
         
          PairedNodeImpl node = new PairedNodeImpl( m );
             
          if ( my_ac == null || !my_ac.equals( node.getAccessCode())){
         
            result.add( node );
          }
        }
      }
     
      return( result );
     
    }catch( Throwable e ){
     
      throw( new PairingException( "Failed to list group", e ));
    }
  }
 
  protected void
  setStatus(
    String    str )
  {
    String last_status = param_status_info.getValue();
   
    if ( !last_status.equals( str )){
     
      param_status_info.setValue( str );
   
      fireChanged();
    }
  }
 
  public String
  getStatus()
  {
    return( param_status_info.getValue());
  }
 
  protected void
  setLastServerError(
    String  error )
  {
    String last_error = param_last_error.getValue();
 
    if ( error == null ){
     
      error = "";
    }
   
    if ( !last_error.equals( error )){
     
      param_last_error.setValue( error );
     
      fireChanged();
    }
  }
 
  public String
  getLastServerError()
  {
    String last_error = param_last_error.getValue();

    if ( last_error.length() == 0 ){
     
      last_error = null;
    }
   
    return( last_error );
  }

  public boolean
  hasActionOutstanding()
  {
    synchronized( this ){
     
      if ( !isEnabled()){
       
        return( false );
      }
     
      return( !updates_enabled || update_outstanding || deferred_update_event != null || update_in_progress );
    }
  }
 
  protected String
  readAccessCode()
  {
    return( COConfigurationManager.getStringParameter( "pairing.accesscode", "" ));
  }
 
  protected void
  writeAccessCode(
    String    ac )
  {
    COConfigurationManager.setParameter( "pairing.accesscode", ac );
   
      // try not to loose this!
   
    COConfigurationManager.save();
   
    param_ac_info.setValue( ac );
    
    param_view.setHyperlink( SERVICE_URL + "/web/view?ac=" + ac );
       
    param_view.setEnabled( ac.length() > 0 );
  }
 
  protected String
  allocateAccessCode(
    boolean    updating )
 
    throws PairingException
  {
    Map<String,Object>  request = new HashMap<String, Object>();
   
    String existing = readAccessCode();
   
    request.put( "ac", existing );
   
    Map<String,Object> response = sendRequest( "allocate", request );
   
    try{
      String code = getString( response, "ac" );
     
      writeAccessCode( code );
       
      if ( !updating ){
     
        updateNeeded();
      }
     
      fireChanged();
     
      return( code );
     
    }catch( Throwable e ){
     
      throw( new PairingException( "allocation failed", e ));
    }
  }

  public String
  peekAccessCode()
  {
    return( readAccessCode());
  }
 
  public String
  getAccessCode()
 
    throws PairingException
  {
    waitForInitialisation();
   
    String ac = readAccessCode();
   
    if ( ac == null || ac.length() == 0 ){
     
      ac = allocateAccessCode( false );
    }
   
    return( ac );
  }
 
  public void
  getAccessCode(
    final PairingManagerListener   listener )
 
    throws PairingException
  {
    new AEThread2( "PM:gac", true )
    {
      public void
      run()
      {
        try{
          getAccessCode();
         
        }catch( Throwable e ){
         
        }finally{
         
          listener.somethingChanged( PairingManagerImpl.this );
        }
      }
    }.start();
  }
 
  public String
  getReplacementAccessCode()
 
    throws PairingException
  {
    waitForInitialisation();
   
    String new_code = allocateAccessCode( false );
   
    return( new_code );
  }
 
  public PairedService
  addService(
    String    sid )
  {
    synchronized( this ){
           
      PairedServiceImpl  result = services.get( sid );
     
      if ( result == null ){
       
        if ( DEBUG ){
          System.out.println( "PS: added " + sid );
        }
       
        result = new PairedServiceImpl( sid );
       
        services.put( sid, result );
      }
     
      return( result );
    }
  }
 
  public PairedService
  getService(
    String    sid )
  {
    synchronized( this ){
     
      PairedService  result = services.get( sid );
     
      return( result );
    }
  }
 
  protected void
  remove(
    PairedServiceImpl  service )
  {
    synchronized( this ){

      String sid = service.getSID();
     
      if ( services.remove( sid ) != null ){
       
        if ( DEBUG ){
          System.out.println( "PS: removed " + sid );
        }
      }
    }
   
    updateNeeded();
  }
 
  protected void
  sync(
    PairedServiceImpl  service )
  {
    updateNeeded();
  }
 
  protected InetAddress
  updateAddress(
    InetAddress    current,
    InetAddress    latest,
    boolean      v6 )
  {
    if ( v6 ){
     
      if ( latest instanceof Inet4Address ){
       
        return( current );
      }
    }else{
     
      if ( latest instanceof Inet6Address ){
       
        return( current );
      }
    }
   
    if ( current == latest ){
     
      return( current );
    }
   
    if ( current == null || latest == null ){
     
      return( latest );
    }
   
    if ( !current.equals( latest )){
     
      return( latest );
    }
   
    return( current );
 
 
  protected void
  updateGlobals(
    boolean  is_updating
  {
    final long now = SystemTime.getMonotonousTime();
   
    synchronized( this ){
           
      NetworkAdmin network_admin = NetworkAdmin.getSingleton();
         
      InetAddress latest_v4 = azureus_core.getInstanceManager().getMyInstance().getExternalAddress();
     
      InetAddress temp_v4 = updateAddress( current_v4, latest_v4, false );
     
      InetAddress latest_v6 = network_admin.getDefaultPublicAddressV6();
 
      InetAddress temp_v6 = updateAddress( current_v6, latest_v6, true );
     
      final TreeSet<String>  latest_v4_locals = new TreeSet<String>();
      final TreeSet<String>  latest_v6_locals = new TreeSet<String>();
     
      NetworkAdminNetworkInterface[] interfaces = network_admin.getInterfaces();
     
      List<Runnable>  to_do = new ArrayList<Runnable>();
     
      Set<String> existing_checked = new HashSet<String>( local_address_checks.keySet());
     
      for ( NetworkAdminNetworkInterface intf: interfaces ){
       
        NetworkAdminNetworkInterfaceAddress[] addresses = intf.getAddresses();
       
        for ( NetworkAdminNetworkInterfaceAddress address: addresses ){
         
          final InetAddress ia = address.getAddress();
         
          if ( ia.isLoopbackAddress()){
           
            continue;
          }
         
          if ( ia.isLinkLocalAddress() || ia.isSiteLocalAddress()){
           
            final String a_str = ia.getHostAddress();
           
            existing_checked.remove( a_str );
           
            Object[] check;
           
            synchronized( local_address_checks ){
           
              check = local_address_checks.get( a_str );
            }
           
            boolean run_check = check == null || now - ((Long)check[0]) > CONNECT_TEST_PERIOD_MILLIS;
           
            if ( run_check ){
           
              to_do.add(
                new Runnable()
                {
                  public void
                  run()
                  {
                    Socket socket = new Socket();
                   
                    String  result = a_str;
                   
                    try{
                      socket.bind( new InetSocketAddress( ia, 0 ));
                                               
                      socket.connectnew InetSocketAddress( "www.google.com", 80 ), 10*1000 );
                     
                      result += "*";
                     
                    }catch( Throwable e ){
                     
                    }finally{
                      try{
                        socket.close();
                      }catch( Throwable e ){
                      }
                     
                    }
                   
                    synchronized( local_address_checks ){
                     
                      local_address_checks.put( a_str, new Object[]{ new Long(now), result });
                     
                      if ( ia instanceof Inet4Address ){
                       
                        latest_v4_locals.add( result );
                       
                      }else{
                       
                        latest_v6_locals.add( result );
                      }
                    }
                  }
                });
             
            }else{
             
              synchronized( local_address_checks ){
           
                if ( ia instanceof Inet4Address ){
               
                  latest_v4_locals.add((String)check[1]);
                 
                }else{
                 
                  latest_v6_locals.add((String)check[1]);
                }
              }
            }
          }
        }
      }
     
      if ( to_do.size() > 0 ){
       
        final AESemaphore  sem = new AESemaphore( "PM:check" );
       
        for ( final Runnable r: to_do ){
         
          new AEThread2( "PM:check:", true )
          {
            public void
            run()
            {
              try{
                r.run();
              }finally{
               
                sem.release();
              }
            }
          }.start();
        }
       
        for (int i=0;i<to_do.size();i++){
         
          sem.reserve();
        }
      }
     
      for ( String excess: existing_checked ){
       
        local_address_checks.remove( excess );
      }
     
      String v4_locals_str = getString( latest_v4_locals );
      String v6_locals_str = getString( latest_v6_locals );
     
     
      if temp_v4 != current_v4 ||
          temp_v6 != current_v6 ||
          !v4_locals_str.equals( local_v4 ) ||
          !v6_locals_str.equals( local_v6 )){
       
        current_v4  = temp_v4;
        current_v6  = temp_v6;
        local_v4  = v4_locals_str;
        local_v6  = v6_locals_str;
       
        if ( !is_updating ){
       
          updateNeeded();
        }
      }

    }
  }
 
  protected String
  getString(
    Set<String>  set )
  {
    String  str = "";
   
    for ( String s: set ){
     
      str += (str.length()==0?"":",") + s;
    }
   
    return( str );
  }
 
  protected void
  enableUpdates()
  {   
    synchronized( this ){
     
      updates_enabled = true;

      if ( update_outstanding ){
       
        update_outstanding = false;
       
        updateNeeded();
      }
    }
  }
 
  protected void
  updateNeeded()
  {
    if ( DEBUG ){
      System.out.println( "PS: updateNeeded" );
    }
   
    synchronized( this ){
     
      if ( updates_enabled ){
       
        dispatcher.dispatch(
          new AERunnable()
          {
            public void
            runSupport()
            {
              doUpdate();
            }
          });
           
       
      }else{
       
        setStatus( MessageText.getString( "pairing.status.initialising" ));
       
        update_outstanding  = true;
      }
    }
  }
 
  protected void
  doUpdate()
  {
    long  now = SystemTime.getMonotonousTime();

    synchronized( this ){
     
      if ( deferred_update_event != null ){
       
        return;
      }
     
      long  time_since_last_update = now - last_update_time;
     
      if ( last_update_time > 0 &&  time_since_last_update < min_update_period ){
       
        deferUpdatemin_update_period - time_since_last_update  );
       
        return;
      }
     
      update_in_progress = true;
    }
   
    try{
      Map<String,Object>  payload = new HashMap<String, Object>();
           
      boolean  is_enabled = param_enable.getValue();
     
      synchronized( this ){
       
        List<Map<String,String>>  list =  new ArrayList<Map<String,String>>();
       
        payload.put( "s", list );
       
        if ( services.size() > 0 && is_enabled ){
         
          if ( global_update_event == null ){
           
            global_update_event =
              SimpleTimer.addPeriodicEvent(
              "PM:updater",
              GLOBAL_UPDATE_PERIOD,
              new TimerEventPerformer()
              {
                private int  tick_count;
               
                public void
                perform(
                  TimerEvent event )
                {
                  tick_count++;
                 
                  updateGlobals( false );
                 
                  if ( tick_count % CD_REFRESH_TICKS == 0 ){
                   
                    updateNeeded();
                  }
                }
              });
           
            updateGlobals( true );
          }
         
          for ( PairedServiceImpl service: services.values()){
           
            list.add( service.toMap());
          }
        }else{
         
            // when we get to zero services we want to push through the
            // last update to remove cd
         
          if ( global_update_event == null ){
           
            if ( consec_update_fails == 0 && !must_update_once ){
         
              update_in_progress = false;
             
              setStatus( MessageText.getString( is_enabled?"pairing.status.noservices":"pairing.status.disabled" ));
             
              return;
            }
          }else{
         
            global_update_event.cancel();
         
            global_update_event = null;
          }
        }
       
        last_update_time = now;
      }
     
        // we need a valid access code here!
     
      String ac = readAccessCode();
     
      if ( ac.length() == 0 ){
       
        ac = allocateAccessCode( true );     
      }
     
      payload.put( "ac", ac );
     
      String  gc = getGroup();
     
      if ( gc != null && gc.length() > 0 ){
       
        payload.put( "gc", gc );
      }
     
      synchronized( this ){

        if ( current_v4 != null ){
       
          payload.put( "c_v4", current_v4.getHostAddress());
        }
       
        if ( current_v6 != null ){
         
          payload.put( "c_v6", current_v6.getHostAddress());
        }
     
        if ( local_v4.length() > 0 ){
         
          payload.put( "l_v4", local_v4 );
        }
       
        if ( local_v6.length() > 0 ){
         
          payload.put( "l_v6", local_v6 );
        }
       
        if ( param_e_enable.getValue()){
       
          String host = param_host.getValue().trim();
         
          if ( host.length() > 0 ){
           
            payload.put( "e_h", host );
          }
         
          String v4 = param_public_ipv4.getValue().trim();
         
          if ( v4.length() > 0 ){
           
            payload.put( "e_v4", v4 );
          }
         
          String v6 = param_public_ipv6.getValue().trim();
         
          if ( v6.length() > 0 ){
           
            payload.put( "e_v6", v6 );
          }
         
          String l_v4 = param_local_ipv4.getValue().trim();
         
          if ( l_v4.length() > 0 ){
           
            payload.put( "e_l_v4", l_v4 );
          }
         
          String l_v6 = param_local_ipv6.getValue().trim();
         
          if ( l_v6.length() > 0 ){
           
            payload.put( "e_l_v6", l_v6 );
          }
        }
       
          // grab some UPnP info for diagnostics
       
        try{
            PluginInterface pi_upnp = azureus_core.getPluginManager().getPluginInterfaceByClass( UPnPPlugin.class );

            if ( pi_upnp != null ){
             
                UPnPPlugin upnp = (UPnPPlugin)pi_upnp.getPlugin();

                if ( upnp.isEnabled()){
                 
                  List<Map<String,String>>  upnp_list = new ArrayList<Map<String,String>>();
                 
                  payload.put( "upnp", upnp_list );
                 
                  UPnPPluginService[] services = upnp.getServices();
                 
                  Set<UPnPRootDevice> devices = new HashSet<UPnPRootDevice>();
                 
                  for ( UPnPPluginService service: services ){
                   
                    UPnPRootDevice root_device = service.getService().getGenericService().getDevice().getRootDevice();
                   
                    if ( !devices.contains( root_device )){
                     
                      devices.add( root_device );
                 
                      Map<String,String>  map = new HashMap<String, String>();
                   
                      upnp_list.add( map );
                     
                      map.put( "i", root_device.getInfo());
                    }
                  }
                }
            }
        }catch( Throwable e ){         
        }
       
        try{
          NetworkAdmin admin = NetworkAdmin.getSingleton();
         
          NetworkAdminHTTPProxy http_proxy = admin.getHTTPProxy();
         
          if ( http_proxy != null ){
           
            payload.put( "hp", http_proxy.getName());
          }
         
          NetworkAdminSocksProxy[] socks_proxies = admin.getSocksProxies();
         
          if ( socks_proxies.length > 0 ){
           
            payload.put( "sp", socks_proxies[0].getName());
          }
        }catch( Throwable e ){ 
        }
       
        payload.put( "_enabled", is_enabled?1L:0L );
      }
     
      if ( DEBUG ){
        System.out.println( "PS: doUpdate: " + payload );
      }
     
      sendRequest( "update", payload );
     
      synchronized( this ){

        consec_update_fails  = 0;
       
        must_update_once = false;
       
        if ( deferred_update_event == null ){
                   
          COConfigurationManager.setParameter( "pairing.updateoutstanding", false );
        }

        update_in_progress = false;
       
        if ( global_update_event == null ){
         
          setStatus( MessageText.getString( is_enabled?"pairing.status.noservices":"pairing.status.disabled" ));
         
        }else{
         
          setStatus(
            MessageText.getString(
              "pairing.status.registered",
              new String[]{ new SimpleDateFormat().format(new Date( SystemTime.getCurrentTime() ))}));
        }
      }
    }catch( Throwable e ){
     
      synchronized( this ){
       
        try{
          consec_update_fails++;
   
          long back_off = min_update_period;
         
          for (int i=0;i<consec_update_fails;i++){
           
            back_off *= 2;
           
            if ( back_off > max_update_period ){
           
              back_off = max_update_period;
             
              break;
            }
          }
         
          deferUpdate( back_off );
         
        }finally{
       
          update_in_progress = false;
        }
      }
    }finally{
     
      synchronized( this ){
       
        if ( update_in_progress ){
       
          Debug.out( "Something didn't clear update_in_progress!!!!" );
         
          update_in_progress = false;
        }
      }
    }
  }
 
  protected void
  deferUpdate(
    long  millis )
  {
    millis += 5000;
   
    long target = SystemTime.getOffsetTime( millis );
   
    deferred_update_event =
      SimpleTimer.addEvent(
        "PM:defer",
        target,
        new TimerEventPerformer()
        {
          public void
          perform(
            TimerEvent event )
          {
            synchronized( PairingManagerImpl.this ){
             
              deferred_update_event = null;
            }
           
            COConfigurationManager.setParameter( "pairing.updateoutstanding", false );
           
            updateNeeded();
          }
        });
   
    setStatus(
        MessageText.getString(
          "pairing.status.pending",
          new String[]{ new SimpleDateFormat().format(new Date( target ))}));

    COConfigurationManager.setParameter( "pairing.updateoutstanding", true );
  }
 
 
  private Map<String, Object>
  sendRequest(
    String         command,
    Map<String, Object> payload )
   
    throws PairingException
  {
    try{
      Map<String, Object> request = new HashMap<String, Object>();

      CryptoManager cman = CryptoManagerFactory.getSingleton();

      String azid = Base32.encode( cman.getSecureID());

      payload.put( "_azid", azid );

      try{
        String pk = Base32.encode( cman.getECCHandler().getPublicKey( "pairing" ));

        payload.put( "_pk", pk );
       
      }catch( Throwable e ){ 
      }
     
      request.put( "req", payload );
     
      String request_str = Base32.encode( BEncoder.encode( request ));
     
      String  sig = null;
     
      try{
        sig = Base32.encode( cman.getECCHandler().sign( request_str.getBytes( "UTF-8" ), "pairing" ));
       
      }catch( Throwable e ){
      }
     
      String other_params =
        "&ver=" + UrlUtils.encode( Constants.AZUREUS_VERSION ) +
        "&app=" + UrlUtils.encode( SystemProperties.getApplicationName()) +
        "&locale=" + UrlUtils.encode( MessageText.getCurrentLocale().toString());

      if ( sig != null ){
       
        other_params += "&sig=" + sig;
      }
     
      URL target = new URL( SERVICE_URL + "/client/" + command + "?request=" + request_str + other_params );
     
      HttpURLConnection connection = (HttpURLConnection)target.openConnection();
     
      connection.setConnectTimeout( 30*1000 );
     
      InputStream is = connection.getInputStream();
     
      Map<String,Object> response = (Map<String,Object>)BDecoder.decode( new BufferedInputStream( is ));
     
      synchronized( this ){
       
        Long  min_retry = (Long)response.get( "min_secs" );
       
        if ( min_retry != null ){
         
          min_update_period  = min_retry.intValue()*1000;
        }
       
        Long  max_retry = (Long)response.get( "max_secs" );
       
        if ( max_retry != null ){
         
          max_update_period  = max_retry.intValue()*1000;
        }
      }
     
      final String message = getString( response, "message" );
     
      if ( message != null ){
       
        if ( last_message == null || !last_message.equals( message )){
         
          last_message = message;
       
          try{
            byte[] message_sig = (byte[])response.get( "message_sig" );
           
            AEVerifier.verifyData( message, message_sig );
           
            new AEThread2( "PairMsg", true )
            {
              public void
              run()
              {
                UIManager ui_manager = StaticUtilities.getUIManager( 120*1000 );
               
                if ( ui_manager != null ){
               
                  ui_manager.showMessageBox(
                      "pairing.server.warning.title",
                      "!" + message + "!",
                      UIManagerEvent.MT_OK );
                }
              }
            }.start();
           
          }catch( Throwable e ){
          }
        }
      }
     
      String error = getString( response, "error" );
     
      if ( error != null ){
       
        throw( new PairingException( error ));
      }
     
      setLastServerError( null );
           
      return((Map<String,Object>)response.get( "rep" ));
     
    }catch( Throwable e ){
           
      setLastServerError( Debug.getNestedExceptionMessage( e ));
     
      if ( e instanceof PairingException ){
       
        throw((PairingException)e);
      }
     
      throw( new PairingException( "invocation failed", e ));
    }
  }
 
 
  public PairingTest
  testService(
    String           sid,
    PairingTestListener   listener )
 
    throws PairingException
  {
    return( new TestServiceImpl( sid, listener ));
  }
 
  protected void
  fireChanged()
  {
    for ( PairingManagerListener l: listeners ){
     
      try{
        l.somethingChanged( this );
       
      }catch( Throwable e ){
       
        Debug.out( e );
      }
    }
  }
   
  public void
  addListener(
    PairingManagerListener    l )
  {
    listeners.add( l );
  }
 
  public void
  removeListener(
    PairingManagerListener    l )
  {
    listeners.remove( l );
  }
 
  protected String
  getString(
    Map<String,Object>  map,
    String        name )
 
    throws IOException
  {
    byte[]  bytes = (byte[])map.get(name);
   
    if ( bytes == null ){
     
      return( null );
    }
   
    return( new String( bytes, "UTF-8" ));
  }
 
  protected class
  TestServiceImpl
    implements PairingTest
  {
    final private String          sid;
    final private PairingTestListener    listener;
   
    private volatile int    outcome = OT_PENDING;
    private volatile String    error_message;
   
    private volatile boolean  cancelled;
   
    protected
    TestServiceImpl(
      String           _sid,
      PairingTestListener   _listener )
    {
      sid      = _sid;
      listener  = _listener;
     
      new AEThread2( "PM:test" )
      {
        public void
        run()
        {
          try{
            String  access_code    = null;
            long  sid_wait_start  = -1;
           
            while( true ){
             
              if ( !isEnabled()){
               
                throw( new Exception( "Pairing is disabled" ));
              }
             
              access_code = peekAccessCode();
             
              if ( access_code != null ){
               
                if ( !hasActionOutstanding()){
                 
                  if ( getService( sid ) != null ){
                 
                    break;
                   
                  }else{
                   
                    long  now = SystemTime.getMonotonousTime();
                   
                    if ( sid_wait_start == -1 ){
                     
                      sid_wait_start = now;
                     
                    }else{
                     
                      if ( now - sid_wait_start > 5000 ){
                       
                        break;
                      }
                    }
                  }
                }
              }
             
              Thread.sleep( 500 );
             
              if ( cancelled ){
               
                outcome = OT_CANCELLED;
               
                return;
              }
            }
           
            PairedService service = getService( sid );
           
            if ( service == null ){
             
              throw( new Exception( "Service not found" ));
            }
           
            listener.testStarted( TestServiceImpl.this );
           
            String other_params =
              "&ver=" + UrlUtils.encode( Constants.AZUREUS_VERSION ) +
              "&app=" + UrlUtils.encode( SystemProperties.getApplicationName()) +
              "&locale=" + UrlUtils.encode( MessageText.getCurrentLocale().toString());
           
            URL target = new URL( SERVICE_URL + "/web/test?sid=" + sid + "&ac=" + access_code + "&format=bencode" + other_params );
           
            HttpURLConnection connection = (HttpURLConnection)target.openConnection();
           
            connection.setConnectTimeout( 10*1000 );
           
            try{
              InputStream is = connection.getInputStream();
             
              Map<String,Object> response = (Map<String,Object>)BDecoder.decode( new BufferedInputStream( is ));
             
              response = BDecoder.decodeStrings( response );
             
              Long code = (Long)response.get( "code" );
             
              if ( code == null ){
               
                throw( new Exception( "Code missing from reply" ));
              }
                       
              error_message = (String)response.get( "msg" );
             
              if ( code == 1 ){
               
                outcome = OT_SUCCESS;
               
              }else if ( code == 2 ){
               
                outcome = OT_SERVER_OVERLOADED;
               
              }else if ( code == 3 ){
               
                outcome = OT_SERVER_FAILED;
               
              }else if ( code == 4 ){
               
                outcome = OT_FAILED;
               
                error_message = "Connect timeout";
               
              }else if ( code == 5 ){
               
                outcome = OT_FAILED;
               
              }else{
               
                outcome = OT_SERVER_FAILED;;
               
                error_message = "Unknown response code " + code;
              }
            }catch( SocketTimeoutException e ){
             
              outcome = OT_SERVER_UNAVAILABLE;
             
              error_message = "Connect timeout";
             
            }
          }catch( Throwable e ){
           
            outcome = OT_SERVER_UNAVAILABLE;
           
            error_message = Debug.getNestedExceptionMessage( e );
           
          }finally{
           
            listener.testComplete( TestServiceImpl.this );
          }
        }
      }.start();
    }
   
    public int
    getOutcome()
    {
      return( outcome );
    }
   
    public String
    getErrorMessage()
    {
      return( error_message );
    }
   
    public void
    cancel()
    {
      cancelled  = true;
    }
  }
 
  protected class
  PairedServiceImpl
    implements PairedService, PairingConnectionData
  {
    private String        sid;
    private Map<String,String>  attributes  = new HashMap<String, String>();
   
    protected
    PairedServiceImpl(
      String    _sid )
    {
      sid    = _sid;
    }
   
    public String
    getSID()
    {
      return( sid );
    }
   
    public PairingConnectionData
    getConnectionData()
    {
      return( this );
    }
   
    public void
    remove()
    {
      PairingManagerImpl.this.remove( this );
    }
   
    public void
    setAttribute(
      String    name,
      String    value )
    {
      synchronized( this ){
       
        if ( DEBUG ){
          System.out.println( "PS: " + sid + ": " + name + " -> " + value );
        }
       
        if  ( value == null ){
         
          attributes.remove( name );
         
        }else{
         
          attributes.put( name, value );
        }
      }
    }
   
    public String
    getAttribute(
      String    name )
    {
      return( attributes.get( name ));
    }
   
    public void
    sync()
    {
      PairingManagerImpl.this.sync( this );
    }
   
    protected Map<String,String>
    toMap()
    {
      Map<String,String> result = new HashMap<String, String>();
     
      result.put( "sid", sid );
     
      synchronized( this ){
     
        result.putAll( attributes );
      }
     
      return( result );
    }
  }
 
  private class
  PairedNodeImpl
    implements PairedNode
  {
    private Map    map;
   
    protected
    PairedNodeImpl(
      Map    _map )
    {
      map  = _map;
    }
   
    public String
    getAccessCode()
    {
      return((String)map.get( "ac" ));
    }
   
    public List<InetAddress>
    getAddresses()
    {
      Set<InetAddress> addresses = new HashSet<InetAddress>();
     
      addAddress( addresses, "c_v4" );
      addAddress( addresses, "c_v6" );
      addAddress( addresses, "l_v4" );
      addAddress( addresses, "l_v6" );
      addAddress( addresses, "e_v4" );
      addAddress( addresses, "e_v6" );
      addAddress( addresses, "e_l_v4" );
      addAddress( addresses, "e_l_v6" );
      addAddress( addresses, "e_h" );
     
      return( new ArrayList<InetAddress>( addresses ));
    }
   
    private void
    addAddress(
      Set<InetAddress>  addresses,
      String        key )
    {
      String str = (String)map.get( key );
     
      if ( str != null ){
       
        String[] bits = str.split(",");
       
        for ( String bit: bits ){
         
          bit = bit.trim();
         
          if ( bit.length() == 0 ){
           
            continue;
          }
         
         
          if ( bit.endsWith( "*" )){
           
            bit = bit.substring( 0, bit.length()-1 );
          }

          try{
            addresses.add( InetAddress.getByName( bit ));
           
          }catch( Throwable e ){
          }
        }
      }
    }
   
    public List<PairedService>
    getServices()
    {
      Map<String,Map> smap = (Map)map.get( "services" );
     
      List<PairedService>  services = new ArrayList<PairedService>();
     
      for ( Map.Entry<String,Map> entry: smap.entrySet()){
       
        services.add( new PairedService2Impl( entry.getKey(), entry.getValue()));
      }
     
      return( services );
    }
  }
 
  private class
  PairedService2Impl
    implements PairedService
  {
    private String    sid;
    private Map      map;
   
    protected
    PairedService2Impl(
      String    _sid,
      Map      _map )
    {
      sid    = _sid;
      map    = _map;
    }
   
    public String
    getSID()
    {
      return( sid );
    }
   
    public PairingConnectionData
    getConnectionData()
    {
      return( new PairingConnectionData2( map ));
    }
   
    public void
    remove()
    {
      throw( new RuntimeException( "Not supported" ));
    }
  }
 
  private class
  PairingConnectionData2
    implements PairingConnectionData
  {
    private Map    map;
   
    protected
    PairingConnectionData2(
      Map    _map )
    {
      map    = _map;
    }
   
    public void
    setAttribute(
      String    name,
      String    value )
    {
      throw( new RuntimeException( "Not supported" ));
    }
   
    public String
    getAttribute(
      String    name )
    {
      return( (String)map.get( name ));
    }
   
    public void
    sync()
    {
      throw( new RuntimeException( "Not supported" ));
    }
  }
}
TOP

Related Classes of com.aelitis.azureus.core.pairing.impl.PairingManagerImpl$PairedService2Impl

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.