mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-24 01:32:16 -05:00
added initial support for HKP key servers, allowing searching and key import
Update issue 9 Can search a key server now, touch a result to import the key. Still needs better error handling and some Intents to import keys based on key ID. Also still need key server preferences.
This commit is contained in:
parent
b3a63beffc
commit
6e9146c91a
@ -177,6 +177,11 @@
|
|||||||
android:label="@string/title_mailInbox"
|
android:label="@string/title_mailInbox"
|
||||||
android:configChanges="keyboardHidden|orientation|keyboard"/>
|
android:configChanges="keyboardHidden|orientation|keyboard"/>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".KeyServerQueryActivity"
|
||||||
|
android:label="@string/title_keyServerQuery"
|
||||||
|
android:configChanges="keyboardHidden|orientation|keyboard"/>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".PreferencesActivity"
|
android:name=".PreferencesActivity"
|
||||||
android:label="@string/title_preferences"
|
android:label="@string/title_preferences"
|
||||||
@ -201,5 +206,6 @@
|
|||||||
<uses-permission android:name="com.google.android.gm.permission.READ_GMAIL" />
|
<uses-permission android:name="com.google.android.gm.permission.READ_GMAIL" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
|
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
48
res/layout/key_server_query_layout.xml
Normal file
48
res/layout/key_server_query_layout.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="fill_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/query"
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_search"
|
||||||
|
android:text="@string/btn_search"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="0dip"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
97
res/layout/key_server_query_result_item.xml
Normal file
97
res/layout/key_server_query_result_item.xml
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:paddingLeft="3dip"
|
||||||
|
android:paddingRight="?android:attr/scrollbarSize"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="5dip"
|
||||||
|
android:paddingRight="5dip"
|
||||||
|
android:layout_width="0dip"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mainUserId"
|
||||||
|
android:text="Main User ID"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/mainUserIdRest"
|
||||||
|
android:text="<user@somewhere.com>"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:minWidth="90dip"
|
||||||
|
android:paddingLeft="3dip"
|
||||||
|
android:gravity="right"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/keyId"
|
||||||
|
android:text="BBBBBBBB"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:typeface="monospace"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="fill_parent"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/algorithm"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/status"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="#e00"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="30dip">
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
26
res/layout/key_server_query_result_user_id.xml
Normal file
26
res/layout/key_server_query_result_user_id.xml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2010 Thialfihar <thi@thialfihar.org>
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_marginRight="?android:attr/scrollbarSize"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
android:paddingRight="3dip">
|
||||||
|
|
||||||
|
</TextView>
|
@ -40,6 +40,7 @@
|
|||||||
<string name="title_exportKeys">Export Keys</string>
|
<string name="title_exportKeys">Export Keys</string>
|
||||||
<string name="title_keyNotFound">Key Not Found</string>
|
<string name="title_keyNotFound">Key Not Found</string>
|
||||||
<string name="title_help">Getting Started</string>
|
<string name="title_help">Getting Started</string>
|
||||||
|
<string name="title_keyServerQuery">Query Key Server</string>
|
||||||
|
|
||||||
<!-- section_lowerCase: capitalized words, no punctuation -->
|
<!-- section_lowerCase: capitalized words, no punctuation -->
|
||||||
<string name="section_userIds">User IDs</string>
|
<string name="section_userIds">User IDs</string>
|
||||||
@ -70,6 +71,7 @@
|
|||||||
<string name="btn_clearFilter">Clear Filter</string>
|
<string name="btn_clearFilter">Clear Filter</string>
|
||||||
<string name="btn_changePassPhrase">Change Pass Phrase</string>
|
<string name="btn_changePassPhrase">Change Pass Phrase</string>
|
||||||
<string name="btn_setPassPhrase">Set Pass Phrase</string>
|
<string name="btn_setPassPhrase">Set Pass Phrase</string>
|
||||||
|
<string name="btn_search">Search</string>
|
||||||
|
|
||||||
<!-- menu_lowerCase: capitalized words, no punctuation -->
|
<!-- menu_lowerCase: capitalized words, no punctuation -->
|
||||||
<string name="menu_about">About</string>
|
<string name="menu_about">About</string>
|
||||||
@ -86,6 +88,7 @@
|
|||||||
<string name="menu_editKey">Edit Key</string>
|
<string name="menu_editKey">Edit Key</string>
|
||||||
<string name="menu_search">Search</string>
|
<string name="menu_search">Search</string>
|
||||||
<string name="menu_help">Help</string>
|
<string name="menu_help">Help</string>
|
||||||
|
<string name="menu_keyServer">Key Server</string>
|
||||||
|
|
||||||
<!-- label_lowerCase: capitalized words, no punctuation -->
|
<!-- label_lowerCase: capitalized words, no punctuation -->
|
||||||
<string name="label_sign">Sign</string>
|
<string name="label_sign">Sign</string>
|
||||||
@ -255,6 +258,7 @@
|
|||||||
<string name="progress_decompressingData">decompressing data...</string>
|
<string name="progress_decompressingData">decompressing data...</string>
|
||||||
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
<string name="progress_verifyingIntegrity">verifying integrity...</string>
|
||||||
<string name="progress_deletingSecurely">deleting \'%s\' securely...</string>
|
<string name="progress_deletingSecurely">deleting \'%s\' securely...</string>
|
||||||
|
<string name="progress_querying">querying...</string>
|
||||||
|
|
||||||
<!-- permission strings -->
|
<!-- permission strings -->
|
||||||
<string name="permission_read_key_details_label">Read key details from APG.</string>
|
<string name="permission_read_key_details_label">Read key details from APG.</string>
|
||||||
|
@ -191,6 +191,10 @@ public class Apg {
|
|||||||
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
|
||||||
Pattern.DOTALL);
|
Pattern.DOTALL);
|
||||||
|
|
||||||
|
public static Pattern PGP_PUBLIC_KEY =
|
||||||
|
Pattern.compile(".*?(-----BEGIN PGP PUBLIC KEY BLOCK-----.*?-----END PGP PUBLIC KEY BLOCK-----).*",
|
||||||
|
Pattern.DOTALL);
|
||||||
|
|
||||||
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
|
private static HashMap<Long, CachedPassPhrase> mPassPhraseCache =
|
||||||
new HashMap<Long, CachedPassPhrase>();
|
new HashMap<Long, CachedPassPhrase>();
|
||||||
private static String mEditPassPhrase = null;
|
private static String mEditPassPhrase = null;
|
||||||
@ -1006,6 +1010,25 @@ public class Apg {
|
|||||||
return algorithmStr + ", " + keySize + "bit";
|
return algorithmStr + ", " + keySize + "bit";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getFingerPrint(long keyId) {
|
||||||
|
String fingerPrint = Long.toHexString(keyId & 0xffffffffL).toUpperCase();
|
||||||
|
while (fingerPrint.length() < 8) {
|
||||||
|
fingerPrint = "0" + fingerPrint;
|
||||||
|
}
|
||||||
|
return fingerPrint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String keyToHex(long keyId) {
|
||||||
|
return getFingerPrint(keyId >> 32) + getFingerPrint(keyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long keyFromHex(String data) {
|
||||||
|
int len = data.length();
|
||||||
|
String s2 = data.substring(len - 8);
|
||||||
|
String s1 = data.substring(0, len - 8);
|
||||||
|
return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16);
|
||||||
|
}
|
||||||
|
|
||||||
public static void deleteKey(int keyRingId) {
|
public static void deleteKey(int keyRingId) {
|
||||||
mDatabase.deleteKeyRing(keyRingId);
|
mDatabase.deleteKeyRing(keyRingId);
|
||||||
}
|
}
|
||||||
|
@ -157,6 +157,13 @@ public class BaseActivity extends Activity
|
|||||||
return mProgressDialog;
|
return mProgressDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Id.dialog.querying: {
|
||||||
|
mProgressDialog.setMessage(this.getString(R.string.progress_querying));
|
||||||
|
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||||
|
mProgressDialog.setCancelable(false);
|
||||||
|
return mProgressDialog;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -362,6 +369,7 @@ public class BaseActivity extends Activity
|
|||||||
|
|
||||||
case Id.message.import_done: // intentionally no break
|
case Id.message.import_done: // intentionally no break
|
||||||
case Id.message.export_done: // intentionally no break
|
case Id.message.export_done: // intentionally no break
|
||||||
|
case Id.message.query_done: // intentionally no break
|
||||||
case Id.message.done: {
|
case Id.message.done: {
|
||||||
mProgressDialog = null;
|
mProgressDialog = null;
|
||||||
doneCallback(msg);
|
doneCallback(msg);
|
||||||
|
@ -584,7 +584,7 @@ public class DecryptActivity extends BaseActivity {
|
|||||||
if (data.getBoolean(Apg.EXTRA_SIGNATURE)) {
|
if (data.getBoolean(Apg.EXTRA_SIGNATURE)) {
|
||||||
String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
|
String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_ID);
|
||||||
mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
|
mSignatureKeyId = data.getLong(Apg.EXTRA_SIGNATURE_KEY_ID);
|
||||||
mUserIdRest.setText("id: " + Long.toHexString(mSignatureKeyId & 0xffffffffL));
|
mUserIdRest.setText("id: " + Apg.getFingerPrint(mSignatureKeyId));
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
userId = getResources().getString(R.string.unknownUserId);
|
userId = getResources().getString(R.string.unknownUserId);
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,6 @@ import android.os.Bundle;
|
|||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
119
src/org/thialfihar/android/apg/HkpKeyServer.java
Normal file
119
src/org/thialfihar/android/apg/HkpKeyServer.java
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import android.text.Html;
|
||||||
|
|
||||||
|
public class HkpKeyServer extends KeyServer {
|
||||||
|
private String mHost;
|
||||||
|
private short mPort = 11371;
|
||||||
|
|
||||||
|
// example:
|
||||||
|
// pub 2048R/<a href="/pks/lookup?op=get&search=0x887DF4BE9F5C9090">9F5C9090</a> 2009-08-17 <a href="/pks/lookup?op=vindex&search=0x887DF4BE9F5C9090">Jörg Runge <joerg@joergrunge.de></a>
|
||||||
|
public static Pattern PUB_KEY_LINE =
|
||||||
|
Pattern.compile("pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)",
|
||||||
|
Pattern.CASE_INSENSITIVE);
|
||||||
|
public static Pattern USER_ID_LINE =
|
||||||
|
Pattern.compile("^ +(.+)$", Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
public HkpKeyServer(String host) {
|
||||||
|
mHost = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HkpKeyServer(String host, short port) {
|
||||||
|
mHost = host;
|
||||||
|
mPort = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
List<KeyInfo> search(String query)
|
||||||
|
throws MalformedURLException, IOException {
|
||||||
|
Vector<KeyInfo> results = new Vector<KeyInfo>();
|
||||||
|
|
||||||
|
String url = "http://" + mHost + ":" + mPort + "/pks/lookup?op=index&search=" +
|
||||||
|
URLEncoder.encode(query, "utf8");
|
||||||
|
URL realUrl = new URL(url);
|
||||||
|
URLConnection conn = realUrl.openConnection();
|
||||||
|
InputStream is = conn.getInputStream();
|
||||||
|
ByteArrayOutputStream raw = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
byte buffer[] = new byte[1 << 16];
|
||||||
|
int n = 0;
|
||||||
|
while ((n = is.read(buffer)) != -1) {
|
||||||
|
raw.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
String encoding = conn.getContentEncoding();
|
||||||
|
if (encoding == null) {
|
||||||
|
encoding = "utf8";
|
||||||
|
}
|
||||||
|
String data = raw.toString(encoding);
|
||||||
|
Matcher matcher = PUB_KEY_LINE.matcher(data);
|
||||||
|
while (matcher.find()) {
|
||||||
|
KeyInfo info = new KeyInfo();
|
||||||
|
info.size = Integer.parseInt(matcher.group(1));
|
||||||
|
info.algorithm = matcher.group(2);
|
||||||
|
info.keyId = Apg.keyFromHex(matcher.group(3));
|
||||||
|
info.fingerPrint = Apg.getFingerPrint(info.keyId);
|
||||||
|
String chunks[] = matcher.group(4).split("-");
|
||||||
|
info.date = new GregorianCalendar(Integer.parseInt(chunks[0]),
|
||||||
|
Integer.parseInt(chunks[1]),
|
||||||
|
Integer.parseInt(chunks[2])).getTime();
|
||||||
|
info.userIds = new Vector<String>();
|
||||||
|
if (matcher.group(5).startsWith("*** KEY")) {
|
||||||
|
info.revoked = matcher.group(5);
|
||||||
|
} else {
|
||||||
|
String tmp = matcher.group(5).replaceAll("<.*?>", "");
|
||||||
|
tmp = Html.fromHtml(tmp).toString();
|
||||||
|
info.userIds.add(tmp);
|
||||||
|
}
|
||||||
|
if (matcher.group(6).length() > 0) {
|
||||||
|
Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6));
|
||||||
|
while (matcher2.find()) {
|
||||||
|
String tmp = matcher2.group(1).replaceAll("<.*?>", "");
|
||||||
|
tmp = Html.fromHtml(tmp).toString();
|
||||||
|
info.userIds.add(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results.add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
String get(long keyId)
|
||||||
|
throws MalformedURLException, IOException {
|
||||||
|
String url = "http://" + mHost + ":" + mPort +
|
||||||
|
"/pks/lookup?op=get&search=0x" + Apg.keyToHex(keyId);
|
||||||
|
URL realUrl = new URL(url);
|
||||||
|
URLConnection conn = realUrl.openConnection();
|
||||||
|
InputStream is = conn.getInputStream();
|
||||||
|
ByteArrayOutputStream raw = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
byte buffer[] = new byte[1 << 16];
|
||||||
|
int n = 0;
|
||||||
|
while ((n = is.read(buffer)) != -1) {
|
||||||
|
raw.write(buffer, 0, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
String data = raw.toString();
|
||||||
|
Matcher matcher = Apg.PGP_PUBLIC_KEY.matcher(data);
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ public final class Id {
|
|||||||
public static final int preferences = 0x21070008;
|
public static final int preferences = 0x21070008;
|
||||||
public static final int search = 0x21070009;
|
public static final int search = 0x21070009;
|
||||||
public static final int help = 0x21070010;
|
public static final int help = 0x21070010;
|
||||||
|
public static final int key_server = 0x21070011;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ public final class Id {
|
|||||||
public static final int create_key = 0x21070007;
|
public static final int create_key = 0x21070007;
|
||||||
public static final int edit_key = 0x21070008;
|
public static final int edit_key = 0x21070008;
|
||||||
public static final int delete_done = 0x21070009;
|
public static final int delete_done = 0x21070009;
|
||||||
|
public static final int query_done = 0x21070010;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class request {
|
public static final class request {
|
||||||
@ -78,6 +80,7 @@ public final class Id {
|
|||||||
public static final int delete_file = 0x21070012;
|
public static final int delete_file = 0x21070012;
|
||||||
public static final int deleting = 0x21070013;
|
public static final int deleting = 0x21070013;
|
||||||
public static final int help = 0x21070014;
|
public static final int help = 0x21070014;
|
||||||
|
public static final int querying = 0x21070015;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class task {
|
public static final class task {
|
||||||
@ -156,4 +159,9 @@ public final class Id {
|
|||||||
public static final int encrypted_data = 1;
|
public static final int encrypted_data = 1;
|
||||||
public static final int keys = 2;
|
public static final int keys = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class query {
|
||||||
|
public static final int search = 0x21070001;
|
||||||
|
public static final int get = 0x21070002;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -710,10 +710,7 @@ public class KeyListActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
||||||
String keyIdStr = Long.toHexString(child.keyId & 0xffffffffL);
|
String keyIdStr = Apg.getFingerPrint(child.keyId);
|
||||||
while (keyIdStr.length() < 8) {
|
|
||||||
keyIdStr = "0" + keyIdStr;
|
|
||||||
}
|
|
||||||
keyId.setText(keyIdStr);
|
keyId.setText(keyIdStr);
|
||||||
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
|
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
|
||||||
String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
|
String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
|
||||||
|
23
src/org/thialfihar/android/apg/KeyServer.java
Normal file
23
src/org/thialfihar/android/apg/KeyServer.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
public abstract class KeyServer {
|
||||||
|
static public class KeyInfo implements Serializable {
|
||||||
|
private static final long serialVersionUID = -7797972113284992662L;
|
||||||
|
Vector<String> userIds;
|
||||||
|
String revoked;
|
||||||
|
Date date;
|
||||||
|
String fingerPrint;
|
||||||
|
long keyId;
|
||||||
|
int size;
|
||||||
|
String algorithm;
|
||||||
|
}
|
||||||
|
abstract List<KeyInfo> search(String query) throws MalformedURLException, IOException;
|
||||||
|
abstract String get(long keyId) throws MalformedURLException, IOException;
|
||||||
|
}
|
246
src/org/thialfihar/android/apg/KeyServerQueryActivity.java
Normal file
246
src/org/thialfihar/android/apg/KeyServerQueryActivity.java
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
package org.thialfihar.android.apg;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import org.thialfihar.android.apg.KeyServer.KeyInfo;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.AdapterView.OnItemClickListener;
|
||||||
|
import android.widget.BaseAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.LinearLayout.LayoutParams;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
public class KeyServerQueryActivity extends BaseActivity {
|
||||||
|
private ListView mList;
|
||||||
|
private EditText mQuery;
|
||||||
|
private Button mSearch;
|
||||||
|
private KeyInfoListAdapter mAdapter;
|
||||||
|
|
||||||
|
private int mQueryType;
|
||||||
|
private String mQueryString;
|
||||||
|
private long mQueryId;
|
||||||
|
private volatile List<KeyInfo> mSearchResult;
|
||||||
|
private volatile String mKeyData;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setContentView(R.layout.key_server_query_layout);
|
||||||
|
|
||||||
|
mQuery = (EditText) findViewById(R.id.query);
|
||||||
|
mSearch = (Button) findViewById(R.id.btn_search);
|
||||||
|
mList = (ListView) findViewById(R.id.list);
|
||||||
|
mAdapter = new KeyInfoListAdapter(this);
|
||||||
|
mList.setAdapter(mAdapter);
|
||||||
|
|
||||||
|
mList.setOnItemClickListener(new OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> adapter, View view, int position, long keyId) {
|
||||||
|
get(keyId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mSearch.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
String query = mQuery.getText().toString();
|
||||||
|
search(query);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mQuery.setText("cartman");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void search(String query) {
|
||||||
|
showDialog(Id.dialog.querying);
|
||||||
|
mQueryType = Id.query.search;
|
||||||
|
mQueryString = query;
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void get(long keyId) {
|
||||||
|
showDialog(Id.dialog.querying);
|
||||||
|
mQueryType = Id.query.get;
|
||||||
|
mQueryId = keyId;
|
||||||
|
startThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
String error = null;
|
||||||
|
Bundle data = new Bundle();
|
||||||
|
Message msg = new Message();
|
||||||
|
|
||||||
|
try {
|
||||||
|
HkpKeyServer server = new HkpKeyServer("198.128.3.63");//"pool.sks-keyservers.net");
|
||||||
|
if (mQueryType == Id.query.search) {
|
||||||
|
mSearchResult = server.search(mQueryString);
|
||||||
|
} else if (mQueryType == Id.query.get) {
|
||||||
|
mKeyData = server.get(mQueryId);
|
||||||
|
}
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
error = "" + e;
|
||||||
|
} catch (IOException e) {
|
||||||
|
error = "" + e;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.putInt(Apg.EXTRA_STATUS, Id.message.done);
|
||||||
|
|
||||||
|
if (error != null) {
|
||||||
|
data.putString(Apg.EXTRA_ERROR, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.setData(data);
|
||||||
|
sendMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doneCallback(Message msg) {
|
||||||
|
super.doneCallback(msg);
|
||||||
|
|
||||||
|
removeDialog(Id.dialog.querying);
|
||||||
|
|
||||||
|
Bundle data = msg.getData();
|
||||||
|
String error = data.getString(Apg.EXTRA_ERROR);
|
||||||
|
if (error != null) {
|
||||||
|
Toast.makeText(this, getString(R.string.errorMessage, error), Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mQueryType == Id.query.search) {
|
||||||
|
if (mSearchResult != null) {
|
||||||
|
mAdapter.setKeys(mSearchResult);
|
||||||
|
}
|
||||||
|
} else if (mQueryType == Id.query.get) {
|
||||||
|
if (mKeyData != null) {
|
||||||
|
Intent intent = new Intent(this, PublicKeyListActivity.class);
|
||||||
|
intent.setAction(Apg.Intent.IMPORT);
|
||||||
|
intent.putExtra(Apg.EXTRA_TEXT, mKeyData);
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KeyInfoListAdapter extends BaseAdapter {
|
||||||
|
protected LayoutInflater mInflater;
|
||||||
|
protected Activity mActivity;
|
||||||
|
protected List<KeyInfo> mKeys;
|
||||||
|
|
||||||
|
public KeyInfoListAdapter(Activity activity) {
|
||||||
|
mActivity = activity;
|
||||||
|
mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
mKeys = new Vector<KeyInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeys(List<KeyInfo> keys) {
|
||||||
|
mKeys = keys;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasStableIds() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mKeys.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return mKeys.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return mKeys.get(position).keyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
KeyInfo keyInfo = mKeys.get(position);
|
||||||
|
|
||||||
|
View view = mInflater.inflate(R.layout.key_server_query_result_item, null);
|
||||||
|
|
||||||
|
TextView mainUserId = (TextView) view.findViewById(R.id.mainUserId);
|
||||||
|
mainUserId.setText(R.string.unknownUserId);
|
||||||
|
TextView mainUserIdRest = (TextView) view.findViewById(R.id.mainUserIdRest);
|
||||||
|
mainUserIdRest.setText("");
|
||||||
|
TextView keyId = (TextView) view.findViewById(R.id.keyId);
|
||||||
|
keyId.setText(R.string.noKey);
|
||||||
|
TextView algorithm = (TextView) view.findViewById(R.id.algorithm);
|
||||||
|
algorithm.setText("");
|
||||||
|
TextView status = (TextView) view.findViewById(R.id.status);
|
||||||
|
status.setText("");
|
||||||
|
|
||||||
|
String userId = keyInfo.userIds.get(0);
|
||||||
|
if (userId != null) {
|
||||||
|
String chunks[] = userId.split(" <", 2);
|
||||||
|
userId = chunks[0];
|
||||||
|
if (chunks.length > 1) {
|
||||||
|
mainUserIdRest.setText("<" + chunks[1]);
|
||||||
|
}
|
||||||
|
mainUserId.setText(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
keyId.setText(Apg.getFingerPrint(keyInfo.keyId));
|
||||||
|
|
||||||
|
if (mainUserIdRest.getText().length() == 0) {
|
||||||
|
mainUserIdRest.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithm.setText("" + keyInfo.size + "/" + keyInfo.algorithm);
|
||||||
|
|
||||||
|
if (keyInfo.revoked != null) {
|
||||||
|
status.setText("revoked");
|
||||||
|
} else {
|
||||||
|
status.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearLayout ll = (LinearLayout) view.findViewById(R.id.list);
|
||||||
|
if (keyInfo.userIds.size() == 1) {
|
||||||
|
ll.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
boolean first = true;
|
||||||
|
boolean second = true;
|
||||||
|
for (String uid : keyInfo.userIds) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!second) {
|
||||||
|
View sep = new View(mActivity);
|
||||||
|
sep.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 1));
|
||||||
|
sep.setBackgroundResource(android.R.drawable.divider_horizontal_dark);
|
||||||
|
ll.addView(sep);
|
||||||
|
}
|
||||||
|
TextView uidView = (TextView) mInflater.inflate(R.layout.key_server_query_result_user_id, null);
|
||||||
|
uidView.setText(uid);
|
||||||
|
ll.addView(uidView);
|
||||||
|
second = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -295,9 +295,11 @@ public class MainActivity extends BaseActivity {
|
|||||||
.setIcon(android.R.drawable.ic_menu_add);
|
.setIcon(android.R.drawable.ic_menu_add);
|
||||||
menu.add(2, Id.menu.option.preferences, 3, R.string.menu_preferences)
|
menu.add(2, Id.menu.option.preferences, 3, R.string.menu_preferences)
|
||||||
.setIcon(android.R.drawable.ic_menu_preferences);
|
.setIcon(android.R.drawable.ic_menu_preferences);
|
||||||
menu.add(2, Id.menu.option.about, 4, R.string.menu_about)
|
menu.add(2, Id.menu.option.key_server, 4, R.string.menu_keyServer)
|
||||||
|
.setIcon(android.R.drawable.ic_menu_search);
|
||||||
|
menu.add(3, Id.menu.option.about, 5, R.string.menu_about)
|
||||||
.setIcon(android.R.drawable.ic_menu_info_details);
|
.setIcon(android.R.drawable.ic_menu_info_details);
|
||||||
menu.add(22, Id.menu.option.help, 4, R.string.menu_help)
|
menu.add(3, Id.menu.option.help, 6, R.string.menu_help)
|
||||||
.setIcon(android.R.drawable.ic_menu_help);
|
.setIcon(android.R.drawable.ic_menu_help);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -325,6 +327,11 @@ public class MainActivity extends BaseActivity {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Id.menu.option.key_server: {
|
||||||
|
startActivity(new Intent(this, KeyServerQueryActivity.class));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
|
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
|
||||||
keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
|
keyId.setText(Apg.getFingerPrint(masterKeyId));
|
||||||
|
|
||||||
if (mainUserIdRest.getText().length() == 0) {
|
if (mainUserIdRest.getText().length() == 0) {
|
||||||
mainUserIdRest.setVisibility(View.GONE);
|
mainUserIdRest.setVisibility(View.GONE);
|
||||||
|
@ -146,7 +146,7 @@ public class SelectSecretKeyListAdapter extends BaseAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
|
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
|
||||||
keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
|
keyId.setText(Apg.getFingerPrint(masterKeyId));
|
||||||
|
|
||||||
if (mainUserIdRest.getText().length() == 0) {
|
if (mainUserIdRest.getText().length() == 0) {
|
||||||
mainUserIdRest.setVisibility(View.GONE);
|
mainUserIdRest.setVisibility(View.GONE);
|
||||||
|
@ -141,14 +141,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mAlgorithm.setText(Apg.getAlgorithmInfo(key));
|
mAlgorithm.setText(Apg.getAlgorithmInfo(key));
|
||||||
String keyId1Str = Long.toHexString((key.getKeyID() >> 32) & 0xffffffffL);
|
String keyId1Str = Apg.getFingerPrint(key.getKeyID());
|
||||||
while (keyId1Str.length() < 8) {
|
String keyId2Str = Apg.getFingerPrint(key.getKeyID() >> 32);
|
||||||
keyId1Str = "0" + keyId1Str;
|
|
||||||
}
|
|
||||||
String keyId2Str = Long.toHexString(key.getKeyID() & 0xffffffffL);
|
|
||||||
while (keyId2Str.length() < 8) {
|
|
||||||
keyId2Str = "0" + keyId2Str;
|
|
||||||
}
|
|
||||||
mKeyId.setText(keyId1Str + " " + keyId2Str);
|
mKeyId.setText(keyId1Str + " " + keyId2Str);
|
||||||
|
|
||||||
Vector<Choice> choices = new Vector<Choice>();
|
Vector<Choice> choices = new Vector<Choice>();
|
||||||
|
Loading…
Reference in New Issue
Block a user