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