aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml6
-rw-r--r--res/layout/key_server_query_layout.xml48
-rw-r--r--res/layout/key_server_query_result_item.xml97
-rw-r--r--res/layout/key_server_query_result_user_id.xml26
-rw-r--r--res/values/strings.xml4
-rw-r--r--src/org/thialfihar/android/apg/Apg.java23
-rw-r--r--src/org/thialfihar/android/apg/BaseActivity.java8
-rw-r--r--src/org/thialfihar/android/apg/DecryptActivity.java2
-rw-r--r--src/org/thialfihar/android/apg/EditKeyActivity.java1
-rw-r--r--src/org/thialfihar/android/apg/HkpKeyServer.java119
-rw-r--r--src/org/thialfihar/android/apg/Id.java8
-rw-r--r--src/org/thialfihar/android/apg/KeyListActivity.java5
-rw-r--r--src/org/thialfihar/android/apg/KeyServer.java23
-rw-r--r--src/org/thialfihar/android/apg/KeyServerQueryActivity.java246
-rw-r--r--src/org/thialfihar/android/apg/MainActivity.java13
-rw-r--r--src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java2
-rw-r--r--src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java2
-rw-r--r--src/org/thialfihar/android/apg/ui/widget/KeyEditor.java10
18 files changed, 624 insertions, 19 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a49148f98..25e824962 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -178,6 +178,11 @@
android:configChanges="keyboardHidden|orientation|keyboard"/>
<activity
+ android:name=".KeyServerQueryActivity"
+ android:label="@string/title_keyServerQuery"
+ android:configChanges="keyboardHidden|orientation|keyboard"/>
+
+ <activity
android:name=".PreferencesActivity"
android:label="@string/title_preferences"
android:configChanges="keyboardHidden|orientation|keyboard"/>
@@ -201,5 +206,6 @@
<uses-permission android:name="com.google.android.gm.permission.READ_GMAIL" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.fsck.k9.permission.READ_ATTACHMENT" />
+ <uses-permission android:name="android.permission.INTERNET"/>
</manifest>
diff --git a/res/layout/key_server_query_layout.xml b/res/layout/key_server_query_layout.xml
new file mode 100644
index 000000000..167d02fef
--- /dev/null
+++ b/res/layout/key_server_query_layout.xml
@@ -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>
diff --git a/res/layout/key_server_query_result_item.xml b/res/layout/key_server_query_result_item.xml
new file mode 100644
index 000000000..29c8b88f4
--- /dev/null
+++ b/res/layout/key_server_query_result_item.xml
@@ -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="&lt;user@somewhere.com&gt;"
+ 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>
diff --git a/res/layout/key_server_query_result_user_id.xml b/res/layout/key_server_query_result_user_id.xml
new file mode 100644
index 000000000..9d3a4a1ab
--- /dev/null
+++ b/res/layout/key_server_query_result_user_id.xml
@@ -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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e7a233b3e..0451ec00e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -40,6 +40,7 @@
<string name="title_exportKeys">Export Keys</string>
<string name="title_keyNotFound">Key Not Found</string>
<string name="title_help">Getting Started</string>
+ <string name="title_keyServerQuery">Query Key Server</string>
<!-- section_lowerCase: capitalized words, no punctuation -->
<string name="section_userIds">User IDs</string>
@@ -70,6 +71,7 @@
<string name="btn_clearFilter">Clear Filter</string>
<string name="btn_changePassPhrase">Change Pass Phrase</string>
<string name="btn_setPassPhrase">Set Pass Phrase</string>
+ <string name="btn_search">Search</string>
<!-- menu_lowerCase: capitalized words, no punctuation -->
<string name="menu_about">About</string>
@@ -86,6 +88,7 @@
<string name="menu_editKey">Edit Key</string>
<string name="menu_search">Search</string>
<string name="menu_help">Help</string>
+ <string name="menu_keyServer">Key Server</string>
<!-- label_lowerCase: capitalized words, no punctuation -->
<string name="label_sign">Sign</string>
@@ -255,6 +258,7 @@
<string name="progress_decompressingData">decompressing data...</string>
<string name="progress_verifyingIntegrity">verifying integrity...</string>
<string name="progress_deletingSecurely">deleting \'%s\' securely...</string>
+ <string name="progress_querying">querying...</string>
<!-- permission strings -->
<string name="permission_read_key_details_label">Read key details from APG.</string>
diff --git a/src/org/thialfihar/android/apg/Apg.java b/src/org/thialfihar/android/apg/Apg.java
index cc8fae4c7..125001024 100644
--- a/src/org/thialfihar/android/apg/Apg.java
+++ b/src/org/thialfihar/android/apg/Apg.java
@@ -191,6 +191,10 @@ public class Apg {
Pattern.compile(".*?(-----BEGIN PGP SIGNED MESSAGE-----.*?-----BEGIN PGP SIGNATURE-----.*?-----END PGP SIGNATURE-----).*",
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 =
new HashMap<Long, CachedPassPhrase>();
private static String mEditPassPhrase = null;
@@ -1006,6 +1010,25 @@ public class Apg {
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) {
mDatabase.deleteKeyRing(keyRingId);
}
diff --git a/src/org/thialfihar/android/apg/BaseActivity.java b/src/org/thialfihar/android/apg/BaseActivity.java
index 9c40efb4f..9f1c7a2ae 100644
--- a/src/org/thialfihar/android/apg/BaseActivity.java
+++ b/src/org/thialfihar/android/apg/BaseActivity.java
@@ -157,6 +157,13 @@ public class BaseActivity extends Activity
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: {
break;
}
@@ -362,6 +369,7 @@ public class BaseActivity extends Activity
case Id.message.import_done: // intentionally no break
case Id.message.export_done: // intentionally no break
+ case Id.message.query_done: // intentionally no break
case Id.message.done: {
mProgressDialog = null;
doneCallback(msg);
diff --git a/src/org/thialfihar/android/apg/DecryptActivity.java b/src/org/thialfihar/android/apg/DecryptActivity.java
index a7eb31248..a23e58ca4 100644
--- a/src/org/thialfihar/android/apg/DecryptActivity.java
+++ b/src/org/thialfihar/android/apg/DecryptActivity.java
@@ -584,7 +584,7 @@ public class DecryptActivity extends BaseActivity {
if (data.getBoolean(Apg.EXTRA_SIGNATURE)) {
String userId = data.getString(Apg.EXTRA_SIGNATURE_USER_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) {
userId = getResources().getString(R.string.unknownUserId);
}
diff --git a/src/org/thialfihar/android/apg/EditKeyActivity.java b/src/org/thialfihar/android/apg/EditKeyActivity.java
index 3fa5a7552..93e048e42 100644
--- a/src/org/thialfihar/android/apg/EditKeyActivity.java
+++ b/src/org/thialfihar/android/apg/EditKeyActivity.java
@@ -39,7 +39,6 @@ import android.os.Bundle;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
diff --git a/src/org/thialfihar/android/apg/HkpKeyServer.java b/src/org/thialfihar/android/apg/HkpKeyServer.java
new file mode 100644
index 000000000..ce65e1da2
--- /dev/null
+++ b/src/org/thialfihar/android/apg/HkpKeyServer.java
@@ -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 &lt;joerg@joergrunge.de&gt;</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;
+ }
+}
diff --git a/src/org/thialfihar/android/apg/Id.java b/src/org/thialfihar/android/apg/Id.java
index cc256caee..ce67444cd 100644
--- a/src/org/thialfihar/android/apg/Id.java
+++ b/src/org/thialfihar/android/apg/Id.java
@@ -35,6 +35,7 @@ public final class Id {
public static final int preferences = 0x21070008;
public static final int search = 0x21070009;
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 edit_key = 0x21070008;
public static final int delete_done = 0x21070009;
+ public static final int query_done = 0x21070010;
}
public static final class request {
@@ -78,6 +80,7 @@ public final class Id {
public static final int delete_file = 0x21070012;
public static final int deleting = 0x21070013;
public static final int help = 0x21070014;
+ public static final int querying = 0x21070015;
}
public static final class task {
@@ -156,4 +159,9 @@ public final class Id {
public static final int encrypted_data = 1;
public static final int keys = 2;
}
+
+ public static final class query {
+ public static final int search = 0x21070001;
+ public static final int get = 0x21070002;
+ }
}
diff --git a/src/org/thialfihar/android/apg/KeyListActivity.java b/src/org/thialfihar/android/apg/KeyListActivity.java
index 6f5442502..fbe74a995 100644
--- a/src/org/thialfihar/android/apg/KeyListActivity.java
+++ b/src/org/thialfihar/android/apg/KeyListActivity.java
@@ -710,10 +710,7 @@ public class KeyListActivity extends BaseActivity {
}
TextView keyId = (TextView) view.findViewById(R.id.keyId);
- String keyIdStr = Long.toHexString(child.keyId & 0xffffffffL);
- while (keyIdStr.length() < 8) {
- keyIdStr = "0" + keyIdStr;
- }
+ String keyIdStr = Apg.getFingerPrint(child.keyId);
keyId.setText(keyIdStr);
TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails);
String algorithmStr = Apg.getAlgorithmInfo(child.algorithm, child.keySize);
diff --git a/src/org/thialfihar/android/apg/KeyServer.java b/src/org/thialfihar/android/apg/KeyServer.java
new file mode 100644
index 000000000..25ff26144
--- /dev/null
+++ b/src/org/thialfihar/android/apg/KeyServer.java
@@ -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;
+}
diff --git a/src/org/thialfihar/android/apg/KeyServerQueryActivity.java b/src/org/thialfihar/android/apg/KeyServerQueryActivity.java
new file mode 100644
index 000000000..e7f393756
--- /dev/null
+++ b/src/org/thialfihar/android/apg/KeyServerQueryActivity.java
@@ -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;
+ }
+ }
+}
diff --git a/src/org/thialfihar/android/apg/MainActivity.java b/src/org/thialfihar/android/apg/MainActivity.java
index 310ae062a..c459ad94a 100644
--- a/src/org/thialfihar/android/apg/MainActivity.java
+++ b/src/org/thialfihar/android/apg/MainActivity.java
@@ -292,12 +292,14 @@ public class MainActivity extends BaseActivity {
menu.add(0, Id.menu.option.manage_secret_keys, 1, R.string.menu_manageSecretKeys)
.setIcon(android.R.drawable.ic_menu_manage);
menu.add(1, Id.menu.option.create, 2, R.string.menu_addAccount)
- .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)
.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);
- 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);
return true;
}
@@ -325,6 +327,11 @@ public class MainActivity extends BaseActivity {
return true;
}
+ case Id.menu.option.key_server: {
+ startActivity(new Intent(this, KeyServerQueryActivity.class));
+ return true;
+ }
+
default: {
return super.onOptionsItemSelected(item);
}
diff --git a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
index d7359dbf2..e1277a4b7 100644
--- a/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
+++ b/src/org/thialfihar/android/apg/SelectPublicKeyListAdapter.java
@@ -187,7 +187,7 @@ public class SelectPublicKeyListAdapter extends BaseAdapter {
}
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
- keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
+ keyId.setText(Apg.getFingerPrint(masterKeyId));
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
diff --git a/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
index 440461aca..979518e68 100644
--- a/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
+++ b/src/org/thialfihar/android/apg/SelectSecretKeyListAdapter.java
@@ -146,7 +146,7 @@ public class SelectSecretKeyListAdapter extends BaseAdapter {
}
long masterKeyId = mCursor.getLong(1); // MASTER_KEY_ID
- keyId.setText("" + Long.toHexString(masterKeyId & 0xffffffffL));
+ keyId.setText(Apg.getFingerPrint(masterKeyId));
if (mainUserIdRest.getText().length() == 0) {
mainUserIdRest.setVisibility(View.GONE);
diff --git a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
index 1bc7515f5..e61451fc0 100644
--- a/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
+++ b/src/org/thialfihar/android/apg/ui/widget/KeyEditor.java
@@ -141,14 +141,8 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener {
}
mAlgorithm.setText(Apg.getAlgorithmInfo(key));
- String keyId1Str = Long.toHexString((key.getKeyID() >> 32) & 0xffffffffL);
- while (keyId1Str.length() < 8) {
- keyId1Str = "0" + keyId1Str;
- }
- String keyId2Str = Long.toHexString(key.getKeyID() & 0xffffffffL);
- while (keyId2Str.length() < 8) {
- keyId2Str = "0" + keyId2Str;
- }
+ String keyId1Str = Apg.getFingerPrint(key.getKeyID());
+ String keyId2Str = Apg.getFingerPrint(key.getKeyID() >> 32);
mKeyId.setText(keyId1Str + " " + keyId2Str);
Vector<Choice> choices = new Vector<Choice>();