package org.anhonesteffort.flock;
import android.app.NotificationManager;
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.InvalidMacException;
import org.anhonesteffort.flock.crypto.KeyHelper;
import org.anhonesteffort.flock.crypto.KeyUtil;
import org.anhonesteffort.flock.crypto.MasterCipher;
import org.anhonesteffort.flock.sync.OwsWebDav;
import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler;
import org.anhonesteffort.flock.sync.addressbook.LocalAddressbookStore;
import org.anhonesteffort.flock.sync.calendar.CalendarsSyncScheduler;
import org.anhonesteffort.flock.sync.calendar.HidingCalDavCollection;
import org.anhonesteffort.flock.sync.calendar.HidingCalDavStore;
import org.anhonesteffort.flock.sync.calendar.LocalCalendarStore;
import org.anhonesteffort.flock.sync.key.DavKeyStore;
import org.anhonesteffort.flock.webdav.PropertyParseException;
import org.apache.jackrabbit.webdav.DavException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import javax.net.ssl.SSLException;
/**
* Created by rhodey
*/
public class ImportOwsAccountService extends ImportAccountService {
private static final String TAG = "org.anhonesteffort.flock.ImportOwsAccountService";
private static final String KEY_INTENT = "ImportOwsAccountService.KEY_INTENT";
protected static final String KEY_MESSENGER = "ImportOwsAccountService.KEY_MESSENGER";
protected static final String KEY_ACCOUNT_ID = "ImportOwsAccountService.KEY_ACCOUNT_ID";
protected static final String KEY_MASTER_PASSPHRASE = "ImportOwsAccountService.KEY_OLD_MASTER_PASSPHRASE";
private Looper serviceLooper;
private ServiceHandler serviceHandler;
private NotificationManager notifyManager;
private NotificationCompat.Builder notificationBuilder;
private Messenger messenger;
private String accountId;
private String masterPassphrase;
private DavAccount importAccount;
private int resultCode;
private boolean remoteActivityIsAlive = true;
private boolean accountWasImported = false;
private void handleInitializeNotification() {
Log.d(TAG, "handleInitializeNotification()");
notificationBuilder.setContentTitle(getString(R.string.title_import_account))
.setContentText(getString(R.string.importing_contacts_and_calendars))
.setProgress(0, 0, true)
.setSmallIcon(R.drawable.flock_actionbar_icon);
startForeground(9003, 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);
if (accountWasImported) {
notificationBuilder
.setProgress(0, 0, false)
.setContentText(getString(R.string.account_import_completed_with_errors));
}
else {
notificationBuilder
.setProgress(0, 0, false)
.setContentText(getString(R.string.account_import_failed));
}
notifyManager.notify(9003, notificationBuilder.build());
}
private void handleImportComplete() {
Log.d(TAG, "handleImportComplete()");
if (accountWasImported && resultCode != ErrorToaster.CODE_SUCCESS)
stopForeground(false);
else if (remoteActivityIsAlive || resultCode == ErrorToaster.CODE_SUCCESS)
stopForeground(true);
else
stopForeground(false);
stopSelf();
}
private void handleDavLogin(Bundle result) {
Log.d(TAG, "handleDavLogin()");
try {
String authToken = KeyUtil.getAuthTokenForPassphrase(masterPassphrase);
importAccount = new DavAccount(accountId, authToken, OwsWebDav.HREF_WEBDAV_HOST);
DavAccountHelper.setAccountDavHREF(getBaseContext(), importAccount.getDavHostHREF());
if (DavAccountHelper.isAuthenticated(getBaseContext(), importAccount)) {
if (DavAccountHelper.isExpired(getBaseContext(), importAccount))
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUBSCRIPTION_EXPIRED);
else
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
}
else
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_UNAUTHORIZED);
} catch (DavException e) {
ErrorToaster.handleBundleError(e, result);
} catch (PropertyParseException e) {
ErrorToaster.handleBundleError(e, result);
} catch (IOException e) {
ErrorToaster.handleBundleError(e, result);
} catch (GeneralSecurityException e) {
ErrorToaster.handleBundleError(e, result);
}
}
private void handleImportAddressbook() {
Log.d(TAG, "handleImportAddressbook()");
LocalAddressbookStore localStore = new LocalAddressbookStore(getBaseContext(), importAccount);
String remotePath = OwsWebDav.getAddressbookPathForUsername(importAccount.getUserId());
String displayName = getString(R.string.addressbook);
if (localStore.getCollections().size() == 0) {
localStore.addCollection(remotePath, displayName);
new AddressbookSyncScheduler(getBaseContext()).requestSync();
}
}
private List<HidingCalDavCollection> handleRemoveKeyCollection(List<HidingCalDavCollection> collections) {
Optional<HidingCalDavCollection> keyCollection = Optional.absent();
for (HidingCalDavCollection collection : collections) {
if (collection.getPath().contains(DavKeyStore.PATH_KEY_COLLECTION))
keyCollection = Optional.of(collection);
}
if (keyCollection.isPresent())
collections.remove(keyCollection.get());
return collections;
}
private void handleImportCalendars(Bundle result) {
Log.d(TAG, "handleImportCalendars()");
try {
Optional<MasterCipher> masterCipher = KeyHelper.getMasterCipher(getBaseContext());
if (!masterCipher.isPresent()) {
Log.e(TAG, "master cipher is missing at handleImportCalendars()");
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_CRYPTO_ERROR);
return;
}
LocalCalendarStore localStore = new LocalCalendarStore(getBaseContext(), importAccount.getOsAccount());
HidingCalDavStore remoteStore = DavAccountHelper.getHidingCalDavStore(getBaseContext(), importAccount, masterCipher.get());
for (HidingCalDavCollection collection : handleRemoveKeyCollection(remoteStore.getCollections())) {
Optional<String> displayName = collection.getHiddenDisplayName();
Optional<Integer> color = collection.getHiddenColor();
if (displayName.isPresent()) {
if (color.isPresent())
localStore.addCollection(collection.getPath(), displayName.get(), color.get());
else
localStore.addCollection(collection.getPath(), displayName.get());
}
else
localStore.addCollection(collection.getPath());
}
remoteStore.releaseConnections();
new CalendarsSyncScheduler(getBaseContext()).requestSync();
result.putInt(ErrorToaster.KEY_STATUS_CODE, ErrorToaster.CODE_SUCCESS);
} catch (PropertyParseException e) {
ErrorToaster.handleBundleError(e, result);
} catch (DavException e) {
ErrorToaster.handleBundleError(e, result);
} catch (InvalidMacException e) {
ErrorToaster.handleBundleError(e, result);
} catch (RemoteException e) {
ErrorToaster.handleBundleError(e, result);
} catch (GeneralSecurityException e) {
ErrorToaster.handleBundleError(e, result);
} catch (SSLException e) {
ErrorToaster.handleBundleError(e, result);
} catch (IOException e) {
ErrorToaster.handleBundleError(e, result);
}
}
private void handleUiCallbackSubscriptionExpired() {
Log.d(TAG, "handleUiCallbackSubscriptionExpired()");
Message message = Message.obtain();
message.arg1 = ErrorToaster.CODE_SUBSCRIPTION_EXPIRED;
message.setData(importAccount.toBundle());
try {
messenger.send(message);
} catch (RemoteException e) {
Log.e(TAG, "caught exception while sending message to activity >> ", e);
remoteActivityIsAlive = false;
}
}
private void handleUiCallbackAccountImported() {
Log.d(TAG, "handleUiCallbackAccountImported()");
Message message = Message.obtain();
message.arg1 = ImportOwsAccountFragment.CODE_ACCOUNT_IMPORTED;
try {
messenger.send(message);
} catch (RemoteException e) {
Log.e(TAG, "caught exception while sending message to activity >> ", e);
remoteActivityIsAlive = false;
}
}
private void handleStartImportOwsAccount() {
Log.d(TAG, "handleStartImportOwsAccount()");
Bundle result = new Bundle();
handleInitializeNotification();
handleDavLogin(result);
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
handleImportAccount(result, importAccount, masterPassphrase);
if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUCCESS) {
accountWasImported = true;
handleUiCallbackAccountImported();
handleImportAddressbook();
handleImportCalendars(result);
}
}
else if (result.getInt(ErrorToaster.KEY_STATUS_CODE) == ErrorToaster.CODE_SUBSCRIPTION_EXPIRED) {
resultCode = result.getInt(ErrorToaster.KEY_STATUS_CODE);
handleUiCallbackSubscriptionExpired();
handleImportComplete();
return;
}
Message message = Message.obtain();
message.arg1 = result.getInt(ErrorToaster.KEY_STATUS_CODE);
resultCode = result.getInt(ErrorToaster.KEY_STATUS_CODE);
try {
if (!accountWasImported)
messenger.send(message);
} catch (RemoteException e) {
Log.e(TAG, "caught exception while sending message to activity >> ", e);
remoteActivityIsAlive = false;
}
handleImportComplete();
}
@Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ImportOwsAccountService", 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_ACCOUNT_ID) != null &&
intent.getExtras().getString(KEY_MASTER_PASSPHRASE) != null)
{
messenger = (Messenger) intent.getExtras().get(KEY_MESSENGER);
accountId = intent.getExtras().getString(KEY_ACCOUNT_ID);
masterPassphrase = intent.getExtras().getString(KEY_MASTER_PASSPHRASE);
handleStartImportOwsAccount();
}
else
Log.e(TAG, "received intent without messenger, account id or master passphrase");
}
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;
}
}