gecko/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java
2012-07-11 14:34:22 -07:00

176 lines
6.3 KiB
Java

/* 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.middleware;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ExecutorService;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.repositories.InactiveSessionException;
import org.mozilla.gecko.sync.repositories.NoStoreDelegateException;
import org.mozilla.gecko.sync.repositories.RecordFactory;
import org.mozilla.gecko.sync.repositories.RepositorySession;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate;
import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate;
import org.mozilla.gecko.sync.repositories.domain.Record;
/**
* It's a RepositorySession that accepts Records as input, producing CryptoRecords
* for submission to a remote service.
* Takes a RecordFactory as a parameter. This is in charge of taking decrypted CryptoRecords
* as input and producing some expected kind of Record as output for local use.
*
*
+------------------------------------+
| Server11RepositorySession |
+-------------------------+----------+
^ |
| |
Encrypted CryptoRecords
| |
| v
+---------+--------------------------+
| Crypto5MiddlewareRepositorySession |
+------------------------------------+
^ |
| | Decrypted CryptoRecords
| |
| +---------------+
| | RecordFactory |
| +--+------------+
| |
Local Record instances
| |
| v
+---------+--------------------------+
| Local RepositorySession instance |
+------------------------------------+
* @author rnewman
*
*/
public class Crypto5MiddlewareRepositorySession extends MiddlewareRepositorySession {
private KeyBundle keyBundle;
private RecordFactory recordFactory;
public Crypto5MiddlewareRepositorySession(RepositorySession session, Crypto5MiddlewareRepository repository, RecordFactory recordFactory) {
super(session, repository);
this.keyBundle = repository.keyBundle;
this.recordFactory = recordFactory;
}
public class DecryptingTransformingFetchDelegate implements RepositorySessionFetchRecordsDelegate {
private RepositorySessionFetchRecordsDelegate next;
private KeyBundle keyBundle;
private RecordFactory recordFactory;
DecryptingTransformingFetchDelegate(RepositorySessionFetchRecordsDelegate next, KeyBundle bundle, RecordFactory recordFactory) {
this.next = next;
this.keyBundle = bundle;
this.recordFactory = recordFactory;
}
@Override
public void onFetchFailed(Exception ex, Record record) {
next.onFetchFailed(ex, record);
}
@Override
public void onFetchedRecord(Record record) {
CryptoRecord r;
try {
r = (CryptoRecord) record;
} catch (ClassCastException e) {
next.onFetchFailed(e, record);
return;
}
r.keyBundle = keyBundle;
try {
r.decrypt();
} catch (Exception e) {
next.onFetchFailed(e, r);
return;
}
Record transformed;
try {
transformed = this.recordFactory.createRecord(r);
} catch (Exception e) {
next.onFetchFailed(e, r);
return;
}
next.onFetchedRecord(transformed);
}
@Override
public void onFetchCompleted(final long fetchEnd) {
next.onFetchCompleted(fetchEnd);
}
@Override
public RepositorySessionFetchRecordsDelegate deferredFetchDelegate(ExecutorService executor) {
// Synchronously perform *our* work, passing through appropriately.
RepositorySessionFetchRecordsDelegate deferredNext = next.deferredFetchDelegate(executor);
return new DecryptingTransformingFetchDelegate(deferredNext, keyBundle, recordFactory);
}
}
private DecryptingTransformingFetchDelegate makeUnwrappingDelegate(RepositorySessionFetchRecordsDelegate inner) {
if (inner == null) {
throw new IllegalArgumentException("Inner delegate cannot be null!");
}
return new DecryptingTransformingFetchDelegate(inner, this.keyBundle, this.recordFactory);
}
@Override
public void fetchSince(long timestamp,
RepositorySessionFetchRecordsDelegate delegate) {
inner.fetchSince(timestamp, makeUnwrappingDelegate(delegate));
}
@Override
public void fetch(String[] guids,
RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException {
inner.fetch(guids, makeUnwrappingDelegate(delegate));
}
@Override
public void fetchAll(RepositorySessionFetchRecordsDelegate delegate) {
inner.fetchAll(makeUnwrappingDelegate(delegate));
}
@Override
public void setStoreDelegate(RepositorySessionStoreDelegate delegate) {
// TODO: it remains to be seen how this will work.
inner.setStoreDelegate(delegate);
this.delegate = delegate; // So we can handle errors without involving inner.
}
@Override
public void store(Record record) throws NoStoreDelegateException {
if (delegate == null) {
throw new NoStoreDelegateException();
}
CryptoRecord rec = record.getEnvelope();
rec.keyBundle = this.keyBundle;
try {
rec.encrypt();
} catch (UnsupportedEncodingException e) {
delegate.onRecordStoreFailed(e, record.guid);
return;
} catch (CryptoException e) {
delegate.onRecordStoreFailed(e, record.guid);
return;
}
// Allow the inner session to do delegate handling.
inner.store(rec);
}
}