/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.sync.setup.auth;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.net.BaseResource;
import org.mozilla.gecko.sync.net.SyncResourceDelegate;
import org.mozilla.gecko.sync.setup.Constants;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.ClientProtocolException;
public class FetchUserNodeStage implements AuthenticatorStage {
private final String LOG_TAG = "FetchUserNodeStage";
public interface FetchNodeStageDelegate {
public void handleSuccess(String url);
public void handleFailure(HttpResponse response);
public void handleError(Exception e);
}
@Override
public void execute(final AccountAuthenticator aa) throws URISyntaxException {
FetchNodeStageDelegate callbackDelegate = new FetchNodeStageDelegate() {
@Override
public void handleSuccess(String server) {
if (server == null) { // No separate auth node; use server url.
Logger.debug(LOG_TAG, "Using server as auth node.");
aa.authServer = aa.nodeServer;
aa.runNextStage();
return;
}
if (!server.endsWith("/")) {
server += "/";
}
aa.authServer = server;
aa.runNextStage();
}
@Override
public void handleFailure(HttpResponse response) {
int statusCode = response.getStatusLine().getStatusCode();
Logger.debug(LOG_TAG, "Failed to fetch user node, with status " + statusCode);
aa.abort(AuthenticationResult.FAILURE_OTHER, new Exception("HTTP " + statusCode + " error."));
}
@Override
public void handleError(Exception e) {
Logger.debug(LOG_TAG, "Error in fetching node.");
aa.abort(AuthenticationResult.FAILURE_OTHER, e);
}
};
String nodeRequestUrl = aa.nodeServer + Constants.AUTH_NODE_PATHNAME + Constants.AUTH_NODE_VERSION + aa.username + "/" + Constants.AUTH_NODE_SUFFIX;
// Might contain a plaintext username in the case of old Sync accounts.
Logger.pii(LOG_TAG, "NodeUrl: " + nodeRequestUrl);
final BaseResource httpResource = makeFetchNodeRequest(callbackDelegate, nodeRequestUrl);
// Make request on separate thread.
AccountAuthenticator.runOnThread(new Runnable() {
@Override
public void run() {
httpResource.get();
}
});
}
private BaseResource makeFetchNodeRequest(final FetchNodeStageDelegate callbackDelegate, String fetchNodeUrl) throws URISyntaxException {
// Fetch node containing user.
final BaseResource httpResource = new BaseResource(fetchNodeUrl);
httpResource.delegate = new SyncResourceDelegate(httpResource) {
@Override
public void handleHttpResponse(HttpResponse response) {
int statusCode = response.getStatusLine().getStatusCode();
switch(statusCode) {
case 200:
try {
InputStream content = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"), 1024);
String server = reader.readLine();
callbackDelegate.handleSuccess(server);
BaseResource.consumeReader(reader);
reader.close();
} catch (IllegalStateException e) {
callbackDelegate.handleError(e);
} catch (IOException e) {
callbackDelegate.handleError(e);
}
break;
case 404: // Does not support auth nodes, use server instead.
callbackDelegate.handleSuccess(null);
break;
default:
// No other acceptable states.
callbackDelegate.handleFailure(response);
}
BaseResource.consumeEntity(response.getEntity());
}
@Override
public void handleHttpProtocolException(ClientProtocolException e) {
callbackDelegate.handleError(e);
}
@Override
public void handleHttpIOException(IOException e) {
callbackDelegate.handleError(e);
}
@Override
public void handleTransportException(GeneralSecurityException e) {
callbackDelegate.handleError(e);
}
};
return httpResource;
}
}