package org.anhonesteffort.flock;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import org.anhonesteffort.flock.util.guava.Optional;
import org.anhonesteffort.flock.auth.DavAccount;
import org.anhonesteffort.flock.crypto.KeyHelper;
import org.anhonesteffort.flock.crypto.KeyStore;
import org.anhonesteffort.flock.crypto.KeyUtil;
import org.anhonesteffort.flock.webdav.PropertyParseException;
import org.apache.jackrabbit.webdav.DavException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLException;
/**
* Created by rhodey.
*/
public class CorrectPasswordService extends Service {
private static final String TAG = "org.anhonesteffort.flock.CorrectPasswordService";
private static final String KEY_INTENT = "CorrectPasswordService.KEY_INTENT";
protected static final String KEY_MESSENGER = "CorrectPasswordService.KEY_MESSENGER";
protected static final String KEY_MASTER_PASSPHRASE = "CorrectPasswordService.KEY_OLD_MASTER_PASSPHRASE";
protected static final String KEY_ACCOUNT = "CorrectPasswordService.KEY_ACCOUNT";
private Looper serviceLooper;
private ServiceHandler serviceHandler;
private NotificationManager notifyManager;
private NotificationCompat.Builder notificationBuilder;
private Messenger messenger;
private String masterPassphrase;
private DavAccount account;
private int resultCode;
private boolean remoteActivityIsAlive = true;
private void handleInitializeNotification() {
Log.d(TAG, "handleInitializeNotification()");
notificationBuilder.setContentTitle(getString(R.string.title_correct_sync_password))
.setContentText(getString(R.string.updating_encryption_secrets))
.setProgress(0, 0, true)
.setSmallIcon(R.drawable.flock_actionbar_icon);
startForeground(9001, notificationBuilder.build());
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
if (remoteActivityIsAlive || resultCode == ErrorToaster.CODE_SUCCESS)
return;
Bundle errorBundler = new Bundle();
errorBundler.putInt(ErrorToaster.KEY_STATUS_CODE, resultCode);
ErrorToaster.handleDisplayToastBundledError(getBaseContext(), errorBundler);
notificationBuilder
.setProgress(0, 0, false)
.setContentText(getString(R.string.error_login_unauthorized));
notifyManager.notify(9001, notificationBuilder.build());
}
private void handleCorrectComplete() {
Log.d(TAG, "handleCorrectComplete()");
if (remoteActivityIsAlive || resultCode == ErrorToaster.CODE_SUCCESS)
stopForeground(true);
else
stopForeground(false);
stopSelf();
}
private void handleUpdateMasterPassphrase(Bundle result, String masterPassphrase) {
Log.d(TAG, "handleUpdateMasterPassphrase()");
KeyStore.saveMasterPassphrase(getBaseContext(), masterPassphrase);
try {
Optional<String> encryptedKeyMaterial = KeyHelper.buildEncryptedKeyMaterial(getBaseContext());
if (encryptedKeyMaterial.isPresent()) {
KeyStore.saveEncryptedKeyMaterial(getBaseContext(), encryptedKeyMaterial.get());
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
}
else {
Log.e(TAG, "unable to build encrypted key material");
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CRYPTO_ERROR);
}
} catch (GeneralSecurityException e) {
ErrorToaster.handleBundleError(e, result);
} catch (IOException e) {
Log.e(TAG, "caught exception while updating master passphrase", e);
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CRYPTO_ERROR);
}
}
private void handleDavLogin(Bundle result, DavAccount account) {
Log.d(TAG, "handleDavLogin()");
if (DavAccountHelper.isUsingOurServers(account)) {
try {
String owsAuthToken = KeyUtil.getAuthTokenForPassphrase(masterPassphrase);
account = new DavAccount(account.getUserId(), owsAuthToken, account.getDavHostHREF());
} catch (GeneralSecurityException e) {
ErrorToaster.handleBundleError(e, result);
return;
}
}
try {
if (DavAccountHelper.isAuthenticated(getBaseContext(), account)) {
DavAccountHelper.setAccountPassword(getBaseContext(), account.getAuthToken());
NotificationDrawer.cancelAuthNotification(getBaseContext());
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
}
else
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_UNAUTHORIZED);
} catch (PropertyParseException e) {
ErrorToaster.handleBundleError(e, result);
} catch (DavException e) {
ErrorToaster.handleBundleError(e, result);
} catch (SSLException e) {
ErrorToaster.handleBundleError(e, result);
} catch (IOException e) {
ErrorToaster.handleBundleError(e, result);
}
}
private void handleStartCorrectPassword() {
Log.d(TAG, "handleStartCorrectPassword()");
Bundle result = new Bundle();
handleInitializeNotification();
handleDavLogin(result, account);
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
if (DavAccountHelper.isUsingOurServers(account))
handleUpdateMasterPassphrase(result, masterPassphrase);
}
Message message = Message.obtain();
message.arg1 = result.getInt(ErrorToaster.KEY_STATUS_CODE);
resultCode = result.getInt(ErrorToaster.KEY_STATUS_CODE);
try {
messenger.send(message);
} catch (RemoteException e) {
Log.e(TAG, "caught exception while sending message to activity >> ", e);
remoteActivityIsAlive = false;
}
handleCorrectComplete();
}
@Override
public void onCreate() {
HandlerThread thread = new HandlerThread("CorrectPasswordService", HandlerThread.NORM_PRIORITY);
thread.start();
serviceLooper = thread.getLooper();
serviceHandler = new ServiceHandler(serviceLooper);
notifyManager = (NotificationManager)getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE);
notificationBuilder = new NotificationCompat.Builder(getBaseContext());
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage()");
Intent intent = msg.getData().getParcelable(KEY_INTENT);
if (intent != null) {
if (intent.getExtras() != null &&
intent.getExtras().get(KEY_MESSENGER) != null &&
intent.getExtras().getString(KEY_MASTER_PASSPHRASE) != null &&
intent.getExtras().getBundle(KEY_ACCOUNT) != null)
{
if (!DavAccount.build(intent.getExtras().getBundle(KEY_ACCOUNT)).isPresent()) {
Log.e(TAG, "received bad account bundle");
return;
}
messenger = (Messenger) intent.getExtras().get(KEY_MESSENGER);
masterPassphrase = intent.getExtras().getString(KEY_MASTER_PASSPHRASE);
account = DavAccount.build(intent.getExtras().getBundle(KEY_ACCOUNT)).get();
handleStartCorrectPassword();
}
else
Log.e(TAG, "received intent without messenger, master passphrase or account");
}
else
Log.e(TAG, "received message with null intent");
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = serviceHandler.obtainMessage();
msg.getData().putParcelable(KEY_INTENT, intent);
serviceHandler.sendMessage(msg);
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind()");
return null;
}
}