5032eef269c4d16cb87324b5e18bdefd917a4b4c
[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.Logger;
37
38 import net.pterodactylus.fcp.AddPeer;
39 import net.pterodactylus.fcp.AllData;
40 import net.pterodactylus.fcp.ClientGet;
41 import net.pterodactylus.fcp.ClientHello;
42 import net.pterodactylus.fcp.CloseConnectionDuplicateClientName;
43 import net.pterodactylus.fcp.ConfigData;
44 import net.pterodactylus.fcp.DataFound;
45 import net.pterodactylus.fcp.EndListPeerNotes;
46 import net.pterodactylus.fcp.EndListPeers;
47 import net.pterodactylus.fcp.EndListPersistentRequests;
48 import net.pterodactylus.fcp.FCPPluginReply;
49 import net.pterodactylus.fcp.FcpConnection;
50 import net.pterodactylus.fcp.FcpListener;
51 import net.pterodactylus.fcp.FcpMessage;
52 import net.pterodactylus.fcp.FcpUtils;
53 import net.pterodactylus.fcp.FinishedCompression;
54 import net.pterodactylus.fcp.GenerateSSK;
55 import net.pterodactylus.fcp.GetFailed;
56 import net.pterodactylus.fcp.IdentifierCollision;
57 import net.pterodactylus.fcp.ListPeers;
58 import net.pterodactylus.fcp.NodeData;
59 import net.pterodactylus.fcp.NodeHello;
60 import net.pterodactylus.fcp.NodeRef;
61 import net.pterodactylus.fcp.Peer;
62 import net.pterodactylus.fcp.PeerNote;
63 import net.pterodactylus.fcp.PeerRemoved;
64 import net.pterodactylus.fcp.PersistentGet;
65 import net.pterodactylus.fcp.PersistentPut;
66 import net.pterodactylus.fcp.PersistentPutDir;
67 import net.pterodactylus.fcp.PersistentRequestModified;
68 import net.pterodactylus.fcp.PersistentRequestRemoved;
69 import net.pterodactylus.fcp.PluginInfo;
70 import net.pterodactylus.fcp.ProtocolError;
71 import net.pterodactylus.fcp.PutFailed;
72 import net.pterodactylus.fcp.PutFetchable;
73 import net.pterodactylus.fcp.PutSuccessful;
74 import net.pterodactylus.fcp.ReturnType;
75 import net.pterodactylus.fcp.SSKKeypair;
76 import net.pterodactylus.fcp.SimpleProgress;
77 import net.pterodactylus.fcp.StartedCompression;
78 import net.pterodactylus.fcp.SubscribedUSKUpdate;
79 import net.pterodactylus.fcp.TestDDAComplete;
80 import net.pterodactylus.fcp.TestDDAReply;
81 import net.pterodactylus.fcp.TestDDARequest;
82 import net.pterodactylus.fcp.TestDDAResponse;
83 import net.pterodactylus.fcp.URIGenerated;
84 import net.pterodactylus.fcp.UnknownNodeIdentifier;
85 import net.pterodactylus.fcp.UnknownPeerNoteType;
86
87 /**
88  * A high-level client that allows simple yet full-featured access to a Freenet
89  * node.
90  *
91  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
92  * @version $Id$
93  */
94 public class HighLevelClient {
95
96         /** Logger. */
97         private static final Logger logger = Logger.getLogger(HighLevelClient.class.getName());
98
99         /** Object for internal synchronization. */
100         private final Object syncObject = new Object();
101
102         /** The name of the client. */
103         private final String clientName;
104
105         /** The address of the node. */
106         private InetAddress address;
107
108         /** The port number of the node. */
109         private int port;
110
111         /** The FCP connection to the node. */
112         private FcpConnection fcpConnection;
113
114         /** Listeners for high-level client events. */
115         private List<HighLevelClientListener> highLevelClientListeners = Collections.synchronizedList(new ArrayList<HighLevelClientListener>());
116
117         /** The listener for the connection. */
118         private HighLevelClientFcpListener highLevelClientFcpListener = new HighLevelClientFcpListener();
119
120         /** The callback for {@link #connect()}. */
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         /**
139          * Creates a new high-level client that connects to a node on
140          * <code>localhost</code>.
141          *
142          * @param clientName
143          *            The name of the client
144          * @throws UnknownHostException
145          *             if the hostname of the node can not be resolved.
146          */
147         public HighLevelClient(String clientName) throws UnknownHostException {
148                 this(clientName, "localhost");
149         }
150
151         /**
152          * Creates a new high-level client that connects to a node on the given
153          * host.
154          *
155          * @param clientName
156          *            The name of the client
157          * @param host
158          *            The hostname of the node
159          * @throws UnknownHostException
160          *             if the hostname of the node can not be resolved.
161          */
162         public HighLevelClient(String clientName, String host) throws UnknownHostException {
163                 this(clientName, host, FcpConnection.DEFAULT_PORT);
164         }
165
166         /**
167          * Creates a new high-level client that connects to a node on the given
168          * host.
169          *
170          * @param clientName
171          *            The name of the client
172          * @param host
173          *            The hostname of the node
174          * @param port
175          *            The port number of the node
176          * @throws UnknownHostException
177          *             if the hostname of the node can not be resolved.
178          */
179         public HighLevelClient(String clientName, String host, int port) throws UnknownHostException {
180                 this(clientName, InetAddress.getByName(host), port);
181         }
182
183         /**
184          * Creates a new high-level client that connects to a node at the given
185          * address.
186          *
187          * @param clientName
188          *            The name of the client
189          * @param address
190          *            The address of the node
191          * @param port
192          *            The port number of the node
193          */
194         public HighLevelClient(String clientName, InetAddress address, int port) {
195                 this.clientName = clientName;
196                 this.address = address;
197                 this.port = port;
198         }
199
200         //
201         // EVENT MANAGEMENT
202         //
203
204         /**
205          * Adds the given high-level client listener to list of listeners.
206          *
207          * @param highLevelClientListener
208          *            The listener to add
209          */
210         public void addHighLevelClientListener(HighLevelClientListener highLevelClientListener) {
211                 highLevelClientListeners.add(highLevelClientListener);
212         }
213
214         /**
215          * Removes the given high-level client listener from the list of listeners.
216          *
217          * @param highLevelClientListener
218          *            The listener to remove
219          */
220         public void removeHighLevelClientListener(HighLevelClientListener highLevelClientListener) {
221                 highLevelClientListeners.remove(highLevelClientListener);
222         }
223
224         /**
225          * Notifies all listeners that a client has connected.
226          */
227         private void fireClientConnected() {
228                 for (HighLevelClientListener highLevelClientListener: highLevelClientListeners) {
229                         highLevelClientListener.clientConnected(this);
230                 }
231         }
232
233         /**
234          * Notifies all listeners that a client has disconnected.
235          *
236          * @param throwable
237          *            The exception that caused the disconnect, or <code>null</code>
238          *            if there was no exception
239          */
240         private void fireClientDisconnected(Throwable throwable) {
241                 for (HighLevelClientListener highLevelClientListener: highLevelClientListeners) {
242                         highLevelClientListener.clientDisconnected(this, throwable);
243                 }
244         }
245
246         //
247         // ACCESSORS
248         //
249
250         /**
251          * Returns the FCP connection that backs this high-level client. This method
252          * should be used with care as fiddling around with the FCP connection can
253          * easily break the high-level client if you don’t know what you’re doing!
254          *
255          * @return The FCP connection of this client
256          */
257         public FcpConnection getFcpConnection() {
258                 return fcpConnection;
259         }
260
261         //
262         // ACTIONS
263         //
264
265         /**
266          * Connects the client.
267          *
268          * @return A callback with a connection result
269          * @throws IOException
270          *             if an I/O error occurs communicating with the node
271          */
272         public HighLevelCallback<ConnectResult> connect() throws IOException {
273                 fcpConnection = new FcpConnection(address, port);
274                 fcpConnection.addFcpListener(highLevelClientFcpListener);
275                 fcpConnection.connect();
276                 ClientHello clientHello = new ClientHello(clientName);
277                 connectCallback = new HighLevelCallback<ConnectResult>(new ConnectResult());
278                 fcpConnection.sendMessage(clientHello);
279                 return connectCallback;
280         }
281
282         /**
283          * Disconnects the client from the node.
284          */
285         public void disconnect() {
286                 disconnect(null);
287         }
288
289         /**
290          * Generates a new SSK keypair.
291          *
292          * @return A callback with the keypair
293          * @throws IOException
294          *             if an I/O error occurs communicating with the node
295          */
296         public HighLevelCallback<KeyGenerationResult> generateKey() throws IOException {
297                 String identifier = generateIdentifier("generateSSK");
298                 GenerateSSK generateSSK = new GenerateSSK(identifier);
299                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = new HighLevelCallback<KeyGenerationResult>(new KeyGenerationResult(identifier));
300                 keyGenerationCallbacks.put(identifier, keyGenerationCallback);
301                 fcpConnection.sendMessage(generateSSK);
302                 return keyGenerationCallback;
303         }
304
305         /**
306          * Gets a list of all peers from the node.
307          *
308          * @return A callback with the peer list
309          * @throws IOException
310          *             if an I/O error occurs with the node
311          */
312         public HighLevelCallback<PeerListResult> getPeers() throws IOException {
313                 String identifier = generateIdentifier("listPeers");
314                 ListPeers listPeers = new ListPeers(identifier, true, true);
315                 HighLevelCallback<PeerListResult> peerListCallback = new HighLevelCallback<PeerListResult>(new PeerListResult(identifier));
316                 peerListCallbacks.put(identifier, peerListCallback);
317                 fcpConnection.sendMessage(listPeers);
318                 return peerListCallback;
319         }
320
321         /**
322          * Adds the peer whose noderef is stored in the given file.
323          *
324          * @param nodeRefFile
325          *            The name of the file the peer’s noderef is stored in
326          * @return A peer callback
327          * @throws IOException
328          *             if an I/O error occurs communicating with the node
329          */
330         public HighLevelCallback<PeerResult> addPeer(String nodeRefFile) throws IOException {
331                 String identifier = generateIdentifier("addPeer");
332                 AddPeer addPeer = new AddPeer(nodeRefFile);
333                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
334                 peerCallbacks.put(identifier, peerCallback);
335                 fcpConnection.sendMessage(addPeer);
336                 return peerCallback;
337         }
338
339         /**
340          * Adds the peer whose noderef is stored in the given file.
341          *
342          * @param nodeRefURL
343          *            The URL where the peer’s noderef is stored
344          * @return A peer callback
345          * @throws IOException
346          *             if an I/O error occurs communicating with the node
347          */
348         public HighLevelCallback<PeerResult> addPeer(URL nodeRefURL) throws IOException {
349                 String identifier = generateIdentifier("addPeer");
350                 AddPeer addPeer = new AddPeer(nodeRefURL);
351                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
352                 peerCallbacks.put(identifier, peerCallback);
353                 fcpConnection.sendMessage(addPeer);
354                 return peerCallback;
355         }
356
357         /**
358          * Adds the peer whose noderef is stored in the given file.
359          *
360          * @param nodeRef
361          *            The peer’s noderef
362          * @return A peer callback
363          * @throws IOException
364          *             if an I/O error occurs communicating with the node
365          */
366         public HighLevelCallback<PeerResult> addPeer(NodeRef nodeRef) throws IOException {
367                 String identifier = generateIdentifier("addPeer");
368                 AddPeer addPeer = new AddPeer(nodeRef);
369                 HighLevelCallback<PeerResult> peerCallback = new HighLevelCallback<PeerResult>(new PeerResult(identifier));
370                 peerCallbacks.put(identifier, peerCallback);
371                 fcpConnection.sendMessage(addPeer);
372                 return peerCallback;
373         }
374
375         /**
376          * Checks whether direct disk access for the given directory is possible.
377          * You have to perform this check before you can upload or download anything
378          * from or the disk directly!
379          *
380          * @param directory
381          *            The directory to check
382          * @param wantRead
383          *            Whether you want to read the given directory
384          * @param wantWrite
385          *            Whether you want to write to the given directory
386          * @return A direct disk access callback
387          * @throws IOException
388          */
389         public HighLevelCallback<DirectDiskAccessResult> checkDirectDiskAccess(String directory, boolean wantRead, boolean wantWrite) throws IOException {
390                 TestDDARequest testDDARequest = new TestDDARequest(directory, wantRead, wantWrite);
391                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = new HighLevelCallback<DirectDiskAccessResult>(new DirectDiskAccessResult(directory));
392                 directDiskAccessCallbacks.put(directory, directDiskAccessCallback);
393                 fcpConnection.sendMessage(testDDARequest);
394                 return directDiskAccessCallback;
395         }
396
397         /**
398          * Starts a download. Files can either be download to disk or streamed from
399          * the node. When downloading to disk you have to perform a direct disk
400          * access check for the directory you want to put the downloaded file in!
401          *
402          * @see #checkDirectDiskAccess(String, boolean, boolean)
403          * @param uri
404          *            The URI to get
405          * @param filename
406          *            The filename to save the data to, or <code>null</code> to
407          *            retrieve the data as InputStream from the
408          *            {@link DownloadResult}
409          * @param global
410          *            Whether to put the download on the global queue
411          * @return A download result
412          * @throws IOException
413          *             if an I/O error occurs communicating with the node
414          */
415         public HighLevelProgressCallback<DownloadResult> download(String uri, String filename, boolean global) throws IOException {
416                 String identifier = generateIdentifier("download");
417                 ClientGet clientGet = new ClientGet(uri, identifier, (filename == null) ? ReturnType.direct : ReturnType.disk);
418                 clientGet.setGlobal(global);
419                 HighLevelProgressCallback<DownloadResult> downloadCallback = new HighLevelProgressCallback<DownloadResult>(new DownloadResult(identifier));
420                 downloadCallbacks.put(identifier, downloadCallback);
421                 fcpConnection.sendMessage(clientGet);
422                 return downloadCallback;
423         }
424
425         //
426         // PRIVATE METHODS
427         //
428
429         /**
430          * Generates an identifier for the given function.
431          *
432          * @param function
433          *            The name of the function
434          * @return An identifier
435          */
436         private String generateIdentifier(String function) {
437                 return "jFCPlib-" + function + "-" + System.currentTimeMillis();
438         }
439
440         /**
441          * Disconnects the client from the node, handing the given Throwable to
442          * {@link #fireClientDisconnected(Throwable)}.
443          *
444          * @param throwable
445          *            The exception that caused the disconnect, or <code>null</code>
446          *            if there was no exception
447          */
448         private void disconnect(Throwable throwable) {
449                 fcpConnection.close();
450                 fireClientDisconnected(throwable);
451         }
452
453         /**
454          * FCP listener for {@link HighLevelClient}.
455          *
456          * @author David ‘Bombe’ Roden &lt;bombe@freenetproject.org&gt;
457          * @version $Id$
458          */
459         private class HighLevelClientFcpListener implements FcpListener {
460
461                 /** Mapping from directory to written file (for cleanup). */
462                 private final Map<DirectDiskAccessResult, String> writtenFiles = new HashMap<DirectDiskAccessResult, String>();
463
464                 /**
465                  * Creates a new FCP listener for {@link HighLevelClient}.
466                  */
467                 HighLevelClientFcpListener() {
468                         /* do nothing. */
469                 }
470
471                 //
472                 // PRIVATE METHODS
473                 //
474
475                 /**
476                  * Searches all callback collections for a callback with the given
477                  * identifier and cancels it.
478                  *
479                  * @param identifier
480                  *            The identifier to search for, or <code>null</code> to
481                  *            cancel all pending requests
482                  */
483                 @SuppressWarnings("synthetic-access")
484                 private void cancelIdentifier(String identifier) {
485                         synchronized (syncObject) {
486                                 if (connectCallback != null) {
487                                         connectCallback.getIntermediaryResult().setFailed(true);
488                                         connectCallback.setDone();
489                                         connectCallback = null;
490                                 }
491                         }
492                         if (identifier == null) {
493                                 /* key generation callbacks */
494                                 for (Entry<String, HighLevelCallback<KeyGenerationResult>> keyGenerationEntry: keyGenerationCallbacks.entrySet()) {
495                                         keyGenerationEntry.getValue().getIntermediaryResult().setFailed(true);
496                                         keyGenerationEntry.getValue().setDone();
497                                 }
498                                 keyGenerationCallbacks.clear();
499                                 /* peer list callbacks. */
500                                 for (Entry<String, HighLevelCallback<PeerListResult>> peerListEntry: peerListCallbacks.entrySet()) {
501                                         peerListEntry.getValue().getIntermediaryResult().setFailed(true);
502                                         peerListEntry.getValue().setDone();
503                                 }
504                                 peerListCallbacks.clear();
505                                 /* peer callbacks. */
506                                 for (Entry<String, HighLevelCallback<PeerResult>> peerEntry: peerCallbacks.entrySet()) {
507                                         peerEntry.getValue().getIntermediaryResult().setFailed(true);
508                                         peerEntry.getValue().setDone();
509                                 }
510                                 peerCallbacks.clear();
511                                 /* direct disk access callbacks. */
512                                 for (Entry<String, HighLevelCallback<DirectDiskAccessResult>> directDiskAccessEntry: directDiskAccessCallbacks.entrySet()) {
513                                         directDiskAccessEntry.getValue().getIntermediaryResult().setFailed(true);
514                                         directDiskAccessEntry.getValue().setDone();
515                                 }
516                                 directDiskAccessCallbacks.clear();
517                                 /* download callbacks. */
518                                 for (Entry<String, HighLevelProgressCallback<DownloadResult>> downloadEntry: downloadCallbacks.entrySet()) {
519                                         downloadEntry.getValue().getIntermediaryResult().setFailed(true);
520                                         downloadEntry.getValue().setDone();
521                                 }
522                                 downloadCallbacks.clear();
523                         } else {
524                                 HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(identifier);
525                                 if (keyGenerationCallback != null) {
526                                         keyGenerationCallback.getIntermediaryResult().setFailed(true);
527                                         keyGenerationCallback.setDone();
528                                         return;
529                                 }
530                                 HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
531                                 if (peerListCallback != null) {
532                                         peerListCallback.getIntermediaryResult().setFailed(true);
533                                         peerListCallback.setDone();
534                                         return;
535                                 }
536                                 HighLevelCallback<PeerResult> peerCallback = peerCallbacks.remove(identifier);
537                                 if (peerCallback != null) {
538                                         peerCallback.getIntermediaryResult().setFailed(true);
539                                         peerCallback.setDone();
540                                         return;
541                                 }
542                                 HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(identifier);
543                                 if (directDiskAccessCallback != null) {
544                                         directDiskAccessCallback.getIntermediaryResult().setFailed(true);
545                                         directDiskAccessCallback.setDone();
546                                         return;
547                                 }
548                                 HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
549                                 if (downloadCallback != null) {
550                                         downloadCallback.getIntermediaryResult().setFailed(true);
551                                         downloadCallback.setDone();
552                                         return;
553                                 }
554                         }
555                 }
556
557                 /**
558                  * Reads the given file and returns the first line of the file.
559                  *
560                  * @param readFilename
561                  *            The name of the file to read
562                  * @return The content of the file
563                  */
564                 private String readContent(String readFilename) {
565                         FileReader fileReader = null;
566                         BufferedReader bufferedFileReader = null;
567                         try {
568                                 fileReader = new FileReader(readFilename);
569                                 bufferedFileReader = new BufferedReader(fileReader);
570                                 String content = bufferedFileReader.readLine();
571                                 return content;
572                         } catch (IOException ioe1) {
573                                 /* swallow. */
574                         } finally {
575                                 FcpUtils.close(bufferedFileReader);
576                                 FcpUtils.close(fileReader);
577                         }
578                         return null;
579                 }
580
581                 /**
582                  * Writes the given content to the given file.
583                  *
584                  * @param directDiskAccessResult
585                  *            The DDA result
586                  * @param writeFilename
587                  *            The name of the file to write to
588                  * @param writeContent
589                  *            The content to write to the file
590                  */
591                 private void writeContent(DirectDiskAccessResult directDiskAccessResult, String writeFilename, String writeContent) {
592                         if ((writeFilename == null) || (writeContent == null)) {
593                                 return;
594                         }
595                         writtenFiles.put(directDiskAccessResult, writeFilename);
596                         FileWriter fileWriter = null;
597                         try {
598                                 fileWriter = new FileWriter(writeFilename);
599                                 fileWriter.write(writeContent);
600                         } catch (IOException ioe1) {
601                                 /* swallow. */
602                         } finally {
603                                 FcpUtils.close(fileWriter);
604                         }
605                 }
606
607                 /**
608                  * Cleans up any files that written for the given result.
609                  *
610                  * @param directDiskAccessResult
611                  *            The direct disk access result
612                  */
613                 @SuppressWarnings("synthetic-access")
614                 private void cleanFiles(DirectDiskAccessResult directDiskAccessResult) {
615                         String writeFilename = writtenFiles.remove(directDiskAccessResult);
616                         if (writeFilename != null) {
617                                 if (!new File(writeFilename).delete()) {
618                                         logger.warning("could not delete " + writeFilename);
619                                 }
620                         }
621                 }
622
623                 //
624                 // INTERFACE FcpListener
625                 //
626
627                 /**
628                  * @see net.pterodactylus.fcp.FcpListener#connectionClosed(net.pterodactylus.fcp.FcpConnection,
629                  *      Throwable)
630                  */
631                 @SuppressWarnings("synthetic-access")
632                 public void connectionClosed(FcpConnection fcpConnection, Throwable throwable) {
633                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
634                                 return;
635                         }
636                         cancelIdentifier(null);
637                         disconnect(throwable);
638                 }
639
640                 /**
641                  * @see net.pterodactylus.fcp.FcpListener#receivedAllData(net.pterodactylus.fcp.FcpConnection,
642                  *      net.pterodactylus.fcp.AllData)
643                  */
644                 public void receivedAllData(FcpConnection fcpConnection, AllData allData) {
645                         /* TODO */
646                 }
647
648                 /**
649                  * @see net.pterodactylus.fcp.FcpListener#receivedCloseConnectionDuplicateClientName(net.pterodactylus.fcp.FcpConnection,
650                  *      net.pterodactylus.fcp.CloseConnectionDuplicateClientName)
651                  */
652                 public void receivedCloseConnectionDuplicateClientName(FcpConnection fcpConnection, CloseConnectionDuplicateClientName closeConnectionDuplicateClientName) {
653                         /* TODO */
654                 }
655
656                 /**
657                  * @see net.pterodactylus.fcp.FcpListener#receivedConfigData(net.pterodactylus.fcp.FcpConnection,
658                  *      net.pterodactylus.fcp.ConfigData)
659                  */
660                 public void receivedConfigData(FcpConnection fcpConnection, ConfigData configData) {
661                         /* TODO */
662                 }
663
664                 /**
665                  * @see net.pterodactylus.fcp.FcpListener#receivedDataFound(net.pterodactylus.fcp.FcpConnection,
666                  *      net.pterodactylus.fcp.DataFound)
667                  */
668                 public void receivedDataFound(FcpConnection fcpConnection, DataFound dataFound) {
669                         /* TODO */
670                 }
671
672                 /**
673                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeerNotes(net.pterodactylus.fcp.FcpConnection,
674                  *      net.pterodactylus.fcp.EndListPeerNotes)
675                  */
676                 public void receivedEndListPeerNotes(FcpConnection fcpConnection, EndListPeerNotes endListPeerNotes) {
677                         /* TODO */
678                 }
679
680                 /**
681                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPeers(net.pterodactylus.fcp.FcpConnection,
682                  *      net.pterodactylus.fcp.EndListPeers)
683                  */
684                 @SuppressWarnings("synthetic-access")
685                 public void receivedEndListPeers(FcpConnection fcpConnection, EndListPeers endListPeers) {
686                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
687                                 return;
688                         }
689                         String identifier = endListPeers.getIdentifier();
690                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.remove(identifier);
691                         if (peerListCallback == null) {
692                                 return;
693                         }
694                         peerListCallback.setDone();
695                 }
696
697                 /**
698                  * @see net.pterodactylus.fcp.FcpListener#receivedEndListPersistentRequests(net.pterodactylus.fcp.FcpConnection,
699                  *      net.pterodactylus.fcp.EndListPersistentRequests)
700                  */
701                 public void receivedEndListPersistentRequests(FcpConnection fcpConnection, EndListPersistentRequests endListPersistentRequests) {
702                         /* TODO */
703                 }
704
705                 /**
706                  * @see net.pterodactylus.fcp.FcpListener#receivedFCPPluginReply(net.pterodactylus.fcp.FcpConnection,
707                  *      net.pterodactylus.fcp.FCPPluginReply)
708                  */
709                 public void receivedFCPPluginReply(FcpConnection fcpConnection, FCPPluginReply fcpPluginReply) {
710                         /* TODO */
711                 }
712
713                 /**
714                  * @see net.pterodactylus.fcp.FcpListener#receivedGetFailed(net.pterodactylus.fcp.FcpConnection,
715                  *      net.pterodactylus.fcp.GetFailed)
716                  */
717                 @SuppressWarnings("synthetic-access")
718                 public void receivedGetFailed(FcpConnection fcpConnection, GetFailed getFailed) {
719                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
720                                 return;
721                         }
722                         String identifier = getFailed.getIdentifier();
723                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.remove(identifier);
724                         if (downloadCallback != null) {
725                                 downloadCallback.getIntermediaryResult().setFailed(true);
726                                 downloadCallback.setDone();
727                                 return;
728                         }
729                         /* unknown identifier? */
730                         logger.warning("unknown identifier for GetFailed: " + identifier);
731                 }
732
733                 /**
734                  * @see net.pterodactylus.fcp.FcpListener#receivedIdentifierCollision(net.pterodactylus.fcp.FcpConnection,
735                  *      net.pterodactylus.fcp.IdentifierCollision)
736                  */
737                 public void receivedIdentifierCollision(FcpConnection fcpConnection, IdentifierCollision identifierCollision) {
738                         /* TODO */
739                 }
740
741                 /**
742                  * @see net.pterodactylus.fcp.FcpListener#receivedMessage(net.pterodactylus.fcp.FcpConnection,
743                  *      net.pterodactylus.fcp.FcpMessage)
744                  */
745                 public void receivedMessage(FcpConnection fcpConnection, FcpMessage fcpMessage) {
746                         /* TODO */
747                 }
748
749                 /**
750                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeData(net.pterodactylus.fcp.FcpConnection,
751                  *      net.pterodactylus.fcp.NodeData)
752                  */
753                 public void receivedNodeData(FcpConnection fcpConnection, NodeData nodeData) {
754                         /* TODO */
755                 }
756
757                 /**
758                  * @see net.pterodactylus.fcp.FcpListener#receivedNodeHello(net.pterodactylus.fcp.FcpConnection,
759                  *      net.pterodactylus.fcp.NodeHello)
760                  */
761                 @SuppressWarnings("synthetic-access")
762                 public void receivedNodeHello(FcpConnection fcpConnection, NodeHello nodeHello) {
763                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
764                                 return;
765                         }
766                         synchronized (syncObject) {
767                                 connectCallback.getIntermediaryResult().setFailed(false);
768                                 connectCallback.setDone();
769                                 connectCallback = null;
770                         }
771                         fireClientConnected();
772                 }
773
774                 /**
775                  * @see net.pterodactylus.fcp.FcpListener#receivedPeer(net.pterodactylus.fcp.FcpConnection,
776                  *      net.pterodactylus.fcp.Peer)
777                  */
778                 @SuppressWarnings("synthetic-access")
779                 public void receivedPeer(FcpConnection fcpConnection, Peer peer) {
780                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
781                                 return;
782                         }
783                         String identifier = peer.getIdentifier();
784                         if (identifier == null) {
785                                 return;
786                         }
787                         HighLevelCallback<PeerListResult> peerListCallback = peerListCallbacks.get(identifier);
788                         if (peerListCallback != null) {
789                                 peerListCallback.getIntermediaryResult().addPeer(peer);
790                                 return;
791                         }
792                         HighLevelCallback<PeerResult> peerResult = peerCallbacks.remove(identifier);
793                         if (peerResult != null) {
794                                 peerResult.getIntermediaryResult().setPeer(peer);
795                                 peerResult.setDone();
796                                 return;
797                         }
798                         logger.warning("got Peer message with unknown identifier: " + identifier);
799                 }
800
801                 /**
802                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerNote(net.pterodactylus.fcp.FcpConnection,
803                  *      net.pterodactylus.fcp.PeerNote)
804                  */
805                 public void receivedPeerNote(FcpConnection fcpConnection, PeerNote peerNote) {
806                         /* TODO */
807                 }
808
809                 /**
810                  * @see net.pterodactylus.fcp.FcpListener#receivedPeerRemoved(net.pterodactylus.fcp.FcpConnection,
811                  *      net.pterodactylus.fcp.PeerRemoved)
812                  */
813                 public void receivedPeerRemoved(FcpConnection fcpConnection, PeerRemoved peerRemoved) {
814                         /* TODO */
815                 }
816
817                 /**
818                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentGet(net.pterodactylus.fcp.FcpConnection,
819                  *      net.pterodactylus.fcp.PersistentGet)
820                  */
821                 @SuppressWarnings("synthetic-access")
822                 public void receivedPersistentGet(FcpConnection fcpConnection, PersistentGet persistentGet) {
823                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
824                                 return;
825                         }
826                         String identifier = persistentGet.getIdentifier();
827                         if (downloadCallbacks.containsKey(identifier)) {
828                                 /* ignore, because a download does not care about this. */
829                                 return;
830                         }
831                 }
832
833                 /**
834                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPut(net.pterodactylus.fcp.FcpConnection,
835                  *      net.pterodactylus.fcp.PersistentPut)
836                  */
837                 public void receivedPersistentPut(FcpConnection fcpConnection, PersistentPut persistentPut) {
838                         /* TODO */
839                 }
840
841                 /**
842                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentPutDir(net.pterodactylus.fcp.FcpConnection,
843                  *      net.pterodactylus.fcp.PersistentPutDir)
844                  */
845                 public void receivedPersistentPutDir(FcpConnection fcpConnection, PersistentPutDir persistentPutDir) {
846                         /* TODO */
847                 }
848
849                 /**
850                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestModified(net.pterodactylus.fcp.FcpConnection,
851                  *      net.pterodactylus.fcp.PersistentRequestModified)
852                  */
853                 public void receivedPersistentRequestModified(FcpConnection fcpConnection, PersistentRequestModified persistentRequestModified) {
854                         /* TODO */
855                 }
856
857                 /**
858                  * @see net.pterodactylus.fcp.FcpListener#receivedPersistentRequestRemoved(net.pterodactylus.fcp.FcpConnection,
859                  *      net.pterodactylus.fcp.PersistentRequestRemoved)
860                  */
861                 public void receivedPersistentRequestRemoved(FcpConnection fcpConnection, PersistentRequestRemoved persistentRequestRemoved) {
862                         /* TODO */
863                 }
864
865                 /**
866                  * @see net.pterodactylus.fcp.FcpListener#receivedPluginInfo(net.pterodactylus.fcp.FcpConnection,
867                  *      net.pterodactylus.fcp.PluginInfo)
868                  */
869                 public void receivedPluginInfo(FcpConnection fcpConnection, PluginInfo pluginInfo) {
870                         /* TODO */
871                 }
872
873                 /**
874                  * @see net.pterodactylus.fcp.FcpListener#receivedProtocolError(net.pterodactylus.fcp.FcpConnection,
875                  *      net.pterodactylus.fcp.ProtocolError)
876                  */
877                 @SuppressWarnings("synthetic-access")
878                 public void receivedProtocolError(FcpConnection fcpConnection, ProtocolError protocolError) {
879                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
880                                 return;
881                         }
882                         String identifier = protocolError.getIdentifier();
883                         if (identifier == null) {
884                                 return;
885                         }
886                         cancelIdentifier(identifier);
887                 }
888
889                 /**
890                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFailed(net.pterodactylus.fcp.FcpConnection,
891                  *      net.pterodactylus.fcp.PutFailed)
892                  */
893                 public void receivedPutFailed(FcpConnection fcpConnection, PutFailed putFailed) {
894                         /* TODO */
895                 }
896
897                 /**
898                  * @see net.pterodactylus.fcp.FcpListener#receivedPutFetchable(net.pterodactylus.fcp.FcpConnection,
899                  *      net.pterodactylus.fcp.PutFetchable)
900                  */
901                 public void receivedPutFetchable(FcpConnection fcpConnection, PutFetchable putFetchable) {
902                         /* TODO */
903                 }
904
905                 /**
906                  * @see net.pterodactylus.fcp.FcpListener#receivedPutSuccessful(net.pterodactylus.fcp.FcpConnection,
907                  *      net.pterodactylus.fcp.PutSuccessful)
908                  */
909                 public void receivedPutSuccessful(FcpConnection fcpConnection, PutSuccessful putSuccessful) {
910                         /* TODO */
911                 }
912
913                 /**
914                  * @see net.pterodactylus.fcp.FcpListener#receivedSSKKeypair(net.pterodactylus.fcp.FcpConnection,
915                  *      net.pterodactylus.fcp.SSKKeypair)
916                  */
917                 @SuppressWarnings("synthetic-access")
918                 public void receivedSSKKeypair(FcpConnection fcpConnection, SSKKeypair sskKeypair) {
919                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
920                                 return;
921                         }
922                         HighLevelCallback<KeyGenerationResult> keyGenerationCallback = keyGenerationCallbacks.remove(sskKeypair.getIdentifier());
923                         if (keyGenerationCallback == null) {
924                                 return;
925                         }
926                         KeyGenerationResult keyGenerationResult = keyGenerationCallback.getIntermediaryResult();
927                         keyGenerationResult.setInsertURI(sskKeypair.getInsertURI());
928                         keyGenerationResult.setRequestURI(sskKeypair.getRequestURI());
929                         keyGenerationCallback.setDone();
930                 }
931
932                 /**
933                  * @see net.pterodactylus.fcp.FcpListener#receivedSimpleProgress(net.pterodactylus.fcp.FcpConnection,
934                  *      net.pterodactylus.fcp.SimpleProgress)
935                  */
936                 @SuppressWarnings("synthetic-access")
937                 public void receivedSimpleProgress(FcpConnection fcpConnection, SimpleProgress simpleProgress) {
938                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
939                                 return;
940                         }
941                         String identifier = simpleProgress.getIdentifier();
942                         HighLevelProgressCallback<DownloadResult> downloadCallback = downloadCallbacks.get(identifier);
943                         if (downloadCallback != null) {
944                                 DownloadResult downloadResult = downloadCallback.getIntermediaryResult();
945                                 downloadResult.setTotalBlocks(simpleProgress.getTotal());
946                                 downloadResult.setRequiredBlocks(simpleProgress.getRequired());
947                                 downloadResult.setSuccessfulBlocks(simpleProgress.getSucceeded());
948                                 downloadResult.setFailedBlocks(simpleProgress.getFailed());
949                                 downloadResult.setFatallyFailedBlocks(simpleProgress.getFatallyFailed());
950                                 downloadResult.setTotalFinalized(simpleProgress.isFinalizedTotal());
951                                 downloadCallback.progressUpdated();
952                                 return;
953                         }
954                         /* unknown identifier? */
955                         logger.warning("unknown identifier for SimpleProgress: " + identifier);
956                 }
957
958                 /**
959                  * @see net.pterodactylus.fcp.FcpListener#receivedStartedCompression(net.pterodactylus.fcp.FcpConnection,
960                  *      net.pterodactylus.fcp.StartedCompression)
961                  */
962                 public void receivedStartedCompression(FcpConnection fcpConnection, StartedCompression startedCompression) {
963                         /* TODO */
964                 }
965
966                 /**
967                  * @see net.pterodactylus.fcp.FcpListener#receivedSubscribedUSKUpdate(net.pterodactylus.fcp.FcpConnection,
968                  *      net.pterodactylus.fcp.SubscribedUSKUpdate)
969                  */
970                 public void receivedSubscribedUSKUpdate(FcpConnection fcpConnection, SubscribedUSKUpdate subscribedUSKUpdate) {
971                         /* TODO */
972                 }
973
974                 /**
975                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAComplete(net.pterodactylus.fcp.FcpConnection,
976                  *      net.pterodactylus.fcp.TestDDAComplete)
977                  */
978                 @SuppressWarnings("synthetic-access")
979                 public void receivedTestDDAComplete(FcpConnection fcpConnection, TestDDAComplete testDDAComplete) {
980                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
981                                 return;
982                         }
983                         String directory = testDDAComplete.getDirectory();
984                         if (directory == null) {
985                                 return;
986                         }
987                         HighLevelCallback<DirectDiskAccessResult> directDiskAccessCallback = directDiskAccessCallbacks.remove(directory);
988                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallback.getIntermediaryResult();
989                         cleanFiles(directDiskAccessResult);
990                         directDiskAccessResult.setReadAllowed(testDDAComplete.isReadDirectoryAllowed());
991                         directDiskAccessResult.setWriteAllowed(testDDAComplete.isWriteDirectoryAllowed());
992                         directDiskAccessCallback.setDone();
993                 }
994
995                 /**
996                  * @see net.pterodactylus.fcp.FcpListener#receivedTestDDAReply(net.pterodactylus.fcp.FcpConnection,
997                  *      net.pterodactylus.fcp.TestDDAReply)
998                  */
999                 @SuppressWarnings("synthetic-access")
1000                 public void receivedTestDDAReply(FcpConnection fcpConnection, TestDDAReply testDDAReply) {
1001                         if (fcpConnection != HighLevelClient.this.fcpConnection) {
1002                                 return;
1003                         }
1004                         String directory = testDDAReply.getDirectory();
1005                         if (directory == null) {
1006                                 return;
1007                         }
1008                         DirectDiskAccessResult directDiskAccessResult = directDiskAccessCallbacks.get(directory).getIntermediaryResult();
1009                         String readFilename = testDDAReply.getReadFilename();
1010                         String readContent = readContent(readFilename);
1011                         String writeFilename = testDDAReply.getWriteFilename();
1012                         String writeContent = testDDAReply.getContentToWrite();
1013                         writeContent(directDiskAccessResult, writeFilename, writeContent);
1014                         TestDDAResponse testDDAResponse = new TestDDAResponse(directory, readContent);
1015                         try {
1016                                 fcpConnection.sendMessage(testDDAResponse);
1017                         } catch (IOException e) {
1018                                 /* swallow. I’m verry unhappy about this. */
1019                         }
1020                 }
1021
1022                 /**
1023                  * @see net.pterodactylus.fcp.FcpListener#receivedURIGenerated(net.pterodactylus.fcp.FcpConnection,
1024                  *      net.pterodactylus.fcp.URIGenerated)
1025                  */
1026                 public void receivedURIGenerated(FcpConnection fcpConnection, URIGenerated uriGenerated) {
1027                         /* TODO */
1028                 }
1029
1030                 /**
1031                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownNodeIdentifier(net.pterodactylus.fcp.FcpConnection,
1032                  *      net.pterodactylus.fcp.UnknownNodeIdentifier)
1033                  */
1034                 public void receivedUnknownNodeIdentifier(FcpConnection fcpConnection, UnknownNodeIdentifier unknownNodeIdentifier) {
1035                         /* TODO */
1036                 }
1037
1038                 /**
1039                  * @see net.pterodactylus.fcp.FcpListener#receivedUnknownPeerNoteType(net.pterodactylus.fcp.FcpConnection,
1040                  *      net.pterodactylus.fcp.UnknownPeerNoteType)
1041                  */
1042                 public void receivedUnknownPeerNoteType(FcpConnection fcpConnection, UnknownPeerNoteType unknownPeerNoteType) {
1043                         /* TODO */
1044                 }
1045
1046                 /**
1047                  * @see net.pterodactylus.fcp.FcpListener#receviedFinishedCompression(net.pterodactylus.fcp.FcpConnection,
1048                  *      net.pterodactylus.fcp.FinishedCompression)
1049                  */
1050                 public void receviedFinishedCompression(FcpConnection fcpConnection, FinishedCompression finishedCompression) {
1051                         /* TODO */
1052                 }
1053
1054         }
1055
1056 }