suppress warnings
[jFCPlib.git] / src / net / pterodactylus / fcp / highlevel / HighLevelClient.java
1 /*
2  * fcplib - HighLevelClient.java -
3  * Copyright © 2008 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
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 package net.pterodactylus.fcp.highlevel;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileReader;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.net.InetAddress;
28 import java.net.URL;
29 import java.net.UnknownHostException;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38
39 import net.pterodactylus.fcp.AddPeer;
40 import net.pterodactylus.fcp.AllData;
41 import net.pterodactylus.fcp.ClientGet;
42 import net.pterodactylus.fcp.ClientHello;
43 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
44 import net.pterodactylus.fcp.ConfigData;
45 import net.pterodactylus.fcp.DataFound;
46 import net.pterodactylus.fcp.EndListPeerNotes;
47 import net.pterodactylus.fcp.EndListPeers;
48 import net.pterodactylus.fcp.EndListPersistentRequests;
49 import net.pterodactylus.fcp.FCPPluginReply;
50 import net.pterodactylus.fcp.FcpConnection;
51 import net.pterodactylus.fcp.FcpListener;
52 import net.pterodactylus.fcp.FcpMessage;
53 import net.pterodactylus.fcp.FcpUtils;
54 import net.pterodactylus.fcp.FinishedCompression;
55 import net.pterodactylus.fcp.GenerateSSK;
56 import net.pterodactylus.fcp.GetFailed;
57 import net.pterodactylus.fcp.IdentifierCollision;
58 import net.pterodactylus.fcp.ListPeers;
59 import net.pterodactylus.fcp.ListPersistentRequests;
60 import net.pterodactylus.fcp.NodeData;
61 import net.pterodactylus.fcp.NodeHello;
62 import net.pterodactylus.fcp.NodeRef;
63 import net.pterodactylus.fcp.Peer;
64 import net.pterodactylus.fcp.PeerNote;
65 import net.pterodactylus.fcp.PeerRemoved;
66 import net.pterodactylus.fcp.PersistentGet;
67 import net.pterodactylus.fcp.PersistentPut;
68 import net.pterodactylus.fcp.PersistentPutDir;
69 import net.pterodactylus.fcp.PersistentRequestModified;
70 import net.pterodactylus.fcp.PersistentRequestRemoved;
71 import net.pterodactylus.fcp.PluginInfo;
72 import net.pterodactylus.fcp.ProtocolError;
73 import net.pterodactylus.fcp.PutFailed;
74 import net.pterodactylus.fcp.PutFetchable;
75 import net.pterodactylus.fcp.PutSuccessful;
76 import net.pterodactylus.fcp.ReturnType;
77 import net.pterodactylus.fcp.SSKKeypair;
78 import net.pterodactylus.fcp.SimpleProgress;
79 import net.pterodactylus.fcp.StartedCompression;
80 import net.pterodactylus.fcp.SubscribedUSKUpdate;
81 import net.pterodactylus.fcp.TestDDAComplete;
82 import net.pterodactylus.fcp.TestDDAReply;
83 import net.pterodactylus.fcp.TestDDARequest;
84 import net.pterodactylus.fcp.TestDDAResponse;
85 import net.pterodactylus.fcp.URIGenerated;
86 import net.pterodactylus.fcp.UnknownNodeIdentifier;
87 import net.pterodactylus.fcp.UnknownPeerNoteType;
88 import net.pterodactylus.fcp.WatchGlobal;
89
90 /**
91  * A high-level client that allows simple yet full-featured access to a Freenet
92  * node.
93  * 
94  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
95  * @version $Id$
96  */
97 public class HighLevelClient {
98
99         /** Logger. */
100         private static final Logger logger = Logger.getLogger(HighLevelClient.class.getName());
101
102         /** Object for internal synchronization. */
103         private final Object syncObject = new Object();
104
105         /** The name of the client. */
106         private final String clientName;
107
108         /** The FCP connection to the node. */
109         private FcpConnection fcpConnection = null;
110
111         /** Listeners for high-level client events. */
112         private List<HighLevelClientListener> highLevelClientListeners = Collections.synchronizedList(new ArrayList<HighLevelClientListener>());
113
114         /** The listener for the connection. */
115         private HighLevelClientFcpListener highLevelClientFcpListener = new HighLevelClientFcpListener();
116
117         /** The listeners for progress events. */
118         private List<HighLevelProgressListener> highLevelProgressListeners = Collections.synchronizedList(new ArrayList<HighLevelProgressListener>());
119
120         /** The callback for {@link #connect(String)}. */
121         private HighLevelCallback<ConnectResult> connectCallback;
122
123         /** Mapping from request identifiers to callbacks. */
124         private Map<String, HighLevelCallback<KeyGenerationResult>> keyGenerationCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<KeyGenerationResult>>());
125
126         /** Mapping from request identifier to peer list callbacks. */
127         private Map<String, HighLevelCallback<PeerListResult>> peerListCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<PeerListResult>>());
128
129         /** Mapping from request identifier to peer callbacks. */
130         private Map<String, HighLevelCallback<PeerResult>> peerCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<PeerResult>>());
131
132         /** Mapping from directories to DDA callbacks. */
133         private Map<String, HighLevelCallback<DirectDiskAccessResult>> directDiskAccessCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelCallback<DirectDiskAccessResult>>());
134
135         /** Mapping from request identifiers to download callbacks. */
136         private Map<String, HighLevelProgressCallback<DownloadResult>> downloadCallbacks = Collections.synchronizedMap(new HashMap<String, HighLevelProgressCallback<DownloadResult>>());
137
138         /** The callback for {@link #getRequests()}. */
139         private HighLevelCallback<RequestListResult> requestListCallback;
140
141         /**
142          * Creates a new high-level client that connects to a node on
143          * <code>localhost</code>.
144          * 
145          * @param clientName
146          *            The name of the client
147          */
148         public HighLevelClient(String clientName) {
149                 this.clientName = clientName;
150         }
151
152         //
153         // EVENT MANAGEMENT
154         //
155
156         /**
157          * Adds the given high-level client listener to list of listeners.
158          * 
159          * @param highLevelClientListener
160          *            The listener to add
161          */
162         public void addHighLevelClientListener(HighLevelClientListener highLevelClientListener) {
163                 highLevelClientListeners.add(highLevelClientListener);
164         }
165
166         /**
167          * Removes the given high-level client listener from the list of listeners.
168          * 
169          * @param highLevelClientListener
170          *            The listener to remove
171          */
172         public void removeHighLevelClientListener(HighLevelClientListener highLevelClientListener) {
173                 highLevelClientListeners.remove(highLevelClientListener);
174         }
175
176         /**
177          * Notifies all listeners that a client has connected.
178          */
179         private void fireClientConnected() {
180                 for (HighLevelClientListener highLevelClientListener: highLevelClientListeners) {
181                         highLevelClientListener.clientConnected(this);
182                 }
183         }
184
185         /**
186          * Notifies all listeners that a client has disconnected.
187          * 
188          * @param throwable
189          *            The exception that caused the disconnect, or <code>null</code>
190          *            if there was no exception
191          */
192         private void fireClientDisconnected(Throwable throwable) {
193                 for (HighLevelClientListener highLevelClientListener: highLevelClientListeners) {
194                         highLevelClientListener.clientDisconnected(this, throwable);
195                 }
196         }
197
198         /**
199          * Adds a high-level progress listener.
200          * 
201          * @param highLevelProgressListener
202          *            The high-level progress listener to add
203          */
204         public void addHighLevelProgressListener(HighLevelProgressListener highLevelProgressListener) {
205                 highLevelProgressListeners.add(highLevelProgressListener);
206         }
207
208         /**
209          * Removes a high-level progress listener.
210          * 
211          * @param highLevelProgressListener
212          *            The high-level progress listener to remove
213          */
214         public void removeHighLevelProgressListener(HighLevelProgressListener highLevelProgressListener) {
215                 highLevelProgressListeners.remove(highLevelProgressListener);
216         }
217
218         /**
219          * Notifies all listeners that the request with the given identifier made
220          * some progress.
221          * 
222          * @param identifier
223          *            The identifier of the request
224          * @param highLevelProgress
225          *            The progress of the request
226          */
227         private void fireProgressReceived(String identifier, HighLevelProgress highLevelProgress) {
228                 for (HighLevelProgressListener highLevelProgressListener: highLevelProgressListeners) {
229                         highLevelProgressListener.progressReceived(this, identifier, highLevelProgress);
230                 }
231         }
232
233         //
234         // ACCESSORS
235         //
236
237         /**
238          * Returns the FCP connection that backs this high-level client. This method
239          * should be used with care as fiddling around with the FCP connection can
240          * easily break the high-level client if you don’t know what you’re doing!
241          * 
242          * @return The FCP connection of this client
243          */
244         public FcpConnection getFcpConnection() {
245                 return fcpConnection;
246         }
247
248         /**
249          * Returns whether the node is connected.
250          * 
251          * @return <code>true</code> if the node is currently connected,
252          *         <code>false</code> otherwise
253          */
254         public boolean isConnected() {
255                 return fcpConnection != null;
256         }
257
258         //
259         // ACTIONS
260         //
261
262         /**
263          * Connects the client.
264          * 
265          * @param hostname
266          *            The hostname of the node
267          * @return A callback with a connection result
268          * @throws UnknownHostException
269          *             if the hostname can not be resolved
270          * @throws IOException
271          *             if an I/O error occurs communicating with the node
272          */
273         public HighLevelCallback<ConnectResult> connect(String hostname) throws UnknownHostException, IOException {
274                 return connect(hostname, 9481);
275         }
276
277         /**
278          * Connects the client.
279          * 
280          * @param hostname
281          *            The hostname of the node
282          * @param port
283          *            The port number of the node
284          * @return A callback with a connection result
285          * @throws UnknownHostException
286          *             if the hostname can not be resolved
287          * @throws IOException
288          *             if an I/O error occurs communicating with the node
289          */
290         public HighLevelCallback<ConnectResult> connect(String hostname, int port) throws UnknownHostException, IOException {
291                 return connect(InetAddress.getByName(hostname), port);
292         }
293
294         /**
295          * Connects the client.
296          * 
297          * @param address
298          *            The address of the node
299          * @param port
300          *            The port number of the node
301          * @return A callback with a connection result
302          * @throws IOException
303          *             if an I/O error occurs communicating with the node
304          */
305         public HighLevelCallback<ConnectResult> connect(InetAddress address, int port) throws IOException {
306                 try {
307                         synchronized (this) {
308                                 fcpConnection = new FcpConnection(address, port);
309                         }
310                         fcpConnection.addFcpListener(highLevelClientFcpListener);
311                         fcpConnection.connect();
312                         ClientHello clientHello = new ClientHello(clientName);
313                         connectCallback = new HighLevelCallback<ConnectResult>(new ConnectResult());
314                         fcpConnection.sendMessage(clientHello);
315                         return connectCallback;
316                 } catch (IOException ioe1) {
317                         fcpConnection = null;
318                         throw ioe1;
319                 }
320         }
321
322         /**
323          * Disconnects the client from the node.
324          */
325         public void disconnect() {
326                 disconnect(null);
327         }
328
329         /**
330          * Generates a new SSK keypair.
331          * 
332          * @return A callback with the keypair
333          * @throws IOException
334          *             if an I/O error occurs communicating with the node
335          * @throws HighLevelException
336          *             if the client is not connected
337          */
338         public HighLevelCallback<KeyGenerationResult> generateKey() throws IOException, HighLevelException {
339                 checkConnection();
340                 String identifier = generateIdentifier("generateSSK");
341                 GenerateSSK generateSSK = new GenerateSSK(identifier);
342                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = new HighLevelCallback<KeyGenerationResult>(new KeyGenerationResult(identifier));
343                 keyGenerationCallbacks.put(identifier, keyGenerationCallback);
344                 fcpConnection.sendMessage(generateSSK);
345                 return keyGenerationCallback;
346         }
347
348         /**
349          * Sets whether to watch the global queue.
350          * 
351          * @param enabled
352          *            <code>true</code> to watch the global queue in addition to
353          *            the client-local queue, <code>false</code> to only watch the
354          *            client-local queue
355          * @throws IOException
356          *             if an I/O error occurs communicating with the node
357          * @throws HighLevelException
358          *             if the client is not connected
359          */
360         public void setWatchGlobal(boolean enabled) throws IOException, HighLevelException {
361                 checkConnection();
362                 WatchGlobal watchGlobal = new WatchGlobal(enabled);
363                 fcpConnection.sendMessage(watchGlobal);
364         }
365
366         /**
367          * Gets a list of all peers from the node.
368          * 
369          * @return A callback with the peer list
370          * @throws IOException
371          *             if an I/O error occurs with the node
372          * @throws HighLevelException
373          *             if the client is not connected
374          */
375         public HighLevelCallback<PeerListResult> getPeers() throws IOException, HighLevelException {
376                 checkConnection();
377                 String identifier = generateIdentifier("listPeers");
378                 ListPeers listPeers = new ListPeers(identifier, true, true);
379                 HighLevelCallback<PeerListResult> peerListCallback = new HighLevelCallback<PeerListResult>(new PeerListResult(identifier));
380                 peerListCallbacks.put(identifier, peerListCallback);
381                 fcpConnection.sendMessage(listPeers);
382                 return peerListCallback;
383         }
384
385         /**
386          * Adds the peer whose noderef is stored in the given file.
387          * 
388          * @param nodeRefFile
389          *            The name of the file the peer’s noderef is stored in
390          * @return A peer callback
391          * @throws IOException
392          *             if an I/O error occurs communicating with the node
393          * @throws HighLevelException
394          *             if the client is not connected
395          */
396         public HighLevelCallback<PeerResult> addPeer(String nodeRefFile) throws IOException, HighLevelException {
397                 checkConnection();
398                 String identifier = generateIdentifier("addPeer");
399                 AddPeer addPeer = new AddPeer(nodeRefFile);
400                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
401                 peerCallbacks.put(identifier, peerCallback);
402                 fcpConnection.sendMessage(addPeer);
403                 return peerCallback;
404         }
405
406         /**
407          * Adds the peer whose noderef is stored in the given file.
408          * 
409          * @param nodeRefURL
410          *            The URL where the peer’s noderef is stored
411          * @return A peer callback
412          * @throws IOException
413          *             if an I/O error occurs communicating with the node
414          * @throws HighLevelException
415          *             if the client is not connected
416          */
417         public HighLevelCallback<PeerResult> addPeer(URL nodeRefURL) throws IOException, HighLevelException {
418                 checkConnection();
419                 String identifier = generateIdentifier("addPeer");
420                 AddPeer addPeer = new AddPeer(nodeRefURL);
421                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
422                 peerCallbacks.put(identifier, peerCallback);
423                 fcpConnection.sendMessage(addPeer);
424                 return peerCallback;
425         }
426
427         /**
428          * Adds the peer whose noderef is stored in the given file.
429          * 
430          * @param nodeRef
431          *            The peer’s noderef
432          * @return A peer callback
433          * @throws IOException
434          *             if an I/O error occurs communicating with the node
435          * @throws HighLevelException
436          *             if the client is not connected
437          */
438         public HighLevelCallback<PeerResult> addPeer(NodeRef nodeRef) throws IOException, HighLevelException {
439                 checkConnection();
440                 String identifier = generateIdentifier("addPeer");
441                 AddPeer addPeer = new AddPeer(nodeRef);
442                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
443                 peerCallbacks.put(identifier, peerCallback);
444                 fcpConnection.sendMessage(addPeer);
445                 return peerCallback;
446         }
447
448         /**
449          * Checks whether direct disk access for the given directory is possible.
450          * You have to perform this check before you can upload or download anything
451          * from or the disk directly!
452          * 
453          * @param directory
454          *            The directory to check
455          * @param wantRead
456          *            Whether you want to read the given directory
457          * @param wantWrite
458          *            Whether you want to write to the given directory
459          * @return A direct disk access callback
460          * @throws IOException
461          *             if an I/O error occurs communicating with the node
462          * @throws HighLevelException
463          *             if the client is not connected
464          */
465         public HighLevelCallback<DirectDiskAccessResult> checkDirectDiskAccess(String directory, boolean wantRead, boolean wantWrite) throws IOException, HighLevelException {
466                 checkConnection();
467                 TestDDARequest testDDARequest = new TestDDARequest(directory, wantRead, wantWrite);
468                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = new HighLevelCallback<DirectDiskAccessResult>(new DirectDiskAccessResult(directory));
469                 directDiskAccessCallbacks.put(directory, directDiskAccessCallback);
470                 fcpConnection.sendMessage(testDDARequest);
471                 return directDiskAccessCallback;
472         }
473
474         /**
475          * Starts a download. Files can either be download to disk or streamed from
476          * the node. When downloading to disk you have to perform a direct disk
477          * access check for the directory you want to put the downloaded file in!
478          * 
479          * @see #checkDirectDiskAccess(String, boolean, boolean)
480          * @param uri
481          *            The URI to get
482          * @param filename
483          *            The filename to save the data to, or <code>null</code> to
484          *            retrieve the data as InputStream from the
485          *            {@link DownloadResult}
486          * @param global
487          *            Whether to put the download on the global queue
488          * @return A download result
489          * @throws IOException
490          *             if an I/O error occurs communicating with the node
491          * @throws HighLevelException
492          *             if the client is not connected
493          */
494         public HighLevelProgressCallback<DownloadResult> download(String uri, String filename, boolean global) throws IOException, HighLevelException {
495                 checkConnection();
496                 String identifier = generateIdentifier("download");
497                 ClientGet clientGet = new ClientGet(uri, identifier, (filename == null) ? ReturnType.direct : ReturnType.disk);
498                 clientGet.setGlobal(global);
499                 HighLevelProgressCallback<DownloadResult> downloadCallback = new HighLevelProgressCallback<DownloadResult>(new DownloadResult(identifier));
500                 downloadCallbacks.put(identifier, downloadCallback);
501                 fcpConnection.sendMessage(clientGet);
502                 return downloadCallback;
503         }
504
505         /**
506          * Requests a list of all running requests from the node.
507          * 
508          * @return The request list result
509          * @throws IOException
510          *             if an I/O errors communicating with the node
511          * @throws HighLevelException
512          *             if the client is not connected
513          */
514         public HighLevelCallback<RequestListResult> getRequests() throws IOException, HighLevelException {
515                 checkConnection();
516                 String identifier = generateIdentifier("list-persistent-requests");
517                 ListPersistentRequests listPersistentRequests = new ListPersistentRequests();
518                 synchronized (syncObject) {
519                         if (requestListCallback != null) {
520                                 logger.log(Level.SEVERE, "getRequests() called with request still running!");
521                         }
522                         requestListCallback = new HighLevelCallback<RequestListResult>(new RequestListResult(identifier));
523                 }
524                 fcpConnection.sendMessage(listPersistentRequests);
525                 return requestListCallback;
526         }
527
528         //
529         // PRIVATE METHODS
530         //
531
532         /**
533          * Checks whether the client is already connected and throws an exception if
534          * it is not.
535          * 
536          * @throws NotConnectedException
537          *             if the client is not connected
538          */
539         private void checkConnection() throws NotConnectedException {
540                 synchronized (this) {
541                         if (fcpConnection == null) {
542                                 throw new NotConnectedException("client is not connected");
543                         }
544                 }
545         }
546
547         /**
548          * Generates an identifier for the given function.
549          * 
550          * @param function
551          *            The name of the function
552          * @return An identifier
553          */
554         private String generateIdentifier(String function) {
555                 return "jFCPlib-" + function + "-" + System.currentTimeMillis();
556         }
557
558         /**
559          * Disconnects the client from the node, handing the given Throwable to
560          * {@link #fireClientDisconnected(Throwable)}.
561          * 
562          * @param throwable
563          *            The exception that caused the disconnect, or <code>null</code>
564          *            if there was no exception
565          */
566         private void disconnect(Throwable throwable) {
567                 if (fcpConnection != null) {
568                         fcpConnection.close();
569                 }
570                 fcpConnection = null;
571         }
572
573         /**
574          * FCP listener for {@link HighLevelClient}.
575          * 
576          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
577          * @version $Id$
578          */
579         private class HighLevelClientFcpListener implements FcpListener {
580
581                 /** Mapping from directory to written file (for cleanup). */
582                 private final Map<DirectDiskAccessResult, String> writtenFiles = new HashMap<DirectDiskAccessResult, String>();
583
584                 /**
585                  * Creates a new FCP listener for {@link HighLevelClient}.
586                  */
587                 HighLevelClientFcpListener() {
588                         /* do nothing. */
589                 }
590
591                 //
592                 // PRIVATE METHODS
593                 //
594
595                 /**
596                  * Searches all callback collections for a callback with the given
597                  * identifier and cancels it.
598                  * 
599                  * @param identifier
600                  *            The identifier to search for, or <code>null</code> to
601                  *            cancel all pending requests
602                  */
603                 @SuppressWarnings("synthetic-access")
604                 private void cancelIdentifier(String identifier) {
605                         synchronized (syncObject) {
606                                 if (connectCallback != null) {
607                                         connectCallback.getIntermediaryResult().setFailed(true);
608                                         connectCallback.setDone();
609                                         connectCallback = null;
610                                 }
611                                 if (requestListCallback != null) {
612                                         requestListCallback.getIntermediaryResult().setFailed(true);
613                                         requestListCallback.setDone();
614                                         requestListCallback = null;
615                                 }
616                         }
617                         if (identifier == null) {
618                                 /* key generation callbacks */
619                                 for (Entry<String, HighLevelCallback<KeyGenerationResult>> keyGenerationEntry: keyGenerationCallbacks.entrySet()) {
620                                         keyGenerationEntry.getValue().getIntermediaryResult().setFailed(true);
621                                         keyGenerationEntry.getValue().setDone();
622                                 }
623                                 keyGenerationCallbacks.clear();
624                                 /* peer list callbacks. */
625                                 for (Entry<String, HighLevelCallback<PeerListResult>> peerListEntry: peerListCallbacks.entrySet()) {
626                                         peerListEntry.getValue().getIntermediaryResult().setFailed(true);
627                                         peerListEntry.getValue().setDone();
628                                 }
629                                 peerListCallbacks.clear();
630                                 /* peer callbacks. */
631                                 for (Entry<String, HighLevelCallback<PeerResult>> peerEntry: peerCallbacks.entrySet()) {
632                                         peerEntry.getValue().getIntermediaryResult().setFailed(true);
633                                         peerEntry.getValue().setDone();
634                                 }
635                                 peerCallbacks.clear();
636                                 /* direct disk access callbacks. */
637                                 for (Entry<String, HighLevelCallback<DirectDiskAccessResult>> directDiskAccessEntry: directDiskAccessCallbacks.entrySet()) {
638                                         directDiskAccessEntry.getValue().getIntermediaryResult().setFailed(true);
639                                         directDiskAccessEntry.getValue().setDone();
640                                 }
641                                 directDiskAccessCallbacks.clear();
642                                 /* download callbacks. */
643                                 for (Entry<String, HighLevelProgressCallback<DownloadResult>> downloadEntry: downloadCallbacks.entrySet()) {
644                                         downloadEntry.getValue().getIntermediaryResult().setFailed(true);
645                                         downloadEntry.getValue().setDone();
646                                 }
647                                 downloadCallbacks.clear();
648                         } else {
649                                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(identifier);
650                                 if (keyGenerationCallback != null) {
651                                         keyGenerationCallback.getIntermediaryResult().setFailed(true);
652                                         keyGenerationCallback.setDone();
653                                         return;
654                                 }
655                                 HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
656                                 if (peerListCallback != null) {
657                                         peerListCallback.getIntermediaryResult().setFailed(true);
658                                         peerListCallback.setDone();
659                                         return;
660                                 }
661                                 HighLevelCallback<PeerResult> peerCallback = peerCallbacks.remove(identifier);
662                                 if (peerCallback != null) {
663                                         peerCallback.getIntermediaryResult().setFailed(true);
664                                         peerCallback.setDone();
665                                         return;
666                                 }
667                                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(identifier);
668                                 if (directDiskAccessCallback != null) {
669                                         directDiskAccessCallback.getIntermediaryResult().setFailed(true);
670                                         directDiskAccessCallback.setDone();
671                                         return;
672                                 }
673                                 HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
674                                 if (downloadCallback != null) {
675                                         downloadCallback.getIntermediaryResult().setFailed(true);
676                                         downloadCallback.setDone();
677                                         return;
678                                 }
679                         }
680                 }
681
682                 /**
683                  * Reads the given file and returns the first line of the file.
684                  * 
685                  * @param readFilename
686                  *            The name of the file to read
687                  * @return The content of the file
688                  */
689                 private String readContent(String readFilename) {
690                         FileReader fileReader = null;
691                         BufferedReader bufferedFileReader = null;
692                         try {
693                                 fileReader = new FileReader(readFilename);
694                                 bufferedFileReader = new BufferedReader(fileReader);
695                                 String content = bufferedFileReader.readLine();
696                                 return content;
697                         } catch (IOException ioe1) {
698                                 /* swallow. */
699                         } finally {
700                                 FcpUtils.close(bufferedFileReader);
701                                 FcpUtils.close(fileReader);
702                         }
703                         return null;
704                 }
705
706                 /**
707                  * Writes the given content to the given file.
708                  * 
709                  * @param directDiskAccessResult
710                  *            The DDA result
711                  * @param writeFilename
712                  *            The name of the file to write to
713                  * @param writeContent
714                  *            The content to write to the file
715                  */
716                 private void writeContent(DirectDiskAccessResult directDiskAccessResult, String writeFilename, String writeContent) {
717                         if ((writeFilename == null) || (writeContent == null)) {
718                                 return;
719                         }
720                         writtenFiles.put(directDiskAccessResult, writeFilename);
721                         FileWriter fileWriter = null;
722                         try {
723                                 fileWriter = new FileWriter(writeFilename);
724                                 fileWriter.write(writeContent);
725                         } catch (IOException ioe1) {
726                                 /* swallow. */
727                         } finally {
728                                 FcpUtils.close(fileWriter);
729                         }
730                 }
731
732                 /**
733                  * Cleans up any files that written for the given result.
734                  * 
735                  * @param directDiskAccessResult
736                  *            The direct disk access result
737                  */
738                 @SuppressWarnings("synthetic-access")
739                 private void cleanFiles(DirectDiskAccessResult directDiskAccessResult) {
740                         String writeFilename = writtenFiles.remove(directDiskAccessResult);
741                         if (writeFilename != null) {
742                                 if (!new File(writeFilename).delete()) {
743                                         logger.warning("could not delete " + writeFilename);
744                                 }
745                         }
746                 }
747
748                 //
749                 // INTERFACE FcpListener
750                 //
751
752                 /**
753                  * @see net.pterodactylus.fcp.FcpListener#connectionClosed(net.pterodactylus.fcp.FcpConnection,
754                  *      Throwable)
755                  */
756                 @SuppressWarnings("synthetic-access")
757                 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
758                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
759                                 return;
760                         }
761                         cancelIdentifier(null);
762                         disconnect(throwable);
763                         fireClientDisconnected(throwable);
764                 }
765
766                 /**
767                  * @see net.pterodactylus.fcp.FcpListener#receivedAllData(net.pterodactylus.fcp.FcpConnection,
768                  *      net.pterodactylus.fcp.AllData)
769                  */
770                 public void receivedAllData(FcpConnection fcpConnection, AllData allData) {
771                         /* TODO */
772                 }
773
774                 /**
775                  * @see net.pterodactylus.fcp.FcpListener#receivedCloseConnectionDuplicateClientName(net.pterodactylus.fcp.FcpConnection,
776                  *      net.pterodactylus.fcp.CloseConnectionDuplicateClientName)
777                  */
778                 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
779                         /* TODO */
780                 }
781
782                 /**
783                  * @see net.pterodactylus.fcp.FcpListener#receivedConfigData(net.pterodactylus.fcp.FcpConnection,
784                  *      net.pterodactylus.fcp.ConfigData)
785                  */
786                 public void receivedConfigData(FcpConnection fcpConnection, ConfigData configData) {
787                         /* TODO */
788                 }
789
790                 /**
791                  * @see net.pterodactylus.fcp.FcpListener#receivedDataFound(net.pterodactylus.fcp.FcpConnection,
792                  *      net.pterodactylus.fcp.DataFound)
793                  */
794                 @SuppressWarnings("synthetic-access")
795                 public void receivedDataFound(FcpConnection fcpConnection, DataFound dataFound) {
796                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
797                                 return;
798                         }
799                         String identifier = dataFound.getIdentifier();
800                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.get(identifier);
801                         if (downloadCallback != null) {
802                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
803                                 downloadResult.setFinished(true);
804                                 downloadResult.setFailed(false);
805                                 downloadCallback.progressUpdated();
806                                 downloadCallback.setDone();
807                         }
808                         HighLevelProgress highLevelProgress = new HighLevelProgress(identifier, true);
809                         fireProgressReceived(identifier, highLevelProgress);
810                 }
811
812                 /**
813                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeerNotes(net.pterodactylus.fcp.FcpConnection,
814                  *      net.pterodactylus.fcp.EndListPeerNotes)
815                  */
816                 public void receivedEndListPeerNotes(FcpConnection fcpConnection, EndListPeerNotes endListPeerNotes) {
817                         /* TODO */
818                 }
819
820                 /**
821                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeers(net.pterodactylus.fcp.FcpConnection,
822                  *      net.pterodactylus.fcp.EndListPeers)
823                  */
824                 @SuppressWarnings("synthetic-access")
825                 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
826                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
827                                 return;
828                         }
829                         String identifier = endListPeers.getIdentifier();
830                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
831                         if (peerListCallback == null) {
832                                 return;
833                         }
834                         peerListCallback.setDone();
835                 }
836
837                 /**
838                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPersistentRequests(net.pterodactylus.fcp.FcpConnection,
839                  *      net.pterodactylus.fcp.EndListPersistentRequests)
840                  */
841                 @SuppressWarnings("synthetic-access")
842                 public void receivedEndListPersistentRequests(FcpConnection fcpConnection, EndListPersistentRequests endListPersistentRequests) {
843                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
844                                 return;
845                         }
846                         synchronized (syncObject) {
847                                 if (HighLevelClient.this.requestListCallback == null) {
848                                         logger.log(Level.WARNING, "got EndListPersistentRequests without running request!");
849                                         return;
850                                 }
851                                 requestListCallback.setDone();
852                                 requestListCallback = null;
853                         }
854                 }
855
856                 /**
857                  * @see net.pterodactylus.fcp.FcpListener#receivedFCPPluginReply(net.pterodactylus.fcp.FcpConnection,
858                  *      net.pterodactylus.fcp.FCPPluginReply)
859                  */
860                 public void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) {
861                         /* TODO */
862                 }
863
864                 /**
865                  * @see net.pterodactylus.fcp.FcpListener#receivedGetFailed(net.pterodactylus.fcp.FcpConnection,
866                  *      net.pterodactylus.fcp.GetFailed)
867                  */
868                 @SuppressWarnings("synthetic-access")
869                 public void receivedGetFailed(FcpConnection fcpConnection, GetFailed getFailed) {
870                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
871                                 return;
872                         }
873                         String identifier = getFailed.getIdentifier();
874                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
875                         if (downloadCallback != null) {
876                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
877                                 downloadResult.setFailed(true);
878                                 downloadResult.setFinished(true);
879                                 downloadCallback.setDone();
880                                 return;
881                         }
882                         /* unknown identifier? */
883                         logger.warning("unknown identifier for GetFailed: " + identifier);
884                 }
885
886                 /**
887                  * @see net.pterodactylus.fcp.FcpListener#receivedIdentifierCollision(net.pterodactylus.fcp.FcpConnection,
888                  *      net.pterodactylus.fcp.IdentifierCollision)
889                  */
890                 public void receivedIdentifierCollision(FcpConnection fcpConnection, IdentifierCollision identifierCollision) {
891                         /* TODO */
892                 }
893
894                 /**
895                  * @see net.pterodactylus.fcp.FcpListener#receivedMessage(net.pterodactylus.fcp.FcpConnection,
896                  *      net.pterodactylus.fcp.FcpMessage)
897                  */
898                 public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) {
899                         /* TODO */
900                 }
901
902                 /**
903                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeData(net.pterodactylus.fcp.FcpConnection,
904                  *      net.pterodactylus.fcp.NodeData)
905                  */
906                 public void receivedNodeData(FcpConnection fcpConnection, NodeData nodeData) {
907                         /* TODO */
908                 }
909
910                 /**
911                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeHello(net.pterodactylus.fcp.FcpConnection,
912                  *      net.pterodactylus.fcp.NodeHello)
913                  */
914                 @SuppressWarnings("synthetic-access")
915                 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
916                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
917                                 return;
918                         }
919                         synchronized (syncObject) {
920                                 connectCallback.getIntermediaryResult().setFailed(false);
921                                 connectCallback.setDone();
922                                 connectCallback = null;
923                         }
924                         fireClientConnected();
925                 }
926
927                 /**
928                  * @see net.pterodactylus.fcp.FcpListener#receivedPeer(net.pterodactylus.fcp.FcpConnection,
929                  *      net.pterodactylus.fcp.Peer)
930                  */
931                 @SuppressWarnings("synthetic-access")
932                 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
933                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
934                                 return;
935                         }
936                         String identifier = peer.getIdentifier();
937                         if (identifier == null) {
938                                 return;
939                         }
940                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.get(identifier);
941                         if (peerListCallback != null) {
942                                 peerListCallback.getIntermediaryResult().addPeer(peer);
943                                 return;
944                         }
945                         HighLevelCallback<PeerResult> peerResult = peerCallbacks.remove(identifier);
946                         if (peerResult != null) {
947                                 peerResult.getIntermediaryResult().setPeer(peer);
948                                 peerResult.setDone();
949                                 return;
950                         }
951                         logger.warning("got Peer message with unknown identifier: " + identifier);
952                 }
953
954                 /**
955                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerNote(net.pterodactylus.fcp.FcpConnection,
956                  *      net.pterodactylus.fcp.PeerNote)
957                  */
958                 public void receivedPeerNote(FcpConnection fcpConnection, PeerNote peerNote) {
959                         /* TODO */
960                 }
961
962                 /**
963                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerRemoved(net.pterodactylus.fcp.FcpConnection,
964                  *      net.pterodactylus.fcp.PeerRemoved)
965                  */
966                 public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) {
967                         /* TODO */
968                 }
969
970                 /**
971                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentGet(net.pterodactylus.fcp.FcpConnection,
972                  *      net.pterodactylus.fcp.PersistentGet)
973                  */
974                 @SuppressWarnings("synthetic-access")
975                 public void receivedPersistentGet(FcpConnection fcpConnection, PersistentGet persistentGet) {
976                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
977                                 return;
978                         }
979                         synchronized (syncObject) {
980                                 if (requestListCallback != null) {
981                                         RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
982                                         requestListResult.addRequestResult(new GetRequestResult(persistentGet));
983                                         return;
984                                 }
985                         }
986                         String identifier = persistentGet.getIdentifier();
987                         if (downloadCallbacks.containsKey(identifier)) {
988                                 /* TODO */
989                                 return;
990                         }
991                 }
992
993                 /**
994                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPut(net.pterodactylus.fcp.FcpConnection,
995                  *      net.pterodactylus.fcp.PersistentPut)
996                  */
997                 @SuppressWarnings("synthetic-access")
998                 public void receivedPersistentPut(FcpConnection fcpConnection, PersistentPut persistentPut) {
999                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1000                                 return;
1001                         }
1002                         synchronized (syncObject) {
1003                                 if (requestListCallback != null) {
1004                                         RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
1005                                         requestListResult.addRequestResult(new PutRequestResult(persistentPut));
1006                                         return;
1007                                 }
1008                         }
1009                 }
1010
1011                 /**
1012                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPutDir(net.pterodactylus.fcp.FcpConnection,
1013                  *      net.pterodactylus.fcp.PersistentPutDir)
1014                  */
1015                 @SuppressWarnings("synthetic-access")
1016                 public void receivedPersistentPutDir(FcpConnection fcpConnection, PersistentPutDir persistentPutDir) {
1017                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1018                                 return;
1019                         }
1020                         synchronized (syncObject) {
1021                                 if (requestListCallback != null) {
1022                                         RequestListResult requestListResult = requestListCallback.getIntermediaryResult();
1023                                         requestListResult.addRequestResult(new PutDirRequestResult(persistentPutDir));
1024                                         return;
1025                                 }
1026                         }
1027                 }
1028
1029                 /**
1030                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestModified(net.pterodactylus.fcp.FcpConnection,
1031                  *      net.pterodactylus.fcp.PersistentRequestModified)
1032                  */
1033                 public void receivedPersistentRequestModified(FcpConnection fcpConnection, PersistentRequestModified persistentRequestModified) {
1034                         /* TODO */
1035                 }
1036
1037                 /**
1038                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestRemoved(net.pterodactylus.fcp.FcpConnection,
1039                  *      net.pterodactylus.fcp.PersistentRequestRemoved)
1040                  */
1041                 public void receivedPersistentRequestRemoved(FcpConnection fcpConnection, PersistentRequestRemoved persistentRequestRemoved) {
1042                         /* TODO */
1043                 }
1044
1045                 /**
1046                  * @see net.pterodactylus.fcp.FcpListener#receivedPluginInfo(net.pterodactylus.fcp.FcpConnection,
1047                  *      net.pterodactylus.fcp.PluginInfo)
1048                  */
1049                 public void receivedPluginInfo(FcpConnection fcpConnection, PluginInfo pluginInfo) {
1050                         /* TODO */
1051                 }
1052
1053                 /**
1054                  * @see net.pterodactylus.fcp.FcpListener#receivedProtocolError(net.pterodactylus.fcp.FcpConnection,
1055                  *      net.pterodactylus.fcp.ProtocolError)
1056                  */
1057                 @SuppressWarnings("synthetic-access")
1058                 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
1059                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1060                                 return;
1061                         }
1062                         String identifier = protocolError.getIdentifier();
1063                         if (identifier == null) {
1064                                 return;
1065                         }
1066                         cancelIdentifier(identifier);
1067                 }
1068
1069                 /**
1070                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFailed(net.pterodactylus.fcp.FcpConnection,
1071                  *      net.pterodactylus.fcp.PutFailed)
1072                  */
1073                 @SuppressWarnings("synthetic-access")
1074                 public void receivedPutFailed(FcpConnection fcpConnection, PutFailed putFailed) {
1075                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1076                                 return;
1077                         }
1078                         String identifier = putFailed.getIdentifier();
1079                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.get(identifier);
1080                         if (downloadCallback != null) {
1081                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
1082                                 downloadResult.setFailed(true);
1083                                 downloadResult.setFinished(true);
1084                                 downloadCallback.progressUpdated();
1085                                 downloadCallback.setDone();
1086                         }
1087                         /* TODO - check inserts */
1088                         HighLevelProgress highLevelProgress = new HighLevelProgress(identifier, true);
1089                         fireProgressReceived(identifier, highLevelProgress);
1090                 }
1091
1092                 /**
1093                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFetchable(net.pterodactylus.fcp.FcpConnection,
1094                  *      net.pterodactylus.fcp.PutFetchable)
1095                  */
1096                 @SuppressWarnings("synthetic-access")
1097                 public void receivedPutFetchable(FcpConnection fcpConnection, PutFetchable putFetchable) {
1098                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1099                                 return;
1100                         }
1101                         String identifier = putFetchable.getIdentifier();
1102                         /* TODO - check inserts */
1103                         HighLevelProgress highLevelProgress = new HighLevelProgress(identifier);
1104                         highLevelProgress.setFetchable(true);
1105                         fireProgressReceived(identifier, highLevelProgress);
1106                 }
1107
1108                 /**
1109                  * @see net.pterodactylus.fcp.FcpListener#receivedPutSuccessful(net.pterodactylus.fcp.FcpConnection,
1110                  *      net.pterodactylus.fcp.PutSuccessful)
1111                  */
1112                 @SuppressWarnings("synthetic-access")
1113                 public void receivedPutSuccessful(FcpConnection fcpConnection, PutSuccessful putSuccessful) {
1114                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1115                                 return;
1116                         }
1117                         String identifier = putSuccessful.getIdentifier();
1118                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.get(identifier);
1119                         if (downloadCallback != null) {
1120                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
1121                                 downloadResult.setFinished(true);
1122                                 downloadResult.setFailed(false);
1123                                 downloadCallback.progressUpdated();
1124                         }
1125                         /* TODO - check inserts */
1126                         HighLevelProgress highLevelProgress = new HighLevelProgress(identifier, true);
1127                         fireProgressReceived(identifier, highLevelProgress);
1128                 }
1129
1130                 /**
1131                  * @see net.pterodactylus.fcp.FcpListener#receivedSSKKeypair(net.pterodactylus.fcp.FcpConnection,
1132                  *      net.pterodactylus.fcp.SSKKeypair)
1133                  */
1134                 @SuppressWarnings("synthetic-access")
1135                 public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair) {
1136                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1137                                 return;
1138                         }
1139                         HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(sskKeypair.getIdentifier());
1140                         if (keyGenerationCallback == null) {
1141                                 return;
1142                         }
1143                         KeyGenerationResult keyGenerationResult = keyGenerationCallback.getIntermediaryResult();
1144                         keyGenerationResult.setInsertURI(sskKeypair.getInsertURI());
1145                         keyGenerationResult.setRequestURI(sskKeypair.getRequestURI());
1146                         keyGenerationCallback.setDone();
1147                 }
1148
1149                 /**
1150                  * @see net.pterodactylus.fcp.FcpListener#receivedSimpleProgress(net.pterodactylus.fcp.FcpConnection,
1151                  *      net.pterodactylus.fcp.SimpleProgress)
1152                  */
1153                 @SuppressWarnings("synthetic-access")
1154                 public void receivedSimpleProgress(FcpConnection fcpConnection, SimpleProgress simpleProgress) {
1155                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1156                                 return;
1157                         }
1158                         String identifier = simpleProgress.getIdentifier();
1159                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.get(identifier);
1160                         if (downloadCallback != null) {
1161                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
1162                                 downloadResult.setTotalBlocks(simpleProgress.getTotal());
1163                                 downloadResult.setRequiredBlocks(simpleProgress.getRequired());
1164                                 downloadResult.setSuccessfulBlocks(simpleProgress.getSucceeded());
1165                                 downloadResult.setFailedBlocks(simpleProgress.getFailed());
1166                                 downloadResult.setFatallyFailedBlocks(simpleProgress.getFatallyFailed());
1167                                 downloadResult.setTotalFinalized(simpleProgress.isFinalizedTotal());
1168                                 downloadCallback.progressUpdated();
1169                         }
1170                         /* TODO - check inserts */
1171                         HighLevelProgress highLevelProgress = new HighLevelProgress(identifier, simpleProgress.getTotal(), simpleProgress.getRequired(), simpleProgress.getSucceeded(), simpleProgress.getFailed(), simpleProgress.getFatallyFailed(), simpleProgress.isFinalizedTotal());
1172                         fireProgressReceived(identifier, highLevelProgress);
1173                 }
1174
1175                 /**
1176                  * @see net.pterodactylus.fcp.FcpListener#receivedStartedCompression(net.pterodactylus.fcp.FcpConnection,
1177                  *      net.pterodactylus.fcp.StartedCompression)
1178                  */
1179                 public void receivedStartedCompression(FcpConnection fcpConnection, StartedCompression startedCompression) {
1180                         /* TODO */
1181                 }
1182
1183                 /**
1184                  * @see net.pterodactylus.fcp.FcpListener#receivedSubscribedUSKUpdate(net.pterodactylus.fcp.FcpConnection,
1185                  *      net.pterodactylus.fcp.SubscribedUSKUpdate)
1186                  */
1187                 public void receivedSubscribedUSKUpdate(FcpConnection fcpConnection, SubscribedUSKUpdate subscribedUSKUpdate) {
1188                         /* TODO */
1189                 }
1190
1191                 /**
1192                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAComplete(net.pterodactylus.fcp.FcpConnection,
1193                  *      net.pterodactylus.fcp.TestDDAComplete)
1194                  */
1195                 @SuppressWarnings("synthetic-access")
1196                 public void receivedTestDDAComplete(FcpConnection fcpConnection, TestDDAComplete testDDAComplete) {
1197                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1198                                 return;
1199                         }
1200                         String directory = testDDAComplete.getDirectory();
1201                         if (directory == null) {
1202                                 return;
1203                         }
1204                         HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(directory);
1205                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallback.getIntermediaryResult();
1206                         cleanFiles(directDiskAccessResult);
1207                         directDiskAccessResult.setReadAllowed(testDDAComplete.isReadDirectoryAllowed());
1208                         directDiskAccessResult.setWriteAllowed(testDDAComplete.isWriteDirectoryAllowed());
1209                         directDiskAccessCallback.setDone();
1210                 }
1211
1212                 /**
1213                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAReply(net.pterodactylus.fcp.FcpConnection,
1214                  *      net.pterodactylus.fcp.TestDDAReply)
1215                  */
1216                 @SuppressWarnings("synthetic-access")
1217                 public void receivedTestDDAReply(FcpConnection fcpConnection, TestDDAReply testDDAReply) {
1218                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1219                                 return;
1220                         }
1221                         String directory = testDDAReply.getDirectory();
1222                         if (directory == null) {
1223                                 return;
1224                         }
1225                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallbacks.get(directory).getIntermediaryResult();
1226                         String readFilename = testDDAReply.getReadFilename();
1227                         String readContent = readContent(readFilename);
1228                         String writeFilename = testDDAReply.getWriteFilename();
1229                         String writeContent = testDDAReply.getContentToWrite();
1230                         writeContent(directDiskAccessResult, writeFilename, writeContent);
1231                         TestDDAResponse testDDAResponse = new TestDDAResponse(directory, readContent);
1232                         try {
1233                                 fcpConnection.sendMessage(testDDAResponse);
1234                         } catch (IOException e) {
1235                                 /* swallow. I’m verry unhappy about this. */
1236                         }
1237                 }
1238
1239                 /**
1240                  * @see net.pterodactylus.fcp.FcpListener#receivedURIGenerated(net.pterodactylus.fcp.FcpConnection,
1241                  *      net.pterodactylus.fcp.URIGenerated)
1242                  */
1243                 @SuppressWarnings("synthetic-access")
1244                 public void receivedURIGenerated(FcpConnection fcpConnection, URIGenerated uriGenerated) {
1245                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1246                                 return;
1247                         }
1248                         String identifier = uriGenerated.getIdentifier();
1249                         /* TODO - check inserts */
1250                         HighLevelProgress highLevelProgress = new HighLevelProgress(identifier, uriGenerated.getURI());
1251                         fireProgressReceived(identifier, highLevelProgress);
1252                 }
1253
1254                 /**
1255                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownNodeIdentifier(net.pterodactylus.fcp.FcpConnection,
1256                  *      net.pterodactylus.fcp.UnknownNodeIdentifier)
1257                  */
1258                 public void receivedUnknownNodeIdentifier(FcpConnection fcpConnection, UnknownNodeIdentifier unknownNodeIdentifier) {
1259                         /* TODO */
1260                 }
1261
1262                 /**
1263                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownPeerNoteType(net.pterodactylus.fcp.FcpConnection,
1264                  *      net.pterodactylus.fcp.UnknownPeerNoteType)
1265                  */
1266                 public void receivedUnknownPeerNoteType(FcpConnection fcpConnection, UnknownPeerNoteType unknownPeerNoteType) {
1267                         /* TODO */
1268                 }
1269
1270                 /**
1271                  * @see net.pterodactylus.fcp.FcpListener#receviedFinishedCompression(net.pterodactylus.fcp.FcpConnection,
1272                  *      net.pterodactylus.fcp.FinishedCompression)
1273                  */
1274                 public void receviedFinishedCompression(FcpConnection fcpConnection, FinishedCompression finishedCompression) {
1275                         /* TODO */
1276                 }
1277
1278         }
1279
1280 }