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