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