package plugins.CENO.Bridge.Signaling;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import plugins.CENO.Bridge.CENOBridge;
import plugins.CENO.Bridge.RequestReceiver;
import freenet.client.FetchException;
import freenet.client.FetchResult;
import freenet.client.InsertException;
import freenet.client.async.ClientContext;
import freenet.client.async.ClientGetCallback;
import freenet.client.async.ClientGetter;
import freenet.client.async.PersistenceDisabledException;
import freenet.keys.FreenetURI;
import freenet.node.RequestClient;
import freenet.support.Logger;
import freenet.support.io.ResumeFailedException;
public class Channel {
private FreenetURI insertSSK, requestSSK;
private Long lastKnownEdition = 0L;
private Long lastSynced = 0L;
public Channel() throws MalformedURLException {
this(null, null, null);
}
public Channel(String insertSSK) throws MalformedURLException {
this(insertSSK, null, null);
}
public Channel(String insertSSK, Long providedEdition, Long lastSynced) throws MalformedURLException {
if (insertSSK == null) {
FreenetURI[] channelKeyPair = CENOBridge.nodeInterface.generateKeyPair();
this.insertSSK = channelKeyPair[0];
this.requestSSK = channelKeyPair[1];
return;
}
this.insertSSK = new FreenetURI(insertSSK);
FreenetURI insertURI = new FreenetURI(this.insertSSK);
this.requestSSK = insertURI.deriveRequestURIFromInsertURI();
if (providedEdition != null) {
this.lastKnownEdition = providedEdition;
return;
}
if (insertURI.isUSK()) {
this.lastKnownEdition = insertURI.getEdition();
}
this.lastSynced = lastSynced != null ? lastSynced : 0;
}
public String getInsertSSK() {
return insertSSK.toASCIIString();
}
public Long getLastKnownEdition() {
return lastKnownEdition;
}
private void setLastKnownEdition(Long newEdition) {
this.lastKnownEdition = newEdition;
}
public Long getLastSynced() {
return lastSynced;
}
public void publishSyn() {
try {
FreenetURI synURI = new FreenetURI("USK", "syn", insertSSK.getRoutingKey(), insertSSK.getCryptoKey(), insertSSK.getExtra());
CENOBridge.nodeInterface.insertSingleChunk(synURI, Long.toString(System.currentTimeMillis()), CENOBridge.nodeInterface.getVoidPutCallback("" +
"Successfully inserted syn response to signalSSK ",
"Failed to insert syn response to signalSSK " + insertSSK));
} catch (UnsupportedEncodingException e) {
Logger.error(this, "Excpetion while inserting Syn response to an insertion key provided by a client: " + e.getMessage());
} catch (InsertException e) {
Logger.error(this, "Excpetion while inserting Syn response to an insertion key provided by a client: " + e.getMessage());
} catch (PersistenceDisabledException e) {
Logger.error(this, "Excpetion while inserting Syn response to an insertion key provided by a client: " + e.getMessage());
}
this.lastSynced = System.currentTimeMillis();
}
public void subscribeToChannelUpdates() throws MalformedURLException {
FreenetURI origUSK = new FreenetURI(requestSSK.getRoutingKey(), requestSSK.getCryptoKey(), requestSSK.getExtra(), "req", lastKnownEdition);
try {
CENOBridge.nodeInterface.fetchULPR(origUSK, new ReqCallback(origUSK, insertSSK.toString()));
} catch (FetchException e) {
Logger.error(this, "FetchException while starting ULPR for a signalling channel: " + e.getMessage());
}
}
public static class ReqCallback implements ClientGetCallback {
private FreenetURI uri;
private String channelSignalSSK;
public ReqCallback(FreenetURI uri, String channelSignalSSK) {
this.uri = uri;
this.channelSignalSSK = channelSignalSSK;
}
private void fetchNewRequests(FetchResult result) {
try {
String fetchedString = new String(result.asByteArray(), "UTF-8");
RequestReceiver.signalReceived(fetchedString.split("\\r?\\n"));
} catch (NullPointerException e) {
Logger.warning(this, "Received new request from client but payload was empty");
} catch (UnsupportedEncodingException e) {
Logger.error(this, "UTF-8 is not supported");
} catch (IOException e) {
Logger.warning(this, "IOException while parsing batch request from client");
}
FreenetURI nextURI = uri.setSuggestedEdition(uri.getEdition() + 1);
try {
CENOBridge.nodeInterface.fetchULPR(nextURI, new ReqCallback(nextURI, this.channelSignalSSK));
} catch (FetchException e) {
Logger.error(this, "FetchException while starting ULPR for next edition of a signalling channel: " + e.getMessage());
}
return;
}
@Override
public void onResume(ClientContext context) throws ResumeFailedException {
}
@Override
public RequestClient getRequestClient() {
return CENOBridge.nodeInterface.getRequestClient();
}
@Override
public void onSuccess(FetchResult result, ClientGetter state) {
ChannelManager.getInstance().getChannel(channelSignalSSK).setLastKnownEdition(uri.getSuggestedEdition());
fetchNewRequests(result);
}
@Override
public void onFailure(FetchException e, ClientGetter state) {
switch (e.getMode()) {
case PERMANENT_REDIRECT :
try {
CENOBridge.nodeInterface.fetchULPR(e.newURI, new ReqCallback(e.newURI, this.channelSignalSSK));
} catch (FetchException e1) {
Logger.error(this, "FetchException while starting ULPR for new edition of a signalling channel: " + e1.getMessage());
}
break;
case ALL_DATA_NOT_FOUND :
case DATA_NOT_FOUND :
Logger.warning(Channel.class,
"Found new request from client but could not fetch data for USK: " + uri);
break;
default:
Logger.warning(Channel.class, "Exception while fetching new request from client for USK: " + uri + ", " + e.getMessage());
break;
}
if (e.isDefinitelyFatal()) {
Logger.error(Channel.class, "Fatal error while fetching new request from client for USK: " + uri + ", " + e.getMessage());
}
}
}
}