/* * Copyright (C) 2012-2014 Dominik Schürmann * Copyright (C) 2010-2014 Thialfihar * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.sufficientlysecure.keychain.pgp; import android.content.Context; import android.os.Bundle; import android.os.Environment; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.openpgp.PGPException; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException; import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; public class PgpImportExport { // TODO: is this really used? public interface KeychainServiceListener { boolean hasServiceStopped(); } private Context mContext; private Progressable mProgressable; private KeychainServiceListener mKeychainServiceListener; private ProviderHelper mProviderHelper; public static final int RETURN_OK = 0; public static final int RETURN_BAD = -2; public static final int RETURN_UPDATED = 1; public PgpImportExport(Context context, Progressable progressable) { super(); this.mContext = context; this.mProgressable = progressable; this.mProviderHelper = new ProviderHelper(context); } public PgpImportExport(Context context, Progressable progressable, KeychainServiceListener keychainListener) { super(); this.mContext = context; this.mProgressable = progressable; this.mProviderHelper = new ProviderHelper(context); this.mKeychainServiceListener = keychainListener; } public void updateProgress(int message, int current, int total) { if (mProgressable != null) { mProgressable.setProgress(message, current, total); } } public void updateProgress(String message, int current, int total) { if (mProgressable != null) { mProgressable.setProgress(message, current, total); } } public void updateProgress(int current, int total) { if (mProgressable != null) { mProgressable.setProgress(current, total); } } public boolean uploadKeyRingToServer(HkpKeyserver server, WrappedPublicKeyRing keyring) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = null; try { aos = new ArmoredOutputStream(bos); keyring.encode(aos); aos.close(); String armoredKey = bos.toString("UTF-8"); server.add(armoredKey); return true; } catch (IOException e) { return false; } catch (AddKeyException e) { // TODO: tell the user? return false; } finally { try { if (aos != null) { aos.close(); } if (bos != null) { bos.close(); } } catch (IOException e) { } } } /** * Imports keys from given data. If keyIds is given only those are imported */ public Bundle importKeyRings(List entries) throws PgpGeneralException, PGPException, IOException { Bundle returnData = new Bundle(); updateProgress(R.string.progress_importing, 0, 100); int newKeys = 0; int oldKeys = 0; int badKeys = 0; int position = 0; for (ParcelableKeyRing entry : entries) { try { UncachedKeyRing key = entry.getUncachedKeyRing(); mProviderHelper.savePublicKeyRing(key); /*switch(status) { case RETURN_UPDATED: oldKeys++; break; case RETURN_OK: newKeys++; break; case RETURN_BAD: badKeys++; break; }*/ // TODO proper import feedback newKeys += 1; } catch (PgpGeneralException e) { Log.e(Constants.TAG, "Encountered bad key on import!", e); ++badKeys; } // update progress position++; updateProgress(position / entries.size() * 100, 100); } returnData.putInt(KeychainIntentService.RESULT_IMPORT_ADDED, newKeys); returnData.putInt(KeychainIntentService.RESULT_IMPORT_UPDATED, oldKeys); returnData.putInt(KeychainIntentService.RESULT_IMPORT_BAD, badKeys); return returnData; } public Bundle exportKeyRings(ArrayList publicKeyRingMasterIds, ArrayList secretKeyRingMasterIds, OutputStream outStream) throws PgpGeneralException, PGPException, IOException { Bundle returnData = new Bundle(); int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size(); int progress = 0; updateProgress( mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, masterKeyIdsSize), 0, 100); if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { throw new PgpGeneralException( mContext.getString(R.string.error_external_storage_not_ready)); } // For each public masterKey id for (long pubKeyMasterId : publicKeyRingMasterIds) { progress++; // Create an output stream ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext)); updateProgress(progress * 100 / masterKeyIdsSize, 100); try { WrappedPublicKeyRing ring = mProviderHelper.getWrappedPublicKeyRing( KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId) ); ring.encode(arOutStream); } catch (ProviderHelper.NotFoundException e) { Log.e(Constants.TAG, "key not found!", e); // TODO: inform user? } if (mKeychainServiceListener.hasServiceStopped()) { arOutStream.close(); return null; } arOutStream.close(); } // For each secret masterKey id for (long secretKeyMasterId : secretKeyRingMasterIds) { progress++; // Create an output stream ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext)); updateProgress(progress * 100 / masterKeyIdsSize, 100); try { WrappedSecretKeyRing secretKeyRing = mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId); secretKeyRing.encode(arOutStream); } catch (ProviderHelper.NotFoundException e) { Log.e(Constants.TAG, "key not found!", e); // TODO: inform user? } if (mKeychainServiceListener.hasServiceStopped()) { arOutStream.close(); return null; } arOutStream.close(); } returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize); updateProgress(R.string.progress_done, 100, 100); return returnData; } }