1 /* 2 * Created on Jul 11, 2008 3 * Created by Paul Gardner 4 * 5 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 */ 19 20 21 package com.aelitis.azureus.core.subs.impl; 22 23 import java.io.File; 24 import java.io.IOException; 25 import java.net.URL; 26 import java.security.KeyPair; 27 import java.util.*; 28 29 import org.gudy.azureus2.core3.internat.MessageText; 30 import org.gudy.azureus2.core3.torrent.TOTorrent; 31 import org.gudy.azureus2.core3.torrent.TOTorrentCreator; 32 import org.gudy.azureus2.core3.torrent.TOTorrentFactory; 33 import org.gudy.azureus2.core3.util.AENetworkClassifier; 34 import org.gudy.azureus2.core3.util.AEThread2; 35 import org.gudy.azureus2.core3.util.BDecoder; 36 import org.gudy.azureus2.core3.util.BEncoder; 37 import org.gudy.azureus2.core3.util.Base32; 38 import org.gudy.azureus2.core3.util.ByteFormatter; 39 import org.gudy.azureus2.core3.util.Debug; 40 import org.gudy.azureus2.core3.util.FileUtil; 41 import org.gudy.azureus2.core3.util.HashWrapper; 42 import org.gudy.azureus2.core3.util.IndentWriter; 43 import org.gudy.azureus2.core3.util.LightHashMap; 44 import org.gudy.azureus2.core3.util.RandomUtils; 45 import org.gudy.azureus2.core3.util.SystemTime; 46 import org.gudy.azureus2.core3.util.TorrentUtils; 47 import org.gudy.azureus2.core3.util.UrlUtils; 48 import org.gudy.bouncycastle.util.encoders.Base64; 49 import org.json.simple.JSONObject; 50 51 import com.aelitis.azureus.core.lws.LightWeightSeed; 52 import com.aelitis.azureus.core.lws.LightWeightSeedAdapter; 53 import com.aelitis.azureus.core.lws.LightWeightSeedManager; 54 import com.aelitis.azureus.core.metasearch.Engine; 55 import com.aelitis.azureus.core.metasearch.MetaSearchManagerFactory; 56 import com.aelitis.azureus.core.security.CryptoECCUtils; 57 import com.aelitis.azureus.core.subs.Subscription; 58 import com.aelitis.azureus.core.subs.SubscriptionException; 59 import com.aelitis.azureus.core.subs.SubscriptionHistory; 60 import com.aelitis.azureus.core.subs.SubscriptionListener; 61 import com.aelitis.azureus.core.subs.SubscriptionManager; 62 import com.aelitis.azureus.core.subs.SubscriptionPopularityListener; 63 import com.aelitis.azureus.core.subs.SubscriptionResult; 64 import com.aelitis.azureus.core.util.CopyOnWriteList; 65 import com.aelitis.azureus.core.vuzefile.VuzeFile; 66 import com.aelitis.azureus.core.vuzefile.VuzeFileHandler; 67 import com.aelitis.azureus.util.ImportExportUtils; 68 import com.aelitis.azureus.util.JSONUtils; 69 70 public class 71 SubscriptionImpl 72 implements Subscription 73 { 74 75 private static final int MAX_ASSOCIATIONS; 76 77 static{ 78 int max_assoc = 256; 79 80 try{ 81 max_assoc = Integer.parseInt( System.getProperty( "azureus.subs.max.associations", ""+max_assoc)); 82 83 }catch( Throwable e ){ 84 Debug.out( e ); 85 } 86 87 MAX_ASSOCIATIONS = max_assoc; 88 } 89 90 private static final int MIN_RECENT_ASSOC_TO_RETAIN = 16; 91 92 //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}; 93 // 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}; 94 95 protected static byte[] intToBytes( int version )96 intToBytes( 97 int version ) 98 { 99 return( new byte[]{ (byte)(version>>24), (byte)(version>>16),(byte)(version>>8),(byte)version } ); 100 } 101 102 protected static int bytesToInt( byte[] bytes )103 bytesToInt( 104 byte[] bytes ) 105 { 106 return( (bytes[0]<<24)&0xff000000 | (bytes[1] << 16)&0x00ff0000 | (bytes[2] << 8)&0x0000ff00 | bytes[3]&0x000000ff ); 107 } 108 109 private SubscriptionManagerImpl manager; 110 111 private byte[] public_key; 112 private byte[] private_key; 113 114 private String name; 115 private String name_ex; 116 117 private int version; 118 private int az_version; 119 120 private boolean is_public; // whether or not we publish associations 121 private boolean is_anonymous; // whether or not the subscription is anon 122 123 private Map singleton_details; 124 125 private byte[] hash; 126 private byte[] sig; 127 private int sig_data_size; 128 129 private int add_type; 130 private long add_time; 131 132 private boolean is_subscribed; 133 134 private int highest_prompted_version; 135 136 private byte[] short_id; 137 138 private String id; 139 140 private List<association> associations = new ArrayList<association>(); 141 142 private int fixed_random; 143 144 private long popularity = -1; 145 146 private long last_auto_upgrade_check = -1; 147 private boolean published; 148 149 private boolean server_published; 150 private boolean server_publication_outstanding; 151 152 private boolean singleton_sp_attempted; 153 private String local_name; 154 155 private LightWeightSeed lws; 156 private int lws_skip_check; 157 158 private boolean destroyed; 159 160 private Map history_map; 161 private Map schedule_map; 162 163 private Map user_data = new LightHashMap(); 164 165 private final SubscriptionHistoryImpl history; 166 167 private String referer; 168 169 private CopyOnWriteList listeners = new CopyOnWriteList(); 170 171 private Map verify_cache_details; 172 private boolean verify_cache_result; 173 174 private String creator_ref; 175 private String category; 176 private long tag_id = -1; 177 private String parent; 178 179 protected static String getSkeletonJSON( Engine engine, int check_interval_mins )180 getSkeletonJSON( 181 Engine engine, 182 int check_interval_mins ) 183 { 184 JSONObject map = new JSONObject(); 185 186 map.put( "engine_id", new Long( engine.getId())); 187 188 map.put( "search_term", "" ); 189 190 map.put( "filters", new HashMap()); 191 192 map.put( "options", new HashMap()); 193 194 Map schedule = new HashMap(); 195 196 schedule.put( "interval", new Long( check_interval_mins )); 197 198 List days = new ArrayList(); 199 200 for (int i=1;i<=7;i++){ 201 202 days.add( String.valueOf(i)); 203 } 204 205 schedule.put( "days", days ); 206 207 map.put( "schedule", schedule ); 208 209 embedEngines( map, engine ); 210 211 return( JSONUtils.encodeToJSON( map )); 212 } 213 214 protected static String getSkeletonJSON( Engine engine, String term, String networks, int check_interval_mins )215 getSkeletonJSON( 216 Engine engine, 217 String term, 218 String networks, 219 int check_interval_mins ) 220 { 221 JSONObject map = new JSONObject(); 222 223 map.put( "engine_id", new Long( engine.getId())); 224 225 map.put( "search_term", term ); 226 227 if ( networks != null ){ 228 229 map.put( "networks", networks ); 230 } 231 232 map.put( "filters", new HashMap()); 233 234 map.put( "options", new HashMap()); 235 236 Map schedule = new HashMap(); 237 238 schedule.put( "interval", new Long( check_interval_mins )); 239 240 List days = new ArrayList(); 241 242 for (int i=1;i<=7;i++){ 243 244 days.add( String.valueOf(i)); 245 } 246 247 schedule.put( "days", days ); 248 249 map.put( "schedule", schedule ); 250 251 embedEngines( map, engine ); 252 253 return( JSONUtils.encodeToJSON( map )); 254 } 255 256 257 // new subs constructor 258 259 protected SubscriptionImpl( SubscriptionManagerImpl _manager, String _name, boolean _public, boolean _anonymous, Map _singleton_details, String _json_content, int _add_type )260 SubscriptionImpl( 261 SubscriptionManagerImpl _manager, 262 String _name, 263 boolean _public, 264 boolean _anonymous, 265 Map _singleton_details, 266 String _json_content, 267 int _add_type ) 268 269 throws SubscriptionException 270 { 271 manager = _manager; 272 273 history_map = new HashMap(); 274 275 history = new SubscriptionHistoryImpl( manager, this ); 276 277 name = _name; 278 is_public = _public; 279 is_anonymous = _anonymous; 280 singleton_details = _singleton_details; 281 282 version = 1; 283 az_version = AZ_VERSION; 284 285 add_type = _add_type; 286 add_time = SystemTime.getCurrentTime(); 287 288 is_subscribed = true; 289 290 try{ 291 KeyPair kp = CryptoECCUtils.createKeys(); 292 293 public_key = CryptoECCUtils.keyToRawdata( kp.getPublic()); 294 private_key = CryptoECCUtils.keyToRawdata( kp.getPrivate()); 295 296 297 fixed_random = RandomUtils.nextInt(); 298 299 init(); 300 301 String json_content = embedEngines( _json_content ); 302 303 SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, name, is_public, is_anonymous, json_content, public_key, version, az_version, singleton_details ); 304 305 syncToBody( body ); 306 307 }catch( Throwable e ){ 308 309 throw( new SubscriptionException( "Failed to create subscription", e )); 310 } 311 } 312 313 // cache detail constructor 314 315 protected SubscriptionImpl( SubscriptionManagerImpl _manager, Map map )316 SubscriptionImpl( 317 SubscriptionManagerImpl _manager, 318 Map map ) 319 320 throws IOException 321 { 322 manager = _manager; 323 324 fromMap( map ); 325 326 history = new SubscriptionHistoryImpl( manager, this ); 327 328 init(); 329 } 330 331 // import constructor 332 333 protected SubscriptionImpl( SubscriptionManagerImpl _manager, SubscriptionBodyImpl _body, int _add_type, boolean _is_subscribed )334 SubscriptionImpl( 335 SubscriptionManagerImpl _manager, 336 SubscriptionBodyImpl _body, 337 int _add_type, 338 boolean _is_subscribed ) 339 340 throws SubscriptionException 341 { 342 manager = _manager; 343 344 history_map = new HashMap(); 345 346 history = new SubscriptionHistoryImpl( manager, this ); 347 348 syncFromBody( _body ); 349 350 add_type = _add_type; 351 add_time = SystemTime.getCurrentTime(); 352 353 is_subscribed = _is_subscribed; 354 355 fixed_random = RandomUtils.nextInt(); 356 357 init(); 358 359 syncToBody( _body ); 360 } 361 362 protected void syncFromBody( SubscriptionBodyImpl body )363 syncFromBody( 364 SubscriptionBodyImpl body ) 365 366 throws SubscriptionException 367 { 368 public_key = body.getPublicKey(); 369 version = body.getVersion(); 370 az_version = body.getAZVersion(); 371 372 name = body.getName(); 373 is_public = body.isPublic(); 374 is_anonymous = body.isAnonymous(); 375 singleton_details = body.getSingletonDetails(); 376 377 if ( az_version > AZ_VERSION ){ 378 379 throw( new SubscriptionException( MessageText.getString( "subscription.version.bad", new String[]{ name }))); 380 } 381 } 382 383 protected void syncToBody( SubscriptionBodyImpl body )384 syncToBody( 385 SubscriptionBodyImpl body ) 386 387 throws SubscriptionException 388 { 389 // this picks up latest values of version, name + is_public from here 390 391 body.writeVuzeFile( this ); 392 393 hash = body.getHash(); 394 sig = body.getSig(); 395 sig_data_size = body.getSigDataSize(); 396 } 397 398 protected Map toMap()399 toMap() 400 401 throws IOException 402 { 403 synchronized( this ){ 404 405 Map map = new HashMap(); 406 407 map.put( "name", name.getBytes( "UTF-8" )); 408 409 map.put( "public_key", public_key ); 410 411 map.put( "version", new Long( version )); 412 413 map.put( "az_version", new Long( az_version )); 414 415 map.put( "is_public", new Long( is_public?1:0 )); 416 417 map.put( "is_anonymous", new Long( is_anonymous?1:0 )); 418 419 if ( singleton_details != null ){ 420 421 map.put( "sin_details", singleton_details ); 422 map.put( "spa", new Long( singleton_sp_attempted?1:0 )); 423 } 424 425 if ( local_name != null ){ 426 427 map.put( "local_name", local_name ); 428 } 429 // body data 430 431 map.put( "hash", hash ); 432 map.put( "sig", sig ); 433 map.put( "sig_data_size", new Long( sig_data_size )); 434 435 // local data 436 437 if ( private_key != null ){ 438 439 map.put( "private_key", private_key ); 440 } 441 442 map.put( "add_type", new Long( add_type )); 443 map.put( "add_time", new Long( add_time )); 444 445 map.put( "subscribed", new Long( is_subscribed?1:0 )); 446 447 map.put( "pop", new Long( popularity )); 448 449 map.put( "rand", new Long( fixed_random )); 450 451 map.put( "hupv", new Long( highest_prompted_version )); 452 453 map.put( "sp", new Long( server_published?1:0 )); 454 map.put( "spo", new Long( server_publication_outstanding?1:0 )); 455 456 if ( associations.size() > 0 ){ 457 458 List l_assoc = new ArrayList(); 459 460 map.put( "assoc", l_assoc ); 461 462 for (int i=0;i<associations.size();i++){ 463 464 association assoc = (association)associations.get(i); 465 466 Map m = new HashMap(); 467 468 l_assoc.add( m ); 469 470 m.put( "h", assoc.getHash()); 471 m.put( "w", new Long( assoc.getWhen())); 472 } 473 } 474 475 map.put( "history", history_map ); 476 477 if ( creator_ref != null ){ 478 479 map.put( "cref", creator_ref.getBytes( "UTF-8" )); 480 } 481 482 if ( category != null ){ 483 484 map.put( "cat", category.getBytes( "UTF-8" )); 485 } 486 487 if ( tag_id != -1 ){ 488 489 map.put( "tag", tag_id ); 490 } 491 492 if ( parent != null ){ 493 494 map.put( "par", parent.getBytes( "UTF-8" )); 495 } 496 497 return( map ); 498 } 499 } 500 501 protected void fromMap( Map map )502 fromMap( 503 Map map ) 504 505 throws IOException 506 { 507 name = new String((byte[])map.get( "name"), "UTF-8" ); 508 public_key = (byte[])map.get( "public_key" ); 509 private_key = (byte[])map.get( "private_key" ); 510 version = ((Long)map.get( "version" )).intValue(); 511 az_version = (int)ImportExportUtils.importLong( map, "az_version", AZ_VERSION ); 512 is_public = ((Long)map.get( "is_public")).intValue() == 1; 513 Long anon = (Long)map.get( "is_anonymous" ); 514 is_anonymous = anon!=null&&anon==1; 515 singleton_details = (Map)map.get( "sin_details" ); 516 517 hash = (byte[])map.get( "hash" ); 518 sig = (byte[])map.get( "sig" ); 519 sig_data_size = ((Long)map.get( "sig_data_size" )).intValue(); 520 521 fixed_random = ((Long)map.get( "rand" )).intValue(); 522 523 add_type = ((Long)map.get( "add_type" )).intValue(); 524 add_time = ((Long)map.get( "add_time" )).longValue(); 525 526 is_subscribed = ((Long)map.get( "subscribed" )).intValue()==1; 527 528 popularity = ((Long)map.get( "pop" )).longValue(); 529 530 highest_prompted_version = ((Long)map.get( "hupv" )).intValue(); 531 532 server_published = ((Long)map.get( "sp" )).intValue()==1; 533 server_publication_outstanding = ((Long)map.get( "spo" )).intValue()==1; 534 535 Long l_spa = (Long)map.get( "spa" ); 536 537 if ( l_spa != null ){ 538 singleton_sp_attempted = l_spa.longValue()==1; 539 } 540 541 byte[] b_local_name = (byte[])map.get( "local_name" ); 542 543 if ( b_local_name != null ){ 544 545 local_name = new String( b_local_name, "UTF-8" ); 546 } 547 548 List l_assoc = (List)map.get( "assoc" ); 549 550 if ( l_assoc != null ){ 551 552 for (int i=0;i<l_assoc.size();i++){ 553 554 Map m = (Map)l_assoc.get(i); 555 556 byte[] hash = (byte[])m.get("h"); 557 long when = ((Long)m.get( "w" )).longValue(); 558 559 associations.add( new association( hash, when )); 560 } 561 } 562 563 history_map = (Map)map.get( "history" ); 564 565 if ( history_map == null ){ 566 567 history_map = new HashMap(); 568 } 569 570 byte[] b_cref = (byte[])map.get( "cref" ); 571 572 if ( b_cref != null ){ 573 574 creator_ref = new String( b_cref, "UTF-8" ); 575 } 576 577 byte[] b_cat = (byte[])map.get( "cat" ); 578 579 if ( b_cat != null ){ 580 581 category = new String( b_cat, "UTF-8" ); 582 } 583 584 Long l_tag_id = (Long)map.get( "tag" ); 585 586 if ( l_tag_id != null ){ 587 588 tag_id = l_tag_id; 589 } 590 591 byte[] b_parent = (byte[])map.get( "par" ); 592 593 if ( b_parent != null ){ 594 595 parent = new String( b_parent, "UTF-8" ); 596 } 597 } 598 599 protected Map getScheduleConfig()600 getScheduleConfig() 601 { 602 if ( schedule_map == null ){ 603 604 try{ 605 Map map = JSONUtils.decodeJSON( getJSON()); 606 607 schedule_map = (Map)map.get( "schedule" ); 608 609 if ( schedule_map == null ){ 610 611 schedule_map = new HashMap(); 612 } 613 }catch( Throwable e ){ 614 615 log( "Failed to load schedule", e ); 616 617 schedule_map = new HashMap(); 618 } 619 } 620 621 return( schedule_map ); 622 } 623 624 protected Map getHistoryConfig()625 getHistoryConfig() 626 { 627 return( history_map ); 628 } 629 630 protected void updateHistoryConfig( Map _history_map )631 updateHistoryConfig( 632 Map _history_map ) 633 { 634 history_map = _history_map; 635 636 fireChanged(); 637 } 638 639 protected void upgrade( SubscriptionBodyImpl body )640 upgrade( 641 SubscriptionBodyImpl body ) 642 643 throws SubscriptionException 644 { 645 // pick up details from the body (excluding json that is maintained in body only) 646 647 syncFromBody( body ); 648 649 // write to file 650 651 syncToBody(body); 652 653 fireChanged(); 654 } 655 656 protected void init()657 init() 658 { 659 short_id = SubscriptionBodyImpl.deriveShortID( public_key, singleton_details ); 660 id = null; 661 } 662 663 public boolean isSingleton()664 isSingleton() 665 { 666 return( singleton_details != null ); 667 } 668 669 public boolean isShareable()670 isShareable() 671 { 672 try{ 673 return( getEngine().isShareable() && !isSingleton()); 674 675 }catch( Throwable e ){ 676 677 Debug.printStackTrace(e); 678 679 return( false ); 680 } 681 } 682 683 public boolean isSearchTemplate()684 isSearchTemplate() 685 { 686 return( getName(false).startsWith( "Search Template:" )); 687 } 688 689 protected Map getSingletonDetails()690 getSingletonDetails() 691 { 692 return( singleton_details ); 693 } 694 695 protected boolean getSingletonPublishAttempted()696 getSingletonPublishAttempted() 697 { 698 return( singleton_sp_attempted ); 699 } 700 701 protected void setSingletonPublishAttempted()702 setSingletonPublishAttempted() 703 { 704 if ( !singleton_sp_attempted ){ 705 706 singleton_sp_attempted = true; 707 708 manager.configDirty( this ); 709 } 710 } 711 712 public String getName()713 getName() 714 { 715 return( getName( true )); 716 } 717 718 public String getName( boolean use_local )719 getName( 720 boolean use_local ) 721 { 722 return( local_name==null?name:local_name ); 723 } 724 725 public String getURI()726 getURI() 727 { 728 String str = "sub:?name=" + UrlUtils.encode(getName()) + "&id=" + Base32.encode(getShortID()) + "&v=" + getVersion(); 729 730 return( "azplug:?id=subscription&arg=" + UrlUtils.encode( str )); 731 } 732 733 public void requestAttention()734 requestAttention() 735 { 736 manager.selectSubscription( this ); 737 } 738 739 public void setLocalName( String str )740 setLocalName( 741 String str ) 742 { 743 local_name = str; 744 745 manager.configDirty( this ); 746 747 fireChanged(); 748 } 749 750 public void setName( String _name )751 setName( 752 String _name ) 753 754 throws SubscriptionException 755 { 756 if ( !name.equals( _name )){ 757 758 boolean ok = false; 759 760 String old_name = name; 761 int old_version = version; 762 763 try{ 764 name = _name; 765 766 version++; 767 768 SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this ); 769 770 syncToBody( body ); 771 772 versionUpdated( body, false ); 773 774 ok = true; 775 776 }finally{ 777 778 if ( !ok ){ 779 780 name = old_name; 781 version = old_version; 782 } 783 } 784 785 fireChanged(); 786 } 787 } 788 789 public String getNameEx()790 getNameEx() 791 { 792 if ( name_ex == null ){ 793 794 try{ 795 Map map = JSONUtils.decodeJSON( getJSON()); 796 797 String search_term = (String)map.get( "search_term" ); 798 Map filters = (Map)map.get( "filters" ); 799 800 Engine engine = manager.getEngine( this, map, true ); 801 802 String engine_name = engine.getNameEx(); 803 804 if ( name.startsWith( engine_name )){ 805 806 name_ex = name; 807 808 }else if ( engine_name.startsWith( name )){ 809 810 name_ex = engine_name; 811 812 }else{ 813 814 name_ex = name + ": " + engine.getNameEx(); 815 } 816 817 if ( search_term != null && search_term.length() > 0 ){ 818 819 name_ex += ", query=" + search_term; 820 } 821 822 if ( filters != null && filters.size() > 0 ){ 823 824 name_ex += ", filters=" + new SubscriptionResultFilter(filters).getString(); 825 } 826 827 }catch( Throwable e ){ 828 829 name_ex = name + ": " + Debug.getNestedExceptionMessage(e); 830 } 831 } 832 833 return( name_ex ); 834 } 835 836 public String getQueryKey()837 getQueryKey() 838 { 839 try{ 840 Map map = JSONUtils.decodeJSON( getJSON()); 841 842 String search_term = (String)map.get( "search_term" ); 843 Map filters = (Map)map.get( "filters" ); 844 845 Engine engine = manager.getEngine( this, map, true ); 846 847 String name = engine.getNameEx(); 848 849 if ( search_term != null && search_term.length() > 0 ){ 850 851 name += ", query=" + search_term; 852 } 853 854 if ( filters != null && filters.size() > 0 ){ 855 856 name += ", filters=" + new SubscriptionResultFilter(filters).getString(); 857 } 858 859 return( name ); 860 861 }catch( Throwable e ){ 862 863 return( null ); 864 } 865 } 866 867 public long getAddTime()868 getAddTime() 869 { 870 return( add_time ); 871 } 872 873 public int getAddType()874 getAddType() 875 { 876 return( add_type ); 877 } 878 879 public boolean isPublic()880 isPublic() 881 { 882 return( is_public ); 883 } 884 885 public boolean isAnonymous()886 isAnonymous() 887 { 888 return( is_anonymous ); 889 } 890 891 public void setPublic( boolean _is_public )892 setPublic( 893 boolean _is_public ) 894 895 throws SubscriptionException 896 { 897 if ( is_public != _is_public ){ 898 899 boolean ok = false; 900 901 boolean old_public = is_public; 902 int old_version = version; 903 904 try{ 905 is_public = _is_public; 906 907 version++; 908 909 SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this ); 910 911 syncToBody( body ); 912 913 versionUpdated( body, false ); 914 915 ok = true; 916 917 }finally{ 918 919 if ( !ok ){ 920 921 version = old_version; 922 is_public = old_public; 923 } 924 } 925 926 fireChanged(); 927 } 928 } 929 930 protected boolean getServerPublicationOutstanding()931 getServerPublicationOutstanding() 932 { 933 return( server_publication_outstanding ); 934 } 935 936 protected void setServerPublicationOutstanding()937 setServerPublicationOutstanding() 938 { 939 if ( !server_publication_outstanding ){ 940 941 server_publication_outstanding = true; 942 943 fireChanged(); 944 } 945 } 946 947 protected void setServerPublished()948 setServerPublished() 949 { 950 if ( server_publication_outstanding || !server_published ){ 951 952 server_published = true; 953 server_publication_outstanding = false; 954 955 fireChanged(); 956 } 957 } 958 959 protected boolean getServerPublished()960 getServerPublished() 961 { 962 return( server_published ); 963 } 964 965 public String getJSON()966 getJSON() 967 968 throws SubscriptionException 969 { 970 try{ 971 SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this ); 972 973 return( body.getJSON()); 974 975 }catch( Throwable e ){ 976 977 history.setFatalError( Debug.getNestedExceptionMessage(e)); 978 979 if ( e instanceof SubscriptionException ){ 980 981 throw((SubscriptionException)e ); 982 } 983 984 throw( new SubscriptionException( "Failed to read subscription", e )); 985 } 986 } 987 988 public boolean setJSON( String _json )989 setJSON( 990 String _json ) 991 992 throws SubscriptionException 993 { 994 String json = embedEngines( _json ); 995 996 SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this ); 997 998 String old_json = body.getJSON(); 999 1000 if ( !json.equals( old_json )){ 1001 1002 boolean ok = false; 1003 1004 int old_version = version; 1005 1006 try{ 1007 version++; 1008 1009 body.setJSON( json ); 1010 1011 syncToBody( body ); 1012 1013 versionUpdated( body, true ); 1014 1015 referer = null; 1016 1017 ok = true; 1018 1019 }finally{ 1020 1021 if ( !ok ){ 1022 1023 version = old_version; 1024 } 1025 } 1026 1027 fireChanged(); 1028 1029 return( true ); 1030 } 1031 1032 return( false ); 1033 } 1034 1035 protected String embedEngines( String json_in )1036 embedEngines( 1037 String json_in ) 1038 { 1039 // see if we need to embed private search templates 1040 1041 Map map = JSONUtils.decodeJSON( json_in ); 1042 1043 long engine_id = ((Long)map.get( "engine_id" )).longValue(); 1044 1045 String json_out = json_in; 1046 1047 if ( engine_id >= Integer.MAX_VALUE || engine_id < 0 ){ 1048 1049 Engine engine = MetaSearchManagerFactory.getSingleton().getMetaSearch().getEngine( engine_id ); 1050 1051 if ( engine == null ){ 1052 1053 log( "Private search template with id '" + engine_id + "' not found!!!!" ); 1054 1055 }else{ 1056 1057 try{ 1058 embedEngines( map, engine ); 1059 1060 json_out = JSONUtils.encodeToJSON( map ); 1061 1062 1063 log( "Embedded private search template '" + engine.getName() + "'" ); 1064 1065 }catch( Throwable e ){ 1066 1067 log( "Failed to embed private search template", e ); 1068 } 1069 } 1070 } 1071 1072 return( json_out ); 1073 } 1074 1075 protected static void embedEngines( Map map, Engine engine )1076 embedEngines( 1077 Map map, 1078 Engine engine ) 1079 { 1080 Map engines = new HashMap(); 1081 1082 map.put( "engines", engines ); 1083 1084 Map engine_map = new HashMap(); 1085 1086 try{ 1087 1088 String engine_str = new String( Base64.encode( BEncoder.encode( engine.exportToBencodedMap())), "UTF-8" ); 1089 1090 engine_map.put( "content", engine_str ); 1091 1092 engines.put( String.valueOf( engine.getId()), engine_map ); 1093 1094 }catch( Throwable e ){ 1095 1096 Debug.out( e ); 1097 } 1098 } 1099 1100 protected Engine extractEngine( Map json_map, long id )1101 extractEngine( 1102 Map json_map, 1103 long id ) 1104 { 1105 Map engines = (Map)json_map.get( "engines" ); 1106 1107 if ( engines != null ){ 1108 1109 Map engine_map = (Map)engines.get( String.valueOf( id )); 1110 1111 if ( engine_map != null ){ 1112 1113 String engine_str = (String)engine_map.get( "content" ); 1114 1115 try{ 1116 1117 Map map = BDecoder.decode( Base64.decode( engine_str.getBytes( "UTF-8" ))); 1118 1119 return( MetaSearchManagerFactory.getSingleton().getMetaSearch().importFromBEncodedMap(map)); 1120 1121 }catch( Throwable e ){ 1122 1123 log( "failed to import engine", e ); 1124 } 1125 } 1126 } 1127 1128 return( null ); 1129 } 1130 1131 public Subscription cloneWithNewEngine( Engine engine )1132 cloneWithNewEngine( 1133 Engine engine ) 1134 1135 throws SubscriptionException 1136 { 1137 try{ 1138 String json = getJSON(); 1139 1140 Map map = JSONUtils.decodeJSON( json ); 1141 1142 long id = ((Long)map.get( "engine_id" )).longValue(); 1143 1144 if ( id == engine.getId()){ 1145 1146 embedEngines(map, engine); 1147 1148 SubscriptionImpl subs = new SubscriptionImpl( manager, getName(), engine.isPublic(), isAnonymous(), null, JSONUtils.encodeToJSON(map), SubscriptionImpl.ADD_TYPE_CREATE ); 1149 1150 subs = manager.addSubscription( subs ); 1151 1152 setLocalName( getName( false ) + " (old)" ); 1153 1154 return( subs ); 1155 1156 }else{ 1157 1158 throw( new SubscriptionException( "Engine mismatch" )); 1159 } 1160 }catch( Throwable e ){ 1161 1162 throw( new SubscriptionException( "Failed to export engine", e )); 1163 } 1164 } 1165 1166 public Engine getEngine()1167 getEngine() 1168 1169 throws SubscriptionException 1170 { 1171 return( getEngine( true )); 1172 } 1173 1174 protected Engine getEngine( boolean local_only )1175 getEngine( 1176 boolean local_only ) 1177 1178 throws SubscriptionException 1179 { 1180 Map map = JSONUtils.decodeJSON( getJSON()); 1181 1182 return( manager.getEngine( this, map, local_only )); 1183 } 1184 1185 protected void engineUpdated( Engine engine )1186 engineUpdated( 1187 Engine engine ) 1188 { 1189 try{ 1190 String json = getJSON(); 1191 1192 Map map = JSONUtils.decodeJSON( json ); 1193 1194 long id = ((Long)map.get( "engine_id" )).longValue(); 1195 1196 if ( id == engine.getId()){ 1197 1198 if ( setJSON( json )){ 1199 1200 log( "Engine has been updated, saved" ); 1201 } 1202 } 1203 }catch( Throwable e ){ 1204 1205 log( "Engine update failed", e ); 1206 } 1207 } 1208 1209 public boolean setDetails( String _name, boolean _is_public, String _json )1210 setDetails( 1211 String _name, 1212 boolean _is_public, 1213 String _json ) 1214 1215 throws SubscriptionException 1216 { 1217 _json = embedEngines( _json ); 1218 1219 SubscriptionBodyImpl body = new SubscriptionBodyImpl( manager, this ); 1220 1221 String old_json = body.getJSON(); 1222 1223 boolean json_changed = !_json.equals( old_json ); 1224 1225 if ( !_name.equals( name ) || 1226 _is_public != is_public || 1227 json_changed ){ 1228 1229 boolean ok = false; 1230 1231 String old_name = name; 1232 boolean old_public = is_public; 1233 int old_version = version; 1234 1235 try{ 1236 is_public = _is_public; 1237 name = _name; 1238 1239 body.setJSON( _json ); 1240 1241 version++; 1242 1243 syncToBody( body ); 1244 1245 versionUpdated( body, json_changed ); 1246 1247 ok = true; 1248 1249 }finally{ 1250 1251 if ( !ok ){ 1252 1253 version = old_version; 1254 is_public = old_public; 1255 name = old_name; 1256 } 1257 } 1258 1259 fireChanged(); 1260 1261 return( true ); 1262 } 1263 1264 return( false ); 1265 } 1266 1267 protected void versionUpdated( SubscriptionBodyImpl body, boolean json_changed )1268 versionUpdated( 1269 SubscriptionBodyImpl body, 1270 boolean json_changed ) 1271 { 1272 if ( json_changed ){ 1273 1274 try{ 1275 Map map = JSONUtils.decodeJSON( body.getJSON()); 1276 1277 schedule_map = (Map)map.get( "schedule" ); 1278 1279 }catch( Throwable e ){ 1280 } 1281 } 1282 1283 name_ex = null; 1284 1285 if ( is_public ){ 1286 1287 manager.updatePublicSubscription( this ); 1288 1289 setPublished( false ); 1290 1291 synchronized( this ){ 1292 1293 for (int i=0;i<associations.size();i++){ 1294 1295 ((association)associations.get(i)).setPublished( false ); 1296 } 1297 } 1298 } 1299 } 1300 1301 public byte[] getPublicKey()1302 getPublicKey() 1303 { 1304 return( public_key ); 1305 } 1306 1307 public byte[] getShortID()1308 getShortID() 1309 { 1310 return( short_id ); 1311 } 1312 1313 public String getID()1314 getID() 1315 { 1316 if (id == null) { 1317 id = Base32.encode(getShortID()); 1318 } 1319 return( id ); 1320 } 1321 1322 protected byte[] getPrivateKey()1323 getPrivateKey() 1324 { 1325 return( private_key ); 1326 } 1327 1328 protected int getFixedRandom()1329 getFixedRandom() 1330 { 1331 return( fixed_random ); 1332 } 1333 1334 public int getVersion()1335 getVersion() 1336 { 1337 return( version ); 1338 } 1339 1340 public int getAZVersion()1341 getAZVersion() 1342 { 1343 return( az_version ); 1344 } 1345 1346 protected void setHighestUserPromptedVersion( int v )1347 setHighestUserPromptedVersion( 1348 int v ) 1349 { 1350 if ( v < version ){ 1351 1352 v = version; 1353 } 1354 1355 if ( highest_prompted_version != v ){ 1356 1357 highest_prompted_version = v; 1358 1359 fireChanged(); 1360 } 1361 } 1362 1363 protected int getHighestUserPromptedVersion()1364 getHighestUserPromptedVersion() 1365 { 1366 return( highest_prompted_version ); 1367 } 1368 1369 public int getHighestVersion()1370 getHighestVersion() 1371 { 1372 return( Math.max( version, highest_prompted_version )); 1373 } 1374 1375 public void resetHighestVersion()1376 resetHighestVersion() 1377 { 1378 if ( highest_prompted_version > 0 ){ 1379 1380 highest_prompted_version = 0; 1381 1382 fireChanged(); 1383 1384 manager.checkUpgrade(this); 1385 } 1386 } 1387 1388 public boolean isMine()1389 isMine() 1390 { 1391 if ( private_key == null ){ 1392 1393 return( false ); 1394 } 1395 1396 if ( isSingleton() && add_type != ADD_TYPE_CREATE ){ 1397 1398 return( false ); 1399 } 1400 1401 return( true ); 1402 } 1403 1404 public boolean isUpdateable()1405 isUpdateable() 1406 { 1407 return( private_key != null ); 1408 } 1409 1410 public boolean isSubscribed()1411 isSubscribed() 1412 { 1413 return( is_subscribed ); 1414 } 1415 1416 public void setSubscribed( boolean s )1417 setSubscribed( 1418 boolean s ) 1419 { 1420 if ( is_subscribed != s ){ 1421 1422 is_subscribed = s; 1423 1424 if ( is_subscribed ){ 1425 1426 manager.setSelected( this ); 1427 1428 }else{ 1429 1430 reset(); 1431 } 1432 1433 fireChanged(); 1434 } 1435 } 1436 1437 public boolean isAutoDownloadSupported()1438 isAutoDownloadSupported() 1439 { 1440 return( history.isAutoDownloadSupported()); 1441 } 1442 1443 public void getPopularity( final SubscriptionPopularityListener listener )1444 getPopularity( 1445 final SubscriptionPopularityListener listener ) 1446 1447 throws SubscriptionException 1448 { 1449 new AEThread2( "subs:popwait", true ) 1450 { 1451 public void 1452 run() 1453 { 1454 try{ 1455 manager.getPopularity( 1456 SubscriptionImpl.this, 1457 new SubscriptionPopularityListener() 1458 { 1459 public void 1460 gotPopularity( 1461 long pop ) 1462 { 1463 if ( pop != popularity ){ 1464 1465 popularity = pop; 1466 1467 fireChanged(); 1468 } 1469 1470 listener.gotPopularity( popularity ); 1471 } 1472 1473 public void 1474 failed( 1475 SubscriptionException e ) 1476 { 1477 if ( popularity == -1 ){ 1478 1479 listener.failed( new SubscriptionException( "Failed to read popularity", e )); 1480 1481 }else{ 1482 1483 listener.gotPopularity( popularity ); 1484 } 1485 } 1486 }); 1487 1488 }catch( Throwable e ){ 1489 1490 if ( popularity == -1 ){ 1491 1492 listener.failed( new SubscriptionException( "Failed to read popularity", e )); 1493 1494 }else{ 1495 1496 listener.gotPopularity( popularity ); 1497 } 1498 } 1499 } 1500 }.start(); 1501 } 1502 1503 public long getCachedPopularity()1504 getCachedPopularity() 1505 { 1506 return( popularity ); 1507 } 1508 1509 protected void setCachedPopularity( long pop )1510 setCachedPopularity( 1511 long pop ) 1512 { 1513 if ( pop != popularity ){ 1514 1515 popularity = pop; 1516 1517 fireChanged(); 1518 } 1519 } 1520 1521 public String getReferer()1522 getReferer() 1523 { 1524 if ( referer == null ){ 1525 1526 try{ 1527 Map map = JSONUtils.decodeJSON( getJSON()); 1528 1529 Engine engine = manager.getEngine( this, map, false ); 1530 1531 if ( engine != null ){ 1532 1533 referer = engine.getReferer(); 1534 } 1535 }catch( Throwable e ){ 1536 1537 log( "Failed to get referer", e ); 1538 } 1539 1540 if ( referer == null ){ 1541 1542 referer = ""; 1543 } 1544 } 1545 1546 return( referer ); 1547 } 1548 1549 protected void checkPublish()1550 checkPublish() 1551 { 1552 synchronized( this ){ 1553 1554 if ( destroyed ){ 1555 1556 return; 1557 } 1558 1559 // singleton's not available for upgrade 1560 1561 if ( isSingleton()){ 1562 1563 return; 1564 } 1565 1566 // nothing to do for unsubscribed ones 1567 1568 if ( !isSubscribed()){ 1569 1570 return; 1571 } 1572 1573 if ( popularity > 100 ){ 1574 1575 // one off test on whether to track so we have around 100 active 1576 1577 if ( lws_skip_check == 2 ){ 1578 1579 return; 1580 1581 }else if ( lws_skip_check == 0 ){ 1582 1583 if ( RandomUtils.nextInt((int)(( popularity + 99 ) / 100 )) == 0 ){ 1584 1585 lws_skip_check = 1; 1586 1587 }else{ 1588 1589 lws_skip_check = 2; 1590 1591 return; 1592 } 1593 } 1594 } 1595 1596 if ( hash != null ){ 1597 1598 boolean create = false; 1599 1600 if ( lws == null ){ 1601 1602 create = true; 1603 1604 }else{ 1605 1606 if ( !Arrays.equals( lws.getHash().getBytes(), hash )){ 1607 1608 lws.remove(); 1609 1610 create = true; 1611 } 1612 } 1613 1614 if ( create ){ 1615 1616 try{ 1617 File original_data_location = manager.getVuzeFile( this ); 1618 1619 if ( original_data_location.exists()){ 1620 1621 // make a version based filename to avoid issues regarding multiple 1622 // versions 1623 1624 final File versioned_data_location = new File( original_data_location.getParent(), original_data_location.getName() + "." + getVersion()); 1625 1626 if ( !versioned_data_location.exists()){ 1627 1628 if ( !FileUtil.copyFile( original_data_location, versioned_data_location )){ 1629 1630 throw( new Exception( "Failed to copy file to '" + versioned_data_location + "'" )); 1631 } 1632 } 1633 1634 lws = LightWeightSeedManager.getSingleton().add( 1635 getName(), 1636 new HashWrapper( hash ), 1637 TorrentUtils.getDecentralisedEmptyURL(), 1638 versioned_data_location, 1639 isAnonymous()?AENetworkClassifier.AT_I2P:AENetworkClassifier.AT_PUBLIC, 1640 new LightWeightSeedAdapter() 1641 { 1642 public TOTorrent 1643 getTorrent( 1644 byte[] hash, 1645 URL announce_url, 1646 File data_location) 1647 1648 throws Exception 1649 { 1650 log( "Generating light-weight torrent: hash=" + ByteFormatter.encodeString( hash )); 1651 1652 TOTorrentCreator creator = 1653 TOTorrentFactory.createFromFileOrDirWithFixedPieceLength( 1654 data_location, 1655 announce_url, 1656 256*1024 ); 1657 1658 TOTorrent t = creator.create(); 1659 1660 t.setHashOverride( hash ); 1661 1662 return( t ); 1663 } 1664 }); 1665 } 1666 1667 }catch( Throwable e ){ 1668 1669 log( "Failed to create light-weight-seed", e ); 1670 } 1671 } 1672 } 1673 } 1674 } 1675 1676 protected synchronized boolean canAutoUpgradeCheck()1677 canAutoUpgradeCheck() 1678 { 1679 if ( isSingleton()){ 1680 1681 return( false ); 1682 } 1683 1684 long now = SystemTime.getMonotonousTime(); 1685 1686 if ( last_auto_upgrade_check == -1 || now - last_auto_upgrade_check > 4*60*60*1000 ){ 1687 1688 last_auto_upgrade_check = now; 1689 1690 return( true ); 1691 } 1692 1693 return( false ); 1694 } 1695 1696 public void addAssociation( byte[] hash )1697 addAssociation( 1698 byte[] hash ) 1699 { 1700 if ( hash.length != 20 ){ 1701 1702 Debug.out( "Invalid hash: " + ByteFormatter.encodeString( hash )); 1703 1704 return; 1705 } 1706 1707 addAssociationSupport( hash, false ); 1708 } 1709 1710 protected boolean addAssociationSupport( byte[] hash, boolean internal )1711 addAssociationSupport( 1712 byte[] hash, 1713 boolean internal ) 1714 { 1715 synchronized( this ){ 1716 1717 for (int i=0;i<associations.size();i++){ 1718 1719 association assoc = (association)associations.get(i); 1720 1721 if ( Arrays.equals( assoc.getHash(), hash )){ 1722 1723 return( false ); 1724 } 1725 } 1726 1727 associations.add( new association( hash, SystemTime.getCurrentTime())); 1728 1729 if ( MAX_ASSOCIATIONS > 0 && associations.size() > MAX_ASSOCIATIONS ){ 1730 1731 associations.remove( RandomUtils.nextInt( MAX_ASSOCIATIONS - MIN_RECENT_ASSOC_TO_RETAIN )); 1732 } 1733 } 1734 1735 if ( !internal ){ 1736 1737 fireChanged(); 1738 1739 manager.associationAdded( this, hash); 1740 } 1741 1742 return( true ); 1743 } 1744 1745 public boolean hasAssociation( byte[] hash )1746 hasAssociation( 1747 byte[] hash ) 1748 { 1749 synchronized( this ){ 1750 1751 for (int i=0;i<associations.size();i++){ 1752 1753 association assoc = (association)associations.get(i); 1754 1755 if ( Arrays.equals( assoc.getHash(), hash )){ 1756 1757 return( true ); 1758 } 1759 } 1760 } 1761 1762 return( false ); 1763 } 1764 1765 public void addPotentialAssociation( String result_id, String key )1766 addPotentialAssociation( 1767 String result_id, 1768 String key ) 1769 { 1770 manager.addPotentialAssociation( this, result_id, key ); 1771 } 1772 1773 public int getAssociationCount()1774 getAssociationCount() 1775 { 1776 synchronized( this ){ 1777 1778 return( associations.size()); 1779 } 1780 } 1781 1782 protected association getAssociationForPublish()1783 getAssociationForPublish() 1784 { 1785 synchronized( this ){ 1786 1787 int num_assoc = associations.size(); 1788 1789 // first set in order of most recent 1790 1791 for (int i=num_assoc-1;i>=Math.max( 0, num_assoc-MIN_RECENT_ASSOC_TO_RETAIN);i--){ 1792 1793 association assoc = (association)associations.get(i); 1794 1795 if ( !assoc.getPublished()){ 1796 1797 assoc.setPublished( true ); 1798 1799 return( assoc ); 1800 } 1801 } 1802 1803 // remaining randomised 1804 1805 int rem = associations.size() - MIN_RECENT_ASSOC_TO_RETAIN; 1806 1807 if ( rem > 0 ){ 1808 1809 List<association> l = new ArrayList<association>( associations.subList( 0, rem )); 1810 1811 Collections.shuffle( l ); 1812 1813 for (int i=0;i<l.size();i++){ 1814 1815 association assoc = l.get(i); 1816 1817 if ( !assoc.getPublished()){ 1818 1819 assoc.setPublished( true ); 1820 1821 return( assoc ); 1822 } 1823 } 1824 } 1825 } 1826 1827 return( null ); 1828 } 1829 1830 protected int getAssociationsRemainingForPublish()1831 getAssociationsRemainingForPublish() 1832 { 1833 synchronized( this ){ 1834 1835 int result = 0; 1836 1837 for ( association a: associations ){ 1838 1839 if ( !a.getPublished()){ 1840 1841 result++; 1842 } 1843 } 1844 1845 return( result ); 1846 } 1847 } 1848 1849 protected boolean getPublished()1850 getPublished() 1851 { 1852 return( published ); 1853 } 1854 1855 protected void setPublished( boolean b )1856 setPublished( 1857 boolean b ) 1858 { 1859 published = b; 1860 } 1861 1862 protected int getVerifiedPublicationVersion( Map details )1863 getVerifiedPublicationVersion( 1864 Map details ) 1865 { 1866 // singleton versions always 1 and each instance has separate private key so 1867 // verification will always fail so save to just return current version 1868 1869 if ( isSingleton()){ 1870 1871 return( getVersion()); 1872 } 1873 1874 if ( !verifyPublicationDetails( details )){ 1875 1876 return( -1 ); 1877 } 1878 1879 return( getPublicationVersion( details )); 1880 } 1881 1882 protected static int getPublicationVersion( Map details )1883 getPublicationVersion( 1884 Map details ) 1885 { 1886 return(((Long)details.get("v")).intValue()); 1887 } 1888 1889 protected byte[] getPublicationHash()1890 getPublicationHash() 1891 { 1892 return( hash ); 1893 } 1894 1895 protected static byte[] getPublicationHash( Map details )1896 getPublicationHash( 1897 Map details ) 1898 { 1899 return((byte[])details.get( "h" )); 1900 } 1901 1902 protected static int getPublicationSize( Map details )1903 getPublicationSize( 1904 Map details ) 1905 { 1906 return(((Long)details.get("z")).intValue()); 1907 } 1908 1909 protected Map getPublicationDetails()1910 getPublicationDetails() 1911 { 1912 Map result = new HashMap(); 1913 1914 result.put( "v", new Long( version )); 1915 1916 if ( singleton_details == null ){ 1917 1918 result.put( "h", hash ); 1919 result.put( "z", new Long( sig_data_size )); 1920 result.put( "s", sig ); 1921 1922 }else{ 1923 1924 result.put( "x", singleton_details ); 1925 } 1926 1927 return( result ); 1928 } 1929 1930 protected boolean verifyPublicationDetails( Map details )1931 verifyPublicationDetails( 1932 Map details ) 1933 { 1934 synchronized( this ){ 1935 1936 if ( BEncoder.mapsAreIdentical( verify_cache_details, details )){ 1937 1938 return( verify_cache_result ); 1939 } 1940 } 1941 1942 byte[] hash = (byte[])details.get( "h" ); 1943 int version = ((Long)details.get( "v" )).intValue(); 1944 int size = ((Long)details.get( "z" )).intValue(); 1945 byte[] sig = (byte[])details.get( "s" ); 1946 1947 boolean result = SubscriptionBodyImpl.verify( public_key, hash, version, size, sig ); 1948 1949 synchronized( this ){ 1950 1951 verify_cache_details = details; 1952 verify_cache_result = result; 1953 } 1954 1955 return( result ); 1956 } 1957 1958 public void setCreatorRef( String ref )1959 setCreatorRef( 1960 String ref ) 1961 { 1962 creator_ref = ref; 1963 1964 fireChanged(); 1965 } 1966 1967 public String getCreatorRef()1968 getCreatorRef() 1969 { 1970 return( creator_ref ); 1971 } 1972 1973 public void setCategory( String _category )1974 setCategory( 1975 String _category ) 1976 { 1977 if ( _category == null && category == null ){ 1978 1979 return; 1980 } 1981 1982 if ( _category != null && category != null && _category.equals( category )){ 1983 1984 return; 1985 } 1986 1987 manager.setCategoryOnExisting( this, category, _category ); 1988 1989 category = _category; 1990 1991 fireChanged(); 1992 } 1993 1994 public String getCategory()1995 getCategory() 1996 { 1997 return( category ); 1998 } 1999 2000 public void setTagID( long _tag_id )2001 setTagID( 2002 long _tag_id ) 2003 { 2004 2005 if ( _tag_id == tag_id ){ 2006 2007 return; 2008 } 2009 2010 // don't update existing download tagging at the moment 2011 //manager.setTagOnExisting( this, tag, _tag ); 2012 2013 tag_id = _tag_id; 2014 2015 fireChanged(); 2016 } 2017 2018 public long getTagID()2019 getTagID() 2020 { 2021 return( tag_id ); 2022 } 2023 2024 public String getParent()2025 getParent() 2026 { 2027 return( parent ); 2028 } 2029 2030 public void setParent( String _parent )2031 setParent( 2032 String _parent ) 2033 { 2034 if ( _parent == null && parent == null ){ 2035 2036 return; 2037 } 2038 2039 if ( _parent != null && parent != null && _parent.equals( parent )){ 2040 2041 return; 2042 } 2043 2044 parent = _parent; 2045 2046 fireChanged(); 2047 } 2048 2049 protected void fireChanged()2050 fireChanged() 2051 { 2052 manager.configDirty( this ); 2053 2054 Iterator it = listeners.iterator(); 2055 2056 while( it.hasNext()){ 2057 2058 try{ 2059 ((SubscriptionListener)it.next()).subscriptionChanged( this ); 2060 2061 }catch( Throwable e ){ 2062 2063 Debug.printStackTrace(e); 2064 } 2065 } 2066 } 2067 2068 protected void fireDownloaded( boolean was_auto )2069 fireDownloaded( 2070 boolean was_auto ) 2071 { 2072 2073 Iterator it = listeners.iterator(); 2074 2075 while( it.hasNext()){ 2076 2077 try{ 2078 ((SubscriptionListener)it.next()).subscriptionDownloaded( this, was_auto ); 2079 2080 }catch( Throwable e ){ 2081 2082 Debug.printStackTrace(e); 2083 } 2084 } 2085 } 2086 2087 public void addListener( SubscriptionListener l )2088 addListener( 2089 SubscriptionListener l ) 2090 { 2091 listeners.add( l ); 2092 } 2093 2094 public void removeListener( SubscriptionListener l )2095 removeListener( 2096 SubscriptionListener l ) 2097 { 2098 listeners.remove( l ); 2099 } 2100 2101 public SubscriptionHistory getHistory()2102 getHistory() 2103 { 2104 return( history ); 2105 } 2106 2107 public SubscriptionManager getManager()2108 getManager() 2109 { 2110 return( manager ); 2111 } 2112 2113 public VuzeFile getVuzeFile()2114 getVuzeFile() 2115 2116 throws SubscriptionException 2117 { 2118 try{ 2119 return( VuzeFileHandler.getSingleton().loadVuzeFile( manager.getVuzeFile( this ).getAbsolutePath())); 2120 2121 }catch( Throwable e ){ 2122 2123 throw( new SubscriptionException( "Failed to get Vuze file", e )); 2124 } 2125 } 2126 2127 public VuzeFile getSearchTemplateVuzeFile()2128 getSearchTemplateVuzeFile() 2129 { 2130 if ( !isSearchTemplate()){ 2131 2132 return( null ); 2133 } 2134 2135 Object[] details = manager.getSearchTemplateVuzeFile( this ); 2136 2137 if ( details != null ){ 2138 2139 return((VuzeFile)details[0]); 2140 } 2141 2142 return( null ); 2143 } 2144 2145 public boolean isSearchTemplateImportable()2146 isSearchTemplateImportable() 2147 { 2148 return( manager.isSearchTemplateImportable( this )); 2149 } 2150 2151 protected void destroy()2152 destroy() 2153 { 2154 LightWeightSeed l; 2155 2156 synchronized( this ){ 2157 2158 destroyed = true; 2159 2160 l = lws; 2161 } 2162 2163 if ( l != null ){ 2164 2165 l.remove(); 2166 } 2167 } 2168 2169 public void reset()2170 reset() 2171 { 2172 getHistory().reset(); 2173 2174 try{ 2175 getEngine().reset(); 2176 2177 }catch( Throwable e ){ 2178 2179 Debug.printStackTrace(e); 2180 } 2181 } 2182 2183 public void remove()2184 remove() 2185 { 2186 destroy(); 2187 2188 manager.removeSubscription( this ); 2189 } 2190 2191 protected boolean isRemoved()2192 isRemoved() 2193 { 2194 synchronized( this ){ 2195 2196 return( destroyed ); 2197 } 2198 } 2199 2200 public SubscriptionResult[] getResults( boolean include_deleted )2201 getResults( 2202 boolean include_deleted ) 2203 { 2204 return( getHistory().getResults( include_deleted )); 2205 } 2206 2207 public void setUserData( Object key, Object data )2208 setUserData( 2209 Object key, 2210 Object data ) 2211 { 2212 synchronized( user_data ){ 2213 2214 if ( data == null ){ 2215 2216 user_data.remove( key ); 2217 2218 }else{ 2219 2220 user_data.put( key, data ); 2221 } 2222 } 2223 } 2224 2225 public Object getUserData( Object key )2226 getUserData( 2227 Object key ) 2228 { 2229 synchronized( user_data ){ 2230 2231 return( user_data.get( key )); 2232 } 2233 } 2234 2235 protected void log( String str )2236 log( 2237 String str ) 2238 { 2239 manager.log( getString() + ": " + str ); 2240 } 2241 2242 protected void log( String str, Throwable e )2243 log( 2244 String str, 2245 Throwable e ) 2246 { 2247 manager.log( getString() + ": " + str, e ); 2248 } 2249 2250 public String getString()2251 getString() 2252 { 2253 return( "name=" + name + 2254 ",sid=" + ByteFormatter.encodeString( short_id ) + 2255 ",ver=" + version + 2256 ",pub=" + is_public + 2257 ",anon=" + is_anonymous + 2258 ",mine=" + isMine() + 2259 ",sub=" + is_subscribed + 2260 (is_subscribed?(",hist={" + history.getString() + "}"):"") + 2261 ",pop=" + popularity + 2262 (server_publication_outstanding?",spo=true":"")); 2263 } 2264 2265 protected void generate( IndentWriter writer )2266 generate( 2267 IndentWriter writer ) 2268 { 2269 String engine_str; 2270 2271 try{ 2272 2273 engine_str = "" + getEngine().getId(); 2274 2275 }catch( Throwable e ){ 2276 2277 engine_str = Debug.getNestedExceptionMessage(e); 2278 } 2279 2280 writer.println( getString() + ": engine=" + engine_str ); 2281 2282 try{ 2283 writer.indent(); 2284 2285 synchronized( this ){ 2286 2287 for (int i=0;i<associations.size();i++){ 2288 2289 ((association)associations.get(i)).generate( writer ); 2290 } 2291 } 2292 }finally{ 2293 2294 writer.exdent(); 2295 } 2296 } 2297 2298 protected static class 2299 association 2300 { 2301 private byte[] hash; 2302 private long when; 2303 private boolean published; 2304 2305 protected association( byte[] _hash, long _when )2306 association( 2307 byte[] _hash, 2308 long _when ) 2309 { 2310 hash = _hash; 2311 when = _when; 2312 } 2313 2314 protected byte[] getHash()2315 getHash() 2316 { 2317 return( hash ); 2318 } 2319 2320 protected long getWhen()2321 getWhen() 2322 { 2323 return( when ); 2324 } 2325 2326 protected boolean getPublished()2327 getPublished() 2328 { 2329 return( published ); 2330 } 2331 2332 protected void setPublished( boolean b )2333 setPublished( 2334 boolean b ) 2335 { 2336 published = b; 2337 } 2338 2339 protected String getString()2340 getString() 2341 { 2342 return( ByteFormatter.encodeString( hash ) + ", pub=" + published ); 2343 } 2344 2345 protected void generate( IndentWriter writer )2346 generate( 2347 IndentWriter writer ) 2348 { 2349 writer.println( getString()); 2350 } 2351 } 2352 } 2353