diff options
Diffstat (limited to 'OpenPGP-Keychain-API/libraries/openpgp-api-library/src')
8 files changed, 1003 insertions, 0 deletions
| diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl new file mode 100644 index 000000000..7ee79d6ab --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/IOpenPgpService.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +  +package org.openintents.openpgp; + +interface IOpenPgpService { + +    // see OpenPgpApi for documentation +    Intent execute(in Intent data, in ParcelFileDescriptor input, in ParcelFileDescriptor output); + +}
\ No newline at end of file diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/OpenPgpError.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/OpenPgpError.java new file mode 100644 index 000000000..4dd2cc641 --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/OpenPgpError.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openintents.openpgp; + +import android.os.Parcel; +import android.os.Parcelable; + +public class OpenPgpError implements Parcelable { +    public static final int CLIENT_SIDE_ERROR = -1; + +    public static final int GENERIC_ERROR = 0; +    public static final int INCOMPATIBLE_API_VERSIONS = 1; + +    public static final int NO_OR_WRONG_PASSPHRASE = 2; +    public static final int NO_USER_IDS = 3; + +    int errorId; +    String message; + +    public OpenPgpError() { +    } + +    public OpenPgpError(int errorId, String message) { +        this.errorId = errorId; +        this.message = message; +    } + +    public OpenPgpError(OpenPgpError b) { +        this.errorId = b.errorId; +        this.message = b.message; +    } + +    public int getErrorId() { +        return errorId; +    } + +    public void setErrorId(int errorId) { +        this.errorId = errorId; +    } + +    public String getMessage() { +        return message; +    } + +    public void setMessage(String message) { +        this.message = message; +    } + +    public int describeContents() { +        return 0; +    } + +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeInt(errorId); +        dest.writeString(message); +    } + +    public static final Creator<OpenPgpError> CREATOR = new Creator<OpenPgpError>() { +        public OpenPgpError createFromParcel(final Parcel source) { +            OpenPgpError error = new OpenPgpError(); +            error.errorId = source.readInt(); +            error.message = source.readString(); +            return error; +        } + +        public OpenPgpError[] newArray(final int size) { +            return new OpenPgpError[size]; +        } +    }; +} diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java new file mode 100644 index 000000000..cb220cf6d --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/OpenPgpSignatureResult.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openintents.openpgp; + +import android.os.Parcel; +import android.os.Parcelable; + +public class OpenPgpSignatureResult implements Parcelable { +    // generic error on signature verification +    public static final int SIGNATURE_ERROR = 0; +    // successfully verified signature, with certified public key +    public static final int SIGNATURE_SUCCESS_CERTIFIED = 1; +    // no public key was found for this signature verification +    public static final int SIGNATURE_UNKNOWN_PUB_KEY = 2; +    // successfully verified signature, but with uncertified public key +    public static final int SIGNATURE_SUCCESS_UNCERTIFIED = 3; + +    int status; +    boolean signatureOnly; +    String userId; +    long keyId; + +    public int getStatus() { +        return status; +    } + +    public void setStatus(int status) { +        this.status = status; +    } + +    public boolean isSignatureOnly() { +        return signatureOnly; +    } + +    public void setSignatureOnly(boolean signatureOnly) { +        this.signatureOnly = signatureOnly; +    } + +    public String getUserId() { +        return userId; +    } + +    public void setUserId(String userId) { +        this.userId = userId; +    } + +    public long getKeyId() { +        return keyId; +    } + +    public void setKeyId(long keyId) { +        this.keyId = keyId; +    } + +    public OpenPgpSignatureResult() { + +    } + +    public OpenPgpSignatureResult(int signatureStatus, String signatureUserId, +                                  boolean signatureOnly, long keyId) { +        this.status = signatureStatus; +        this.signatureOnly = signatureOnly; +        this.userId = signatureUserId; +        this.keyId = keyId; +    } + +    public OpenPgpSignatureResult(OpenPgpSignatureResult b) { +        this.status = b.status; +        this.userId = b.userId; +        this.signatureOnly = b.signatureOnly; +        this.keyId = b.keyId; +    } + +    public int describeContents() { +        return 0; +    } + +    public void writeToParcel(Parcel dest, int flags) { +        dest.writeInt(status); +        dest.writeByte((byte) (signatureOnly ? 1 : 0)); +        dest.writeString(userId); +        dest.writeLong(keyId); +    } + +    public static final Creator<OpenPgpSignatureResult> CREATOR = new Creator<OpenPgpSignatureResult>() { +        public OpenPgpSignatureResult createFromParcel(final Parcel source) { +            OpenPgpSignatureResult vr = new OpenPgpSignatureResult(); +            vr.status = source.readInt(); +            vr.signatureOnly = source.readByte() == 1; +            vr.userId = source.readString(); +            vr.keyId = source.readLong(); +            return vr; +        } + +        public OpenPgpSignatureResult[] newArray(final int size) { +            return new OpenPgpSignatureResult[size]; +        } +    }; + +    @Override +    public String toString() { +        String out = new String(); +        out += "\nstatus: " + status; +        out += "\nuserId: " + userId; +        out += "\nsignatureOnly: " + signatureOnly; +        out += "\nkeyId: " + keyId; +        return out; +    } + +} diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java new file mode 100644 index 000000000..f768a1685 --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openintents.openpgp.util; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.util.Log; +import org.openintents.openpgp.IOpenPgpService; +import org.openintents.openpgp.OpenPgpError; +import java.io.InputStream; +import java.io.OutputStream; + +public class OpenPgpApi { + +    public static final String TAG = "OpenPgp API"; + +    public static final int API_VERSION = 2; +    public static final String SERVICE_INTENT = "org.openintents.openpgp.IOpenPgpService"; +     +    /** +     * General extras +     * -------------- +     * +     * required extras: +     * int           EXTRA_API_VERSION           (always required) +     * +     * returned extras: +     * int           RESULT_CODE                 (RESULT_CODE_ERROR, RESULT_CODE_SUCCESS or RESULT_CODE_USER_INTERACTION_REQUIRED) +     * OpenPgpError  RESULT_ERROR                (if RESULT_CODE == RESULT_CODE_ERROR) +     * PendingIntent RESULT_INTENT               (if RESULT_CODE == RESULT_CODE_USER_INTERACTION_REQUIRED) +     */ + +    /** +     * Sign only +     * +     * optional extras: +     * boolean       EXTRA_REQUEST_ASCII_ARMOR   (request ascii armor for ouput) +     * String        EXTRA_PASSPHRASE            (key passphrase) +     */ +    public static final String ACTION_SIGN = "org.openintents.openpgp.action.SIGN"; + +    /** +     * Encrypt +     * +     * required extras: +     * String[]      EXTRA_USER_IDS              (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT) +     * or +     * long[]        EXTRA_KEY_IDS +     * +     * optional extras: +     * boolean       EXTRA_REQUEST_ASCII_ARMOR   (request ascii armor for ouput) +     * String        EXTRA_PASSPHRASE            (key passphrase) +     */ +    public static final String ACTION_ENCRYPT = "org.openintents.openpgp.action.ENCRYPT"; + +    /** +     * Sign and encrypt +     * +     * required extras: +     * String[]      EXTRA_USER_IDS              (=emails of recipients, if more than one key has a user_id, a PendingIntent is returned via RESULT_INTENT) +     * or +     * long[]        EXTRA_KEY_IDS +     * +     * optional extras: +     * boolean       EXTRA_REQUEST_ASCII_ARMOR   (request ascii armor for ouput) +     * String        EXTRA_PASSPHRASE            (key passphrase) +     */ +    public static final String ACTION_SIGN_AND_ENCRYPT = "org.openintents.openpgp.action.SIGN_AND_ENCRYPT"; + +    /** +     * Decrypts and verifies given input stream. This methods handles encrypted-only, signed-and-encrypted, +     * and also signed-only input. +     * +     * If OpenPgpSignatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY +     * in addition a PendingIntent is returned via RESULT_INTENT to download missing keys. +     * +     * optional extras: +     * boolean       EXTRA_REQUEST_ASCII_ARMOR   (request ascii armor for ouput) +     * +     * returned extras: +     * OpenPgpSignatureResult   RESULT_SIGNATURE +     */ +    public static final String ACTION_DECRYPT_VERIFY = "org.openintents.openpgp.action.DECRYPT_VERIFY"; + +    /** +     * Get key ids based on given user ids (=emails) +     * +     * required extras: +     * String[]      EXTRA_USER_IDS +     * +     * returned extras: +     * long[]        EXTRA_KEY_IDS +     */ +    public static final String ACTION_GET_KEY_IDS = "org.openintents.openpgp.action.GET_KEY_IDS"; + +    /** +     * This action returns RESULT_CODE_SUCCESS if the OpenPGP Provider already has the key +     * corresponding to the given key id in its database. +     * +     * It returns RESULT_CODE_USER_INTERACTION_REQUIRED if the Provider does not have the key. +     * The PendingIntent from RESULT_INTENT can be used to retrieve those from a keyserver. +     * +     * required extras: +     * long        EXTRA_KEY_ID +     */ +    public static final String ACTION_GET_KEY = "org.openintents.openpgp.action.GET_KEY"; + +    /* Intent extras */ +    public static final String EXTRA_API_VERSION = "api_version"; + +    // SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY +    // request ASCII Armor for output +    // OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53) +    public static final String EXTRA_REQUEST_ASCII_ARMOR = "ascii_armor"; + +    // ENCRYPT, SIGN_AND_ENCRYPT +    public static final String EXTRA_USER_IDS = "user_ids"; +    public static final String EXTRA_KEY_IDS = "key_ids"; +    // optional extras: +    public static final String EXTRA_PASSPHRASE = "passphrase"; + +    // GET_KEY +    public static final String EXTRA_KEY_ID = "key_id"; + +    /* Service Intent returns */ +    public static final String RESULT_CODE = "result_code"; + +    // get actual error object from RESULT_ERROR +    public static final int RESULT_CODE_ERROR = 0; +    // success! +    public static final int RESULT_CODE_SUCCESS = 1; +    // get PendingIntent from RESULT_INTENT, start PendingIntent with startIntentSenderForResult, +    // and execute service method again in onActivityResult +    public static final int RESULT_CODE_USER_INTERACTION_REQUIRED = 2; + +    public static final String RESULT_ERROR = "error"; +    public static final String RESULT_INTENT = "intent"; + +    // DECRYPT_VERIFY +    public static final String RESULT_SIGNATURE = "signature"; + +    IOpenPgpService mService; +    Context mContext; + +    public OpenPgpApi(Context context, IOpenPgpService service) { +        this.mContext = context; +        this.mService = service; +    } + +    public interface IOpenPgpCallback { +        void onReturn(final Intent result); +    } + +    private class OpenPgpAsyncTask extends AsyncTask<Void, Integer, Intent> { +        Intent data; +        InputStream is; +        OutputStream os; +        IOpenPgpCallback callback; + +        private OpenPgpAsyncTask(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) { +            this.data = data; +            this.is = is; +            this.os = os; +            this.callback = callback; +        } + +        @Override +        protected Intent doInBackground(Void... unused) { +            return executeApi(data, is, os); +        } + +        protected void onPostExecute(Intent result) { +            callback.onReturn(result); +        } + +    } + +    @TargetApi(Build.VERSION_CODES.HONEYCOMB) +    public void executeApiAsync(Intent data, InputStream is, OutputStream os, IOpenPgpCallback callback) { +        OpenPgpAsyncTask task = new OpenPgpAsyncTask(data, is, os, callback); + +        // don't serialize async tasks! +        // http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { +            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); +        } else { +            task.execute((Void[]) null); +        } +    } + +    public Intent executeApi(Intent data, InputStream is, OutputStream os) { +        try { +            data.putExtra(EXTRA_API_VERSION, OpenPgpApi.API_VERSION); + +            Intent result = null; + +            if (ACTION_GET_KEY_IDS.equals(data.getAction())) { +                result = mService.execute(data, null, null); +                return result; +            } else { +                // pipe the input and output +                ParcelFileDescriptor input = ParcelFileDescriptorUtil.pipeFrom(is, +                        new ParcelFileDescriptorUtil.IThreadListener() { + +                            @Override +                            public void onThreadFinished(Thread thread) { +                                //Log.d(OpenPgpApi.TAG, "Copy to service finished"); +                            } +                        }); +                ParcelFileDescriptor output = ParcelFileDescriptorUtil.pipeTo(os, +                        new ParcelFileDescriptorUtil.IThreadListener() { + +                            @Override +                            public void onThreadFinished(Thread thread) { +                                //Log.d(OpenPgpApi.TAG, "Service finished writing!"); +                            } +                        }); + +                // blocks until result is ready +                result = mService.execute(data, input, output); +                // close() is required to halt the TransferThread +                output.close(); + +                // set class loader to current context to allow unparcelling +                // of OpenPgpError and OpenPgpSignatureResult +                // http://stackoverflow.com/a/3806769 +                result.setExtrasClassLoader(mContext.getClassLoader()); + +                return result; +            } +        } catch (Exception e) { +            Log.e(OpenPgpApi.TAG, "Exception", e); +            Intent result = new Intent(); +            result.putExtra(RESULT_CODE, RESULT_CODE_ERROR); +            result.putExtra(RESULT_ERROR, +                    new OpenPgpError(OpenPgpError.CLIENT_SIDE_ERROR, e.getMessage())); +            return result; +        } +    } + +} diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java new file mode 100644 index 000000000..cf5864620 --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpListPreference.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openintents.openpgp.util; + +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ResolveInfo; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.preference.DialogPreference; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ListAdapter; +import android.widget.TextView; +import org.openintents.openpgp.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * Does not extend ListPreference, but is very similar to it! + * http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/preference/ListPreference.java/?v=source + */ +public class OpenPgpListPreference extends DialogPreference { +    private static final String OPENKEYCHAIN_PACKAGE = "org.sufficientlysecure.keychain"; +    private static final String MARKET_INTENT_URI_BASE = "market://details?id=%s"; +    private static final Intent MARKET_INTENT = new Intent(Intent.ACTION_VIEW, Uri.parse( +            String.format(MARKET_INTENT_URI_BASE, OPENKEYCHAIN_PACKAGE))); + +    private ArrayList<OpenPgpProviderEntry> mLegacyList = new ArrayList<OpenPgpProviderEntry>(); +    private ArrayList<OpenPgpProviderEntry> mList = new ArrayList<OpenPgpProviderEntry>(); + +    private String mSelectedPackage; + +    public OpenPgpListPreference(Context context, AttributeSet attrs) { +        super(context, attrs); +    } + +    public OpenPgpListPreference(Context context) { +        this(context, null); +    } + +    /** +     * Public method to add new entries for legacy applications +     * +     * @param packageName +     * @param simpleName +     * @param icon +     */ +    public void addLegacyProvider(int position, String packageName, String simpleName, Drawable icon) { +        mLegacyList.add(position, new OpenPgpProviderEntry(packageName, simpleName, icon)); +    } + +    @Override +    protected void onPrepareDialogBuilder(Builder builder) { +        mList.clear(); +         +        // add "none"-entry +        mList.add(0, new OpenPgpProviderEntry("", +                getContext().getString(R.string.openpgp_list_preference_none), +                getContext().getResources().getDrawable(R.drawable.ic_action_cancel_launchersize))); +         +        // add all additional (legacy) providers +        mList.addAll(mLegacyList); +         +        // search for OpenPGP providers... +        ArrayList<OpenPgpProviderEntry> providerList = new ArrayList<OpenPgpProviderEntry>(); +        Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT); +        List<ResolveInfo> resInfo = getContext().getPackageManager().queryIntentServices(intent, 0); +        if (!resInfo.isEmpty()) { +            for (ResolveInfo resolveInfo : resInfo) { +                if (resolveInfo.serviceInfo == null) +                    continue; + +                String packageName = resolveInfo.serviceInfo.packageName; +                String simpleName = String.valueOf(resolveInfo.serviceInfo.loadLabel(getContext() +                        .getPackageManager())); +                Drawable icon = resolveInfo.serviceInfo.loadIcon(getContext().getPackageManager()); + +                providerList.add(new OpenPgpProviderEntry(packageName, simpleName, icon)); +            } +        } + +        if (providerList.isEmpty()) { +            // add install links if provider list is empty +            resInfo = getContext().getPackageManager().queryIntentActivities +                    (MARKET_INTENT, 0); +            for (ResolveInfo resolveInfo : resInfo) { +                Intent marketIntent = new Intent(MARKET_INTENT); +                marketIntent.setPackage(resolveInfo.activityInfo.packageName); +                Drawable icon = resolveInfo.activityInfo.loadIcon(getContext().getPackageManager()); +                String marketName = String.valueOf(resolveInfo.activityInfo.applicationInfo +                        .loadLabel(getContext().getPackageManager())); +                String simpleName = String.format(getContext().getString(R.string +                        .openpgp_install_openkeychain_via), marketName); +                mList.add(new OpenPgpProviderEntry(OPENKEYCHAIN_PACKAGE, simpleName, +                        icon, marketIntent)); +            } +        } else { +            // add provider +            mList.addAll(providerList); +        } + +        // Init ArrayAdapter with OpenPGP Providers +        ListAdapter adapter = new ArrayAdapter<OpenPgpProviderEntry>(getContext(), +                android.R.layout.select_dialog_singlechoice, android.R.id.text1, mList) { +            public View getView(int position, View convertView, ViewGroup parent) { +                // User super class to create the View +                View v = super.getView(position, convertView, parent); +                TextView tv = (TextView) v.findViewById(android.R.id.text1); + +                // Put the image on the TextView +                tv.setCompoundDrawablesWithIntrinsicBounds(mList.get(position).icon, null, +                        null, null); + +                // Add margin between image and text (support various screen densities) +                int dp10 = (int) (10 * getContext().getResources().getDisplayMetrics().density + 0.5f); +                tv.setCompoundDrawablePadding(dp10); + +                return v; +            } +        }; + +        builder.setSingleChoiceItems(adapter, getIndexOfProviderList(getValue()), +                new DialogInterface.OnClickListener() { + +                    @Override +                    public void onClick(DialogInterface dialog, int which) { +                        OpenPgpProviderEntry entry = mList.get(which); + +                        if (entry.intent != null) { +                            /* +                             * Intents are called as activity +                             * +                             * Current approach is to assume the user installed the app. +                             * If he does not, the selected package is not valid. +                             * +                             * However  applications should always consider this could happen, +                             * as the user might remove the currently used OpenPGP app. +                             */ +                            getContext().startActivity(entry.intent); +                        } + +                        mSelectedPackage = entry.packageName; + +                        /* +                         * Clicking on an item simulates the positive button click, and dismisses +                         * the dialog. +                         */ +                        OpenPgpListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE); +                        dialog.dismiss(); +                    } +                }); + +        /* +         * The typical interaction for list-based dialogs is to have click-on-an-item dismiss the +         * dialog instead of the user having to press 'Ok'. +         */ +        builder.setPositiveButton(null, null); +    } + +    @Override +    protected void onDialogClosed(boolean positiveResult) { +        super.onDialogClosed(positiveResult); + +        if (positiveResult && (mSelectedPackage != null)) { +            if (callChangeListener(mSelectedPackage)) { +                setValue(mSelectedPackage); +            } +        } +    } + +    private int getIndexOfProviderList(String packageName) { +        for (OpenPgpProviderEntry app : mList) { +            if (app.packageName.equals(packageName)) { +                return mList.indexOf(app); +            } +        } + +        return -1; +    } + +    public void setValue(String packageName) { +        mSelectedPackage = packageName; +        persistString(packageName); +    } + +    public String getValue() { +        return mSelectedPackage; +    } + +    public String getEntry() { +        return getEntryByValue(mSelectedPackage); +    } + +    @Override +    protected Object onGetDefaultValue(TypedArray a, int index) { +        return a.getString(index); +    } + +    @Override +    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { +        setValue(restoreValue ? getPersistedString(mSelectedPackage) : (String) defaultValue); +    } + +    public String getEntryByValue(String packageName) { +        for (OpenPgpProviderEntry app : mList) { +            if (app.packageName.equals(packageName)) { +                return app.simpleName; +            } +        } + +        return null; +    } + +    private static class OpenPgpProviderEntry { +        private String packageName; +        private String simpleName; +        private Drawable icon; +        private Intent intent; + +        public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon) { +            this.packageName = packageName; +            this.simpleName = simpleName; +            this.icon = icon; +        } + +        public OpenPgpProviderEntry(String packageName, String simpleName, Drawable icon, Intent intent) { +            this(packageName, simpleName, icon); +            this.intent = intent; +        } + +        @Override +        public String toString() { +            return simpleName; +        } +    } +} diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java new file mode 100644 index 000000000..c80656c52 --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpServiceConnection.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openintents.openpgp.util; + +import org.openintents.openpgp.IOpenPgpService; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.IBinder; + +public class OpenPgpServiceConnection { +    private Context mApplicationContext; + +    private boolean mBound; +    private IOpenPgpService mService; +    private String mProviderPackageName; + +    public OpenPgpServiceConnection(Context context, String providerPackageName) { +        this.mApplicationContext = context.getApplicationContext(); +        this.mProviderPackageName = providerPackageName; +    } + +    public IOpenPgpService getService() { +        return mService; +    } + +    public boolean isBound() { +        return mBound; +    } + +    private ServiceConnection mServiceConnection = new ServiceConnection() { +        public void onServiceConnected(ComponentName name, IBinder service) { +            mService = IOpenPgpService.Stub.asInterface(service); +            mBound = true; +        } + +        public void onServiceDisconnected(ComponentName name) { +            mService = null; +            mBound = false; +        } +    }; + +    /** +     * If not already bound, bind to service! +     * +     * @return +     */ +    public boolean bindToService() { +        // if not already bound... +        if (mService == null && !mBound) { +            try { +                Intent serviceIntent = new Intent(); +                serviceIntent.setAction(IOpenPgpService.class.getName()); +                // NOTE: setPackage is very important to restrict the intent to this provider only! +                serviceIntent.setPackage(mProviderPackageName); +                mApplicationContext.bindService(serviceIntent, mServiceConnection, +                        Context.BIND_AUTO_CREATE); + +                return true; +            } catch (Exception e) { +                return false; +            } +        } else { +            return true; +        } +    } + +    public void unbindFromService() { +        mApplicationContext.unbindService(mServiceConnection); +    } + +} diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java new file mode 100644 index 000000000..67fe86291 --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpUtils.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openintents.openpgp.util; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ResolveInfo; + +public class OpenPgpUtils { + +    public static final Pattern PGP_MESSAGE = Pattern.compile( +            ".*?(-----BEGIN PGP MESSAGE-----.*?-----END PGP MESSAGE-----).*", +            Pattern.DOTALL); + +    public static final Pattern PGP_SIGNED_MESSAGE = Pattern.compile( +            ".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*", +            Pattern.DOTALL); + +    public static final int PARSE_RESULT_NO_PGP = -1; +    public static final int PARSE_RESULT_MESSAGE = 0; +    public static final int PARSE_RESULT_SIGNED_MESSAGE = 1; + +    public static int parseMessage(String message) { +        Matcher matcherSigned = PGP_SIGNED_MESSAGE.matcher(message); +        Matcher matcherMessage = PGP_MESSAGE.matcher(message); + +        if (matcherMessage.matches()) { +            return PARSE_RESULT_MESSAGE; +        } else if (matcherSigned.matches()) { +            return PARSE_RESULT_SIGNED_MESSAGE; +        } else { +            return PARSE_RESULT_NO_PGP; +        } +    } + +    public static boolean isAvailable(Context context) { +        Intent intent = new Intent(OpenPgpApi.SERVICE_INTENT); +        List<ResolveInfo> resInfo = context.getPackageManager().queryIntentServices(intent, 0); +        if (!resInfo.isEmpty()) { +            return true; +        } else { +            return false; +        } +    } + +} diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java new file mode 100644 index 000000000..58c62110d --- /dev/null +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/ParcelFileDescriptorUtil.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + *               2013 Flow (http://stackoverflow.com/questions/18212152/transfer-inputstream-to-another-service-across-process-boundaries-with-parcelf) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.openintents.openpgp.util; + +import android.os.ParcelFileDescriptor; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class ParcelFileDescriptorUtil { + +    public interface IThreadListener { +        void onThreadFinished(final Thread thread); +    } + +    public static ParcelFileDescriptor pipeFrom(InputStream inputStream, IThreadListener listener) +            throws IOException { +        ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); +        ParcelFileDescriptor readSide = pipe[0]; +        ParcelFileDescriptor writeSide = pipe[1]; + +        // start the transfer thread +        new TransferThread(inputStream, new ParcelFileDescriptor.AutoCloseOutputStream(writeSide), +                listener) +                .start(); + +        return readSide; +    } + +    public static ParcelFileDescriptor pipeTo(OutputStream outputStream, IThreadListener listener) +            throws IOException { +        ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); +        ParcelFileDescriptor readSide = pipe[0]; +        ParcelFileDescriptor writeSide = pipe[1]; + +        // start the transfer thread +        new TransferThread(new ParcelFileDescriptor.AutoCloseInputStream(readSide), outputStream, +                listener) +                .start(); + +        return writeSide; +    } + +    static class TransferThread extends Thread { +        final InputStream mIn; +        final OutputStream mOut; +        final IThreadListener mListener; + +        TransferThread(InputStream in, OutputStream out, IThreadListener listener) { +            super("ParcelFileDescriptor Transfer Thread"); +            mIn = in; +            mOut = out; +            mListener = listener; +            setDaemon(true); +        } + +        @Override +        public void run() { +            byte[] buf = new byte[1024]; +            int len; + +            try { +                while ((len = mIn.read(buf)) > 0) { +                    mOut.write(buf, 0, len); +                } +                mOut.flush(); // just to be safe +            } catch (IOException e) { +                //Log.e(OpenPgpApi.TAG, "TransferThread" + getId() + ": writing failed", e); +            } finally { +                try { +                    mIn.close(); +                } catch (IOException e) { +                    //Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e); +                } +                try { +                    mOut.close(); +                } catch (IOException e) { +                    //Log.e(OpenPgpApi.TAG, "TransferThread" + getId(), e); +                } +            } +            if (mListener != null) { +                //Log.d(OpenPgpApi.TAG, "TransferThread " + getId() + " finished!"); +                mListener.onThreadFinished(this); +            } +        } +    } +}
\ No newline at end of file | 
