/* * DCClient.java * * Created on 11. November 2005, 22:00 * * To change this template, choose Tools | Options and locate the template under * the Source Creation and Management node. Right-click the template and choose * Open. You can then make changes to the template in the Source Editor. */ package uc; import helpers.Observable; import helpers.Version; import java.io.File; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.ref.WeakReference; import java.security.Security; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.Semaphore; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy; import java.util.concurrent.TimeUnit; import logger.LoggerFactory; import org.apache.log4j.Logger; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import uc.IStoppable.IStartable; import uc.Identity.DefaultIdentity; import uc.InfoChange.IInfoChanged; import uc.crypto.HashValue; import uc.crypto.IHashEngine; import uc.database.HashedFile; import uc.database.IDatabase; import uc.files.IUploadQueue; import uc.files.UploadQueue; import uc.files.downloadqueue.DownloadQueue; import uc.files.filelist.FileList; import uc.files.filelist.IFilelistProcessor; import uc.files.filelist.IOwnFileList; import uc.files.filelist.OwnFileList; import uc.files.search.AutomaticSearchForAlternatives; import uc.files.search.FileSearch; import uc.files.search.ISearchResult; import uc.files.search.SearchResult.ISearchResultListener; import uc.files.transfer.SlotManager; import uc.protocols.ConnectionState; import uc.protocols.hub.Hub; import uc.user.Population; import uc.user.StoredPM; import uc.user.User; /** * TODO when there is a TTHL downloading while the DQE for the tthl is deleted -> DQE without user will be left in DQ * TODO do not readd removed users to a file by an autosearch! * * TODO when there is a download during upload idle statemachine shown in transfers view * * TODO FavHubs listener change on hub connection.. / not just listener needs more * * TODO translation plugin needs to be changed because google translation api got deprecated!!!http://basharkokash.com/post/2010/04/19/Bing-Translator-for-developers.aspx * http://blog.gts-translation.com/2011/05/30/why-larry-page-killed-google-translate-api-and-other-assorted-thoughts/ * TODO PFSR * TODO better upload bookkeeping (Size to full file .. Status -> store past uploads of file) .. * TODO cooler looking transfers view with 3D instead of table.. -> OpenGL * * TODO extensions SEGA + Grouping of file extensions in SCH * PFSR - Partial file sharing http://www.adcportal.com/wiki/PFSR_-_Partial_file_sharing * http://www.adcportal.com/forums/viewtopic.php?f=55&t=696 -> change SF flag on first downloaded blog to allow hub send new BLOM request * * BLOW - Encrypting chat messages with blowfish This is protection in private hubs to encrypt messages from users until a correct password term is given to decrypt so that conversations remains private even if the hub is penetrated by unwanted users The idea is that a client connects to the private hub supplies a pass for the chat thats encrypted unless the pass word requierment is meet the chat remains encrypted forcing the unwanted user not to see any messages posted in the chat. * * https://adc.svn.sourceforge.net/svnroot/adc/trunk/ADC-EXT.txt * * adc://test.flexhub.org:8000 * test zlib * * http://adc.sourceforge.net/versions/ADC-EXT-1.0.6.html#_version_1_0_6 todo SEGA extension * TODO translater could be used for translating messages for sending too.. * * JXTA possibly ... as cooperation tool for ops..? or better via pm.. * JXTA for cooperation of ops between different hubs viable.. not for normal work * * * * TODO just warning the way it is without explanation for bad KP is not good... * TODO fix fontsize issue on MACOsX! * * * TODO think about option to place unfinished downloads next into target folder so no moving of files occur * after finishing the download.. * * TODO (discfree),total uptime .. total download.. * TODO stats counter online time, total up+downloaded, average speed up/down * * * * TODO triggers for external programs / or try grow plugin * * * TODO max upload limit per slot/User * - additional semaphore / value based on usercategory settings? * - uploadlimit max = Min(limiting,slots*normlimit); * * * TODO Option in settings to auto open desired tabs after application start (like download queue, upload queue, finished downloads, finished uploads etc.) * * TODO animated smileys * * TODO CT64 = hidden -> for hiding users * * * TODO upload/download logs to db .. not keeping in ram.. * * TODO identity management (adding identities i.e. a identity includes different FileList / CID / TCP/SSL-Ports / Certificate(KeyPrint) ) * * TODO discovery UI for installing extensions https://bugs.eclipse.org/bugs/show_bug.cgi?id=295273 * http://tasktop.com/blog/eclipse/mylyn-connector-discovery -> wait for 3.7 * * TODO Presentation of magnet -> save as button could be added * * TODO comments on favUsers.. * * * TODO CO-OP plugin cooperative OP work .. tagging user so other ops can read it and setting timecounters * for them having something done.. i.e. distributed remember me function * * TODO http://jid3.blinkenlights.org/ ID3 tag reading for search indexing... possibly there also was some lib for lucene that covered more filetypes?? * * TODO no gui option ..possibly simple commandline steering.. * * TODO implement transfer of uncompressed filelists.. * * * * * TODO potentially Empty interleaves provided bug might show itself when file is already present on disc? * TODO theoretical optimization of segment size by using Golden ration instead of cutting in half! * * TODO ... new special user category for tons of settings.. * may be create not one special user category but rather many categories... make FavUsers one such category.. * i.e. special treatment for PMs ... and and and.. * * * TODO transferred size boxes in statusline could on click change between showing total * vs showing only current session upload.. -> store uploaded/downloaded at end of session * or current upload vs average upload.. * -> or may be rather add possibility for a total transferred box.. * * * * hideshare functionality.. -> TODO better feature would be different filelists * on a per hub basis.. hideshare = no filelist.. * * * TODO (un)checkable actions in popup menu for do after download in DownloadQueue (open filelist,open file) * - possibly as extension point? * * * * TODO DQ, finished downloads, uploaded , uploadQueue , Filelist -> move to file eu.jucy.gui.file plugin * search possibly in its own plugin ... or also in file plugin.. * * * TODO show only text instead of flags... country plugin.. * * TODO upload folder folder where people can upload files (maximum size) * and possibly also delete files (i.e. client behaves like a och legality?) * * TODO may be some statistics window?? drawing what is currently transferred... * * * TODO test maximise on jucy icon under kde 3 doubleclicks or * 3 times clicking on Maximise.. needed according to hali - * -> might be problem with no active workbench window if minimized.. * --> tried and could not be verified.. i.e. works fine with Kubuntu * * * TODO warning when running out of space.. * -> not possible due to Missing api in 1.5 * --> wait for 1.6 as requirement * * TODO LuaJava plugin... -> wait till java 6 mandatory for "kahlua" * otherwise use lua java? probably better -> real lua.. * use with scripting engine... * i.e. * http://cadmium.x9c.fr/downloads.html -> ocaml scripting * Jython -> python * more: https://scripting.dev.java.net/ * * @author Quicksilver */ public final class DCClient { public static final Logger logger = LoggerFactory.make(); private static Initializer init = new Initializer(); public static void setInitializer(Initializer initializer) { synchronized(synchSingleton) { init = initializer; } } private static volatile long debugcount = 0; /* * * @deprecated use scheduled executor */ public static void execute(final Runnable r) { if (++debugcount % 5000 == 0) { logger.debug("Totally submitted tasks " + debugcount); } exec.execute(new Runnable() { public void run() { try { r.run(); } catch (Exception e) { logger.warn("execution of runnable failed.." + e + " isShutdown:" + exec.isShutdown(), e); } } }); } public void executeDir(final Runnable r) { scheduler.execute(new Runnable() { public void run() { try { r.run(); } catch (Exception e) { logger.warn("sexecution of runnable failed.." + e + " isShutdown:" + scheduler.isShutdown(), e); } } }); } /** * timer replacement.. */ private final ScheduledExecutorService scheduler; public static ScheduledExecutorService getScheduler() { return get().scheduler; } public ScheduledExecutorService getSchedulerDir() { return scheduler; } { ScheduledThreadPoolExecutor ste = new ScheduledThreadPoolExecutor(5); ste.setRejectedExecutionHandler(new DiscardPolicy()); scheduler = Executors.unconfigurableScheduledExecutorService(ste); } /** * cached Thread pool executor with CallerRunsPolicy to better find problems.. * at least better than discard policy.. */ private static final ExecutorService exec = new ThreadPoolExecutor(1, 150, 10L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new DiscardPolicy() ); private static DCClient singleton; private static final Object synchSingleton = new Object(); /** * * */ public static DCClient get() { synchronized(synchSingleton) { return singleton; } } private final User fileListSelf; //the self that holds our own FileList private final Object synchAway = new Object(); private boolean away = false; private String awayMessage; private final Observable<String> awayObservable = new Observable<String>(); /** * running hubs.. */ private final Map<FavHub,Hub> hubs = Collections.synchronizedMap(new HashMap<FavHub,Hub>()); private final Map<String,Identity> loadedIdentiies = Collections.synchronizedMap(new HashMap<String,Identity>()); /** * maps an IP-string to an hub .. this is needed so SearchResults can resolve users * ex. 87.35.157.86 * no DNS mapping! */ private final Map<String,WeakReference<Hub>> ipToHub = Collections.synchronizedMap( new HashMap<String,WeakReference<Hub>>()); private final CopyOnWriteArrayList<ISearchResultListener> searchListeners = new CopyOnWriteArrayList<ISearchResultListener>(); private final CopyOnWriteArrayList<IDCCControlListener> hublisteners = new CopyOnWriteArrayList<IDCCControlListener>(); private final CopyOnWriteArrayList<ISearchReceivedListener> srl = new CopyOnWriteArrayList<ISearchReceivedListener>(); private final CopyOnWriteArrayList<IInfoChanged> changedInfo = new CopyOnWriteArrayList<IInfoChanged>(); private final CopyOnWriteArrayList<ILogEventListener> logEventListener= new CopyOnWriteArrayList<ILogEventListener>(); private final Set<InfoChange> changes = Collections.synchronizedSet(new HashSet<InfoChange>()); // /** // * @deprecated -> Identity // */ // private final ConnectionHandler ch; private final IUploadQueue upQueue ; private final IUploadQueue downQueue ; // private final IConnectionDeterminator connectionDeterminator; private final IFavHubs favHubs; private final FavFolders favFolders; private final ISlotManager slotManager; private final Population population; private final StoredPM storedPM; private final DownloadQueue downloadQueue; // /** // * @deprecated -> Identity // */ // private final ICryptoManager cryptoManager; private final DefaultIdentity defaultIdentity; public Identity getDefaultIdentity() { return defaultIdentity; } private final Object infSynch = new Object(); private ScheduledFuture<?> myInfoUpdater = null; /** * current version as it is used in * Description */ public static final String VERSION; /** * longer version string for human readability.. */ public static final String LONGVERSION; public static final String URL = "http://jucy.eu"; static { String v = " V:" + Version.getVersion()+ (Platform.inDevelopmentMode()?"d":""); VERSION = "UC"+v; LONGVERSION = "jucy"+v; Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { logger.warn(e,e); } }); } private final OwnFileList filelist; private final IHashEngine hashEngine; private final IDatabase database; private final List<IFilelistProcessor> filelistProcessors; private final List<IOperatorPlugin> operatorPlugins; private final IUDPHandler udphandler; private final AutomaticSearchForAlternatives altSearch; private final IUserChangedListener databaseUserChanges = new IUserChangedListener() { public void changed(UserChangeEvent uce) { switch(uce.getDetail()) { case UserChangeEvent.DOWNLOADQUEUE_ENTRY_PRE_ADD_FIRST: case UserChangeEvent.DOWNLOADQUEUE_ENTRY_POST_REMOVE_LAST: case UserChangeEvent.FAVUSER_ADDED: case UserChangeEvent.FAVUSER_REMOVED: case UserChangeEvent.SLOT_GRANTED: case UserChangeEvent.SLOTGRANT_REVOKED: case UserChangeEvent.SLOTGRANT_CHANGED: logger.debug("Persistance change for user: "+uce.getChanged()); database.addUpdateOrDeleteUser(uce.getChanged()); } } }; /** * Creates a new instance of DCClient * and all its objects */ public DCClient() { logger.debug("creating client"); favFolders = new FavFolders(); population = new Population(this); storedPM = new StoredPM(population); altSearch = new AutomaticSearchForAlternatives(this); downloadQueue = new DownloadQueue(this); database = init.loadDB(this); defaultIdentity = new DefaultIdentity(this); // pid = loadPID(); // cryptoManager = new CryptoManager(this); hashEngine = init.loadHashEngine(); filelistProcessors = init.loadFilelistProcessors(); operatorPlugins = init.loadOperatorPlugins(); // connectionDeterminator = init.createConnectionDeterminator(this); favHubs = init.getFavHubs(this); slotManager = init.createSlotManager(this); upQueue = init.createUploadQueue(this, true); downQueue = init.createUploadQueue(this, false); fileListSelf = new User(this,PI.get(PI.nick), defaultIdentity.getPID()) { @Override public String getNick() { return PI.get(PI.nick); } @Override public HashValue getCID() { return defaultIdentity.getCID(); } @Override public HashValue getPD() { return defaultIdentity.getPID(); } }; filelist = new OwnFileList(fileListSelf,database,hashEngine,this); // ch = new ConnectionHandler(this); udphandler = init.createUDPHandler(this); PI.get().addPreferenceChangeListener(new IPreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent event) { String pref = event.getKey(); logger.debug("pref changed: "+pref); if (pref.equals(PI.eMail) || pref.equals(PI.description) || pref.equals(PI.connectionNew)|| pref.equals(PI.passive) || pref.equals(PI.uploadLimit) || pref.equals(PI.nick) || pref.startsWith("socksProxy")) { notifyChangedInfo(InfoChange.Misc); } else if(pref.equals(PI.slots) ) { notifyChangedInfo(InfoChange.Slots); } } }); logger.debug("created client"); } private Identity getIdentity(String identityname) { Identity identity = loadedIdentiies.get(identityname); if (identity == null) { return defaultIdentity; } else { return identity; } } /** * sends a search to all provided hubs .. * @param search the search what should be searched.. and where the results should go to.. * @param hubsToSearch which hubs should be searched.. */ public void search(FileSearch search, Set<IHub> hubsToSearch) { if (!hubsToSearch.isEmpty()) { udphandler.addKeyExpected(search.getEncryptionKey()); HashSet<Identity> usedBySearch = new HashSet<Identity>(); for (IHub hub:hubsToSearch) { Identity i = hub.getIdentity(); if (usedBySearch.add(i)){ i.getConnectionDeterminator().searchStarted(search); } hub.search(search); } } } /** * search all hubs.. * @param search the search what should be searched.. and where the results should go to. */ public void search(FileSearch search) { search(search,new HashSet<IHub>(hubs.values())); } /** * * @return a user holding our filelist */ public IUser getFilelistself() { return fileListSelf; } /** * * @return our own filelist.. */ public FileList getOwnFileList() { return filelist.getFileList(); } /** * starts a FileList refresh in a separate Thread */ public void refreshFilelist() { filelist.refresh(false); } /** * clears all HashData from the DownloadQueue * forcing rehashing all Files on the disc */ public void rebuildFilelist() { database.deleteAllHashedFiles(); refreshFilelist(); } /** * deletes all File hashes of the DB * that are currently not in use. * * @return the number of files pruned from the database.. */ public int pruneHashes() { Map<File,HashedFile> pruned = database.pruneUnusedHashedFiles(); if (logger.isDebugEnabled()) { for (File f:pruned.keySet()) { logger.debug("Deleted hash for: "+f.getPath()); } } return pruned.size(); } // /** // * initialises the own FileList, as this takes the most // * time here separate method that can be called from the // * splashScreen is provided.. // */ // synchronized Future<?> initialiseFilelist() { // if (null == fileListInitialised) { // fileListInitialised = start(filelist); // } // return fileListInitialised; // } /** * starts the DCClient * * called after the GUI is ready... * */ public synchronized void start(IProgressMonitor monitor) { if (get() == this) { throw new IllegalStateException("DCClient already started"); } synchronized (synchSingleton) { singleton = this; } monitor.beginTask(String.format(LanguageKeys.StartingJucy, DCClient.LONGVERSION), 10); // favs are loaded.. time to register Database to be notified on userchanges.. population.registerUserChangedListener(databaseUserChanges); monitor.worked(1); Future<?> filelistInit = start(filelist); //initialiseFilelist(); Future<?> identity = start(defaultIdentity); upQueue.start(); downQueue.start(); udphandler.start(); monitor.worked(1); downloadQueue.loadDQ(); monitor.worked(2); try { //wait for deferred tasks... if (PI.getLong(PI.lastFilelistSize) == 0) { filelistInit.get(); } identity.get(); } catch(InterruptedException e) { logger.warn(e,e); } catch (ExecutionException e) { logger.warn(e,e); } monitor.worked(3); storedPM.start(); logEvent(LanguageKeys.StartingHubs); //downloads can be started.. favHubs.openAutoStartHubs(); monitor.worked(3); altSearch.start(); slotManager.init(); monitor.done(); while (true) { //Its a DC Client after all .. wouldn't work without this , kudos to NEV! break; } Security.setProperty("networkaddress.cache.ttl", "60"); //cache positive results only for 1 minute... because of dyndns.. } /** * called to on closing by the GUI....hopefully.. * @param shutDownExec .. if set to false * executor is let alive.. making it possible to create * a new DCClient object.. */ public void stop(boolean shutDownExec) { logEvent(LONGVERSION+" is stopping"); if (PI.getBoolean(PI.deleteFilelistsOnExit)) { FileList.deleteFilelists(); } storedPM.stop(); for (IHub hub:getHubs().values()) { hub.close(); } Future<?> stop = stopAll(hashEngine,downloadQueue,altSearch,filelist,udphandler,defaultIdentity); try { stop.get(); } catch (InterruptedException e) { logger.warn(e, e); } catch (ExecutionException e) { logger.warn(e, e); } if (shutDownExec) { exec.shutdown(); } scheduler.shutdown(); population.unregisterUserChangedListener(databaseUserChanges); logger.debug("shut down executors"); database.shutdown(); logger.debug("shut down database"); logEvent(LONGVERSION+" stopped"); } public void restart() { for (IDCCControlListener c:hublisteners) { c.requireRestart(); } } /** * creates tag * (i.e. something like <UC 0.03,M:A,H:0/0/1,S:2> ) * */ public String getTag(Identity id) { int[] hubs = getNumberOfHubs(false); return String.format("<%s,M:%c,H:%d/%d/%d,S:%d%s>", VERSION , id.getMode().getModeChar(),hubs[0],hubs[1],hubs[2],getTotalSlots(),getAdditionalTag()); } private String getAdditionalTag() { String s = ""; int ul = PI.getInt(PI.uploadLimit); if (ul > 0) { s+=",L:"+ul; } return s; } /** * * @return a 3 dimensional array containing <p> * on index 0 - normal hubs <br> * on index 1 - registered hubs <br> * on index 2 - OP hubs */ public int[] getNumberOfHubs(boolean countChatOnly) { int normal = 0,registered = 0, ophub = 0; for (Hub hub:hubs.values()) { if (hub.getState() == ConnectionState.LOGGEDIN && (countChatOnly || !hub.getFavHub().isChatOnly())) { if (hub.isOpHub()) { ophub++; } else if (hub.isRegistered()) { registered++; } else { normal++; } } } return new int[] {normal,registered,ophub}; } // /** // * @return the connection set by the user // * i.e. 0.02 or 50 // */ // public String getConnection() { // return SizeEnum.toShortSpeedString(PI.getLong(PI.connectionNew)); // } // /** // * check if TLS was enabled at start of Program .. and is now in a usable state. // * @return true if TLS is fully initialized and running.. // * // */ // public boolean currentlyTLSSupport() { // return ch.isTLSRunning() && PI.getBoolean(PI.allowTLS); // } /** * removes a mapping for a hub * from the list of all open hubs.. * !! this is only called by the hub's destroy method.. */ public void internal_unregisterHub(FavHub favHub) { //first remove mapping from hubId to hub Hub hub = hubs.remove(favHub); if (hub != null) { Identity id = hub.getIdentity(); id.removeRunningHub(favHub); //the inform hubs slowly of change.. notifyChangedInfo(InfoChange.Hubs); } } public Hub getHub(FavHub favHub, final boolean showInUI) { Hub hub = hubs.get(favHub); if (hub != null) { return hub; } synchronized (this) { hub = hubs.get(favHub); if (hub == null) { final FavHub ifavHub = favHubs.internal(favHub); Identity id = getIdentity(ifavHub.getIdentityName()); id.addRunningHub(ifavHub); hub = new Hub(ifavHub,id,this); final Hub iHub = hub; hubs.put(ifavHub, hub); final Semaphore creation = new Semaphore(0,false); DCClient.execute(new Runnable() { public void run() { Semaphore sem = new Semaphore(0,false); for (IDCCControlListener hubl:hublisteners) { hubl.hubCreated(ifavHub,showInUI,sem); } creation.release(); sem.acquireUninterruptibly(hublisteners.size()); iHub.start(); notifyChangedInfo(InfoChange.Hubs); } }); creation.acquireUninterruptibly(); } } return hub; } public boolean isRunningHub(FavHub favHub) { return hubs.containsKey(favHub); } /** * @return an unmodifiable view of the currently open hubs.. */ public Map<FavHub,Hub> getHubs() { return Collections.unmodifiableMap(hubs); } /** * registers a ISearchresult listener with the client * * @param sirl - a search result listener .. for example a SearchEditor */ public void register(ISearchResultListener sirl) { searchListeners.addIfAbsent(sirl); } /** * unregisters a ISearchresult listener with the client so he will no * longer receive notification for incoming searches * * @param sirl - a search result listener .. for example a SearchEditor */ public void unregister(ISearchResultListener sirl) { searchListeners.remove(sirl); } public int searchListenersRegistered() { return searchListeners.size(); } public void register(IInfoChanged iic) { changedInfo.addIfAbsent(iic); } public void unregister(IInfoChanged iic) { changedInfo.remove(iic); } /** * this method is called by Hubs * it delegates the searchResults to anyone interested.. * * @param sr - the sr received */ public void srReceived(ISearchResult sr) { for (ISearchResultListener srl: searchListeners) { srl.received(sr); } } // public boolean isActive() { // return !PI.getBoolean(PI.passive) ; // } /** * resolves a user only by his nick ... may result in the wrong user * @param nick - the nick searched * @return the user found null if none */ public User getUserByNick(String nick) { synchronized(hubs){ for (Hub hub : hubs.values()) { if (hub.isNMDC()) { User usr = hub.getUserByNick(nick); if (usr != null) { return usr; } } } } return null; } // public IUser getUserForCID(HashValue cid) { // synchronized(hubs){ // for (Hub hub : hubs.values()) { // if (!hub.isNMDC()) { // User usr = hub.getUserByCID(cid); // if (usr != null) { // return usr; // } // } // } // } // // return null; // } public List<IUser> getUsersForCID(HashValue cid) { List<IUser> users = new ArrayList<IUser>(); synchronized(hubs){ for (Hub hub : hubs.values()) { if (!hub.isNMDC()) { User usr = hub.getUserByCID(cid); if (usr != null) { users.add(usr); } } } } return users; } /** * sends MyInfo to every hub about a changed Info * (message may be delayed based on the type) */ public void notifyChangedInfo(InfoChange type) { if (type.isSeparateRefresh()) { for (IInfoChanged iic: changedInfo) { iic.infoChanged(Collections.singleton(type)); } } else { if (changes.add(type)) { synchronized(infSynch) { if (myInfoUpdater != null && myInfoUpdater.getDelay(TimeUnit.MILLISECONDS) > type.getDelay()) { myInfoUpdater.cancel(false); myInfoUpdater = null; } if (myInfoUpdater == null) { myInfoUpdater = scheduler.schedule(new Runnable() { public void run() { synchronized(infSynch) { myInfoUpdater = null; } for (Hub hub : hubs.values()) { synchronized(hub) { if (hub.getState() == ConnectionState.LOGGEDIN) { hub.sendMyInfo(); } } Set<InfoChange> copy; synchronized(changes) { copy = new HashSet<InfoChange>(changes); changes.clear(); } for (IInfoChanged iic: changedInfo) { iic.infoChanged(copy); } } } }, type.getDelay(), TimeUnit.MILLISECONDS); } } } } } public static interface ILogEventListener { void logEvent(String event); } public void logEvent(String event) { for (ILogEventListener loel:logEventListener) { loel.logEvent(event); } } /** * determines hub by nick of sender and hub-ip * @param nick - nick of SR sender * @param hubip - the hubIP in the SR string * @return the hub that matches the provided values * null if none can be found */ public Hub hubForNickAndIP(String nick, String hubip) { WeakReference<Hub> refHub = ipToHub.get(hubip); if (refHub != null && refHub.get()!= null) { return refHub.get(); } User usr = getUserByNick(nick); if (usr != null) { ipToHub.put(hubip, new WeakReference<Hub>(usr.getHub())); return usr.getHub(); } return null; } public void register(IDCCControlListener hubl) { hublisteners.addIfAbsent(hubl); } public void unregister(IDCCControlListener hubl) { hublisteners.remove(hubl); } public boolean addLogEventListener(ILogEventListener element) { return logEventListener.addIfAbsent(element); } public boolean removeLogEventListener(ILogEventListener element) { return logEventListener.remove(element); } public void registerSRL(ISearchReceivedListener listener) { srl.addIfAbsent(listener); } public void unregisterSRL(ISearchReceivedListener listener) { srl.remove(listener); } /** * (only used for SearchSpy) * * notifies the client that a search was received * @param searchStrings what search strings are searched for.. size will be one if its a tth * @param source - if active this will eb an inetSocketAddress if passive a User */ public void searchReceived(Set<String> searchStrings,Object source, int nrOfResults) { for (ISearchReceivedListener listener:srl) { listener.searchReceived(searchStrings, source, nrOfResults); } } /** * @return the hashEngine */ public IHashEngine getHashEngine() { return hashEngine; } /** * * @return TODO for multiple identities this needs to be changed.. */ public List<ConnectionHandler> getAllConnectionHandler() { return Collections.singletonList(defaultIdentity.getConnectionHandler()); } public IDatabase getDatabase() { return database; } public IOwnFileList getFilelist() { return filelist; } public IUDPHandler getUDPhandler() { return udphandler; } /** * * @return value= total slots - currently in use slots. * so it returns the value usually sent in search messages. */ public int getCurrentSlots() { return slotManager.getCurrentSlots(); } /** * * @return totalSlots - the current maximum of slots */ public int getTotalSlots() { return slotManager.getTotalSlots(); } public ISlotManager getSlotManager() { return slotManager; } public List<IFilelistProcessor> getFilelistProcessors() { return filelistProcessors; } public boolean isAway() { synchronized(synchAway) { return away; } } /** * * @param away - true for away - false for back * away message is default away message if true */ public void setAway(boolean away) { if (away) { setAway(PI.get(PI.defaultAFKMessage)); } else { synchronized(synchAway) { this.away = false; awayObservable.notifyObservers(awayMessage); } } } /** * sets a way to true * @param awayMessage - with provided away message */ public void setAway(String awayMessage) { synchronized(synchAway) { this.awayMessage = awayMessage; this.away = true; awayObservable.notifyObservers(awayMessage); } } public Observable<String> getAwayObservable() { return awayObservable; } public String getAwayMessage() { synchronized(synchAway) { return awayMessage+" <"+LONGVERSION+">"; } } public List<IOperatorPlugin> getOperatorPlugins() { return Collections.unmodifiableList(operatorPlugins); } public IUploadQueue getUpQueue() { return upQueue; } public IUploadQueue getDownQueue() { return downQueue; } public IUploadQueue getUpDownQueue(boolean up) { return up? upQueue: downQueue; } public IFavHubs getFavHubs() { return favHubs; } public FavFolders getFavFolders() { return favFolders; } public Population getPopulation() { return population; } public DownloadQueue getDownloadQueue() { return downloadQueue; } public StoredPM getStoredPM() { return storedPM; } public static class Initializer { protected IFavHubs getFavHubs(DCClient dcc) { return new FavHubs(dcc); } protected IUploadQueue createUploadQueue(DCClient dcc,boolean upload) { return new UploadQueue(dcc); } protected IUDPHandler createUDPHandler(DCClient dcc) { return new UDPhandler(dcc); } /** * loads the database plug-in * */ protected IDatabase loadDB(DCClient dcc) { String idToLoad = PI.get(IDatabase.ExtensionpointID); IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] configElements = reg .getConfigurationElementsFor(IDatabase.ExtensionpointID); IDatabase db = null; for (IConfigurationElement element : configElements) { logger.debug(element.getAttribute("id")); try { if(idToLoad.equals(element.getAttribute("id")) ) { db = (IDatabase) element.createExecutableExtension("class"); db.init(PI.getStoragePath(),dcc); } } catch (CoreException e) { logger.error("Can't load the Database!",e); } catch (Exception e) { throw new Error("Error initializing database: "+e.getMessage(),e); } } if (db == null) { throw new Error("no database found"); } return db; } /** * loads the hash-engine-plug-in */ protected IHashEngine loadHashEngine() { String idToLoad= PI.get(IHashEngine.ExtensionpointID); IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] configElements = reg .getConfigurationElementsFor(IHashEngine.ExtensionpointID); IHashEngine he = null; for (IConfigurationElement element : configElements) { try { if (idToLoad.equals(element.getAttribute("id")) ) { he = (IHashEngine) element.createExecutableExtension("class"); } } catch (CoreException e) { logger.error("Can't load the HashEngine!",e); } } if (he == null ) { throw new Error("no hashEngine found"); } he.init(); return he; } /** * loads all FileList processor plug-ins * @return All plug-ins available.. */ protected List<IFilelistProcessor> loadFilelistProcessors() { IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] configElements = reg .getConfigurationElementsFor(IFilelistProcessor.ExtensionpointID); List<IFilelistProcessor> processor = new ArrayList<IFilelistProcessor>(configElements.length); for (IConfigurationElement element : configElements) { try { IFilelistProcessor fp = (IFilelistProcessor) element.createExecutableExtension("class"); processor.add(fp); } catch (CoreException e) { logger.error("Can't load the FilelistProcessor: "+element.getAttribute("id"),e); } } return processor; } protected List<IOperatorPlugin> loadOperatorPlugins() { IExtensionRegistry reg = Platform.getExtensionRegistry(); IConfigurationElement[] configElements = reg .getConfigurationElementsFor(IOperatorPlugin.PointID); List<IOperatorPlugin> opPlugins = new ArrayList<IOperatorPlugin>(); for (IConfigurationElement element : configElements) { try { IOperatorPlugin fp = (IOperatorPlugin) element.createExecutableExtension("class"); opPlugins.add(fp); } catch (CoreException e) { logger.error("Can't load the Op Plugin: "+element.getAttribute("name")+" "+element.getAttribute("id"),e); } } return opPlugins; } protected ISlotManager createSlotManager(DCClient dcc) { return new SlotManager(dcc); } } public Future<?> start(final IStartable startable) { return scheduler.submit(new Runnable() { public void run() { startable.start(); } }); } public Future<?> stop(final IStoppable startable) { return scheduler.submit(new Runnable() { public void run() { startable.stop(); } }); } private Future<?> stopAll(IStoppable... stoppables) { final List<Future<?>> future = new ArrayList<Future<?>>(); for (IStoppable s:stoppables) { Future<?> f= stop(s); future.add(f); } return scheduler.submit(new Runnable() { public void run() { for (Future<?> f:future) { try { f.get(); } catch (InterruptedException e) { logger.warn(e,e); } catch (ExecutionException e) { logger.warn(e, e); } } } }); } }