comparison src/com/five_ten_sg/connectbot/PubkeyListActivity.java @ 0:0ce5cc452d02

initial version
author Carl Byington <carl@five-ten-sg.com>
date Thu, 22 May 2014 10:41:19 -0700
parents
children 265a4733edcb
comparison
equal deleted inserted replaced
-1:000000000000 0:0ce5cc452d02
1 /*
2 * ConnectBot: simple, powerful, open-source SSH client for Android
3 * Copyright 2007 Kenny Root, Jeffrey Sharkey
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package com.five_ten_sg.connectbot;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.security.KeyPair;
27 import java.security.PrivateKey;
28 import java.security.PublicKey;
29 import java.util.EventListener;
30 import java.util.List;
31
32 import com.five_ten_sg.connectbot.bean.PubkeyBean;
33 import com.five_ten_sg.connectbot.service.TerminalManager;
34 import com.five_ten_sg.connectbot.util.FileChooser;
35 import com.five_ten_sg.connectbot.util.FileChooserCallback;
36 import com.five_ten_sg.connectbot.util.PubkeyDatabase;
37 import com.five_ten_sg.connectbot.util.PubkeyUtils;
38 import android.app.AlertDialog;
39 import android.app.ListActivity;
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.content.DialogInterface;
43 import android.content.Intent;
44 import android.content.ServiceConnection;
45 import android.os.Bundle;
46 import android.os.Environment;
47 import android.os.IBinder;
48 import android.text.ClipboardManager;
49 import android.util.Log;
50 import android.view.ContextMenu;
51 import android.view.LayoutInflater;
52 import android.view.Menu;
53 import android.view.MenuItem;
54 import android.view.MenuItem.OnMenuItemClickListener;
55 import android.view.View;
56 import android.view.ViewGroup;
57 import android.widget.AdapterView;
58 import android.widget.AdapterView.OnItemClickListener;
59 import android.widget.ArrayAdapter;
60 import android.widget.EditText;
61 import android.widget.ImageView;
62 import android.widget.TableRow;
63 import android.widget.TextView;
64 import android.widget.Toast;
65
66 import com.trilead.ssh2.crypto.Base64;
67 import com.trilead.ssh2.crypto.PEMDecoder;
68 import com.trilead.ssh2.crypto.PEMStructure;
69
70 /**
71 * List public keys in database by nickname and describe their properties. Allow users to import,
72 * generate, rename, and delete key pairs.
73 *
74 * @author Kenny Root
75 */
76 public class PubkeyListActivity extends ListActivity implements EventListener, FileChooserCallback {
77
78 public final static String TAG = "ConnectBot.PubkeyListActivity";
79
80 private static final int MAX_KEYFILE_SIZE = 8192;
81 private static final int KEYTYPE_PUBLIC = 0;
82 private static final int KEYTYPE_PRIVATE = 1;
83
84 protected PubkeyDatabase pubkeydb;
85 private List<PubkeyBean> pubkeys;
86
87 protected ClipboardManager clipboard;
88
89 protected LayoutInflater inflater = null;
90
91 protected TerminalManager bound = null;
92
93 private MenuItem onstartToggle = null;
94 private MenuItem confirmUse = null;
95
96 private ServiceConnection connection = new ServiceConnection() {
97 public void onServiceConnected(ComponentName className, IBinder service) {
98 bound = ((TerminalManager.TerminalBinder) service).getService();
99 // update our listview binder to find the service
100 updateList();
101 }
102 public void onServiceDisconnected(ComponentName className) {
103 bound = null;
104 updateList();
105 }
106 };
107
108 @Override
109 public void onStart() {
110 super.onStart();
111 bindService(new Intent(this, TerminalManager.class), connection, Context.BIND_AUTO_CREATE);
112
113 if (pubkeydb == null)
114 pubkeydb = new PubkeyDatabase(this);
115 }
116
117 @Override
118 public void onStop() {
119 super.onStop();
120 unbindService(connection);
121
122 if (pubkeydb != null) {
123 pubkeydb.close();
124 pubkeydb = null;
125 }
126 }
127
128 @Override
129 public void onCreate(Bundle icicle) {
130 super.onCreate(icicle);
131 setContentView(R.layout.act_pubkeylist);
132 this.setTitle(String.format("%s: %s",
133 getResources().getText(R.string.app_name),
134 getResources().getText(R.string.title_pubkey_list)));
135 // connect with hosts database and populate list
136 pubkeydb = new PubkeyDatabase(this);
137 updateList();
138 registerForContextMenu(getListView());
139 getListView().setOnItemClickListener(new OnItemClickListener() {
140 public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
141 PubkeyBean pubkey = (PubkeyBean) getListView().getItemAtPosition(position);
142 boolean loaded = bound.isKeyLoaded(pubkey.getNickname());
143
144 // handle toggling key in-memory on/off
145 if (loaded) {
146 bound.removeKey(pubkey.getNickname());
147 updateList();
148 }
149 else {
150 handleAddKey(pubkey);
151 }
152 }
153 });
154 clipboard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
155 inflater = LayoutInflater.from(this);
156 }
157
158 /**
159 * Read given file into memory as <code>byte[]</code>.
160 */
161 protected static byte[] readRaw(File file) throws Exception {
162 InputStream is = new FileInputStream(file);
163 ByteArrayOutputStream os = new ByteArrayOutputStream();
164 int bytesRead;
165 byte[] buffer = new byte[1024];
166
167 while ((bytesRead = is.read(buffer)) != -1) {
168 os.write(buffer, 0, bytesRead);
169 }
170
171 os.flush();
172 os.close();
173 is.close();
174 return os.toByteArray();
175 }
176
177 @Override
178 public boolean onCreateOptionsMenu(Menu menu) {
179 super.onCreateOptionsMenu(menu);
180 MenuItem generatekey = menu.add(R.string.pubkey_generate);
181 generatekey.setIcon(android.R.drawable.ic_menu_manage);
182 generatekey.setIntent(new Intent(PubkeyListActivity.this, GeneratePubkeyActivity.class));
183 MenuItem importkey = menu.add(R.string.pubkey_import);
184 importkey.setIcon(android.R.drawable.ic_menu_upload);
185 importkey.setOnMenuItemClickListener(new OnMenuItemClickListener() {
186 public boolean onMenuItemClick(MenuItem item) {
187 FileChooser.selectFile(PubkeyListActivity.this, PubkeyListActivity.this,
188 FileChooser.REQUEST_CODE_SELECT_FILE,
189 getString(R.string.file_chooser_select_file, getString(R.string.select_for_key_import)));
190 return true;
191 }
192 });
193 return true;
194 }
195
196 protected void handleAddKey(final PubkeyBean pubkey) {
197 if (pubkey.isEncrypted()) {
198 final View view = inflater.inflate(R.layout.dia_password, null);
199 final EditText passwordField = (EditText)view.findViewById(android.R.id.text1);
200 new AlertDialog.Builder(PubkeyListActivity.this)
201 .setView(view)
202 .setPositiveButton(R.string.pubkey_unlock, new DialogInterface.OnClickListener() {
203 public void onClick(DialogInterface dialog, int which) {
204 handleAddKey(pubkey, passwordField.getText().toString());
205 }
206 })
207 .setNegativeButton(android.R.string.cancel, null).create().show();
208 }
209 else {
210 handleAddKey(pubkey, null);
211 }
212 }
213
214 protected void handleAddKey(PubkeyBean keybean, String password) {
215 KeyPair pair = null;
216
217 if (PubkeyDatabase.KEY_TYPE_IMPORTED.equals(keybean.getType())) {
218 // load specific key using pem format
219 try {
220 pair = PEMDecoder.decode(new String(keybean.getPrivateKey()).toCharArray(), password);
221 }
222 catch (Exception e) {
223 String message = getResources().getString(R.string.pubkey_failed_add, keybean.getNickname());
224 Log.e(TAG, message, e);
225 Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG).show();
226 }
227 }
228 else {
229 // load using internal generated format
230 try {
231 PrivateKey privKey = PubkeyUtils.decodePrivate(keybean.getPrivateKey(), keybean.getType(), password);
232 PublicKey pubKey = PubkeyUtils.decodePublic(keybean.getPublicKey(), keybean.getType());
233 Log.d(TAG, "Unlocked key " + PubkeyUtils.formatKey(pubKey));
234 pair = new KeyPair(pubKey, privKey);
235 }
236 catch (Exception e) {
237 String message = getResources().getString(R.string.pubkey_failed_add, keybean.getNickname());
238 Log.e(TAG, message, e);
239 Toast.makeText(PubkeyListActivity.this, message, Toast.LENGTH_LONG).show();
240 return;
241 }
242 }
243
244 if (pair == null) return;
245
246 Log.d(TAG, String.format("Unlocked key '%s'", keybean.getNickname()));
247 // save this key in memory
248 bound.addKey(keybean, pair, true);
249 updateList();
250 }
251
252 @Override
253 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
254 // Create menu to handle deleting and editing pubkey
255 AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
256 final PubkeyBean pubkey = (PubkeyBean) getListView().getItemAtPosition(info.position);
257 menu.setHeaderTitle(pubkey.getNickname());
258 // TODO: option load/unload key from in-memory list
259 // prompt for password as needed for passworded keys
260 // cant change password or clipboard imported keys
261 final boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType());
262 final boolean loaded = bound.isKeyLoaded(pubkey.getNickname());
263 MenuItem load = menu.add(loaded ? R.string.pubkey_memory_unload : R.string.pubkey_memory_load);
264 load.setOnMenuItemClickListener(new OnMenuItemClickListener() {
265 public boolean onMenuItemClick(MenuItem item) {
266 if (loaded) {
267 bound.removeKey(pubkey.getNickname());
268 updateList();
269 }
270 else {
271 handleAddKey(pubkey);
272 //bound.addKey(nickname, trileadKey);
273 }
274
275 return true;
276 }
277 });
278 onstartToggle = menu.add(R.string.pubkey_load_on_start);
279 onstartToggle.setVisible(!pubkey.isEncrypted());
280 onstartToggle.setCheckable(true);
281 onstartToggle.setChecked(pubkey.isStartup());
282 onstartToggle.setOnMenuItemClickListener(new OnMenuItemClickListener() {
283 public boolean onMenuItemClick(MenuItem item) {
284 // toggle onstart status
285 pubkey.setStartup(!pubkey.isStartup());
286 pubkeydb.savePubkey(pubkey);
287 updateList();
288 return true;
289 }
290 });
291 MenuItem changePassword = menu.add(R.string.pubkey_change_password);
292 changePassword.setVisible(!imported);
293 changePassword.setOnMenuItemClickListener(new OnMenuItemClickListener() {
294 public boolean onMenuItemClick(MenuItem item) {
295 final View changePasswordView = inflater.inflate(R.layout.dia_changepassword, null, false);
296 ((TableRow)changePasswordView.findViewById(R.id.old_password_prompt))
297 .setVisibility(pubkey.isEncrypted() ? View.VISIBLE : View.GONE);
298 new AlertDialog.Builder(PubkeyListActivity.this)
299 .setView(changePasswordView)
300 .setPositiveButton(R.string.button_change, new DialogInterface.OnClickListener() {
301 public void onClick(DialogInterface dialog, int which) {
302 String oldPassword = ((EditText)changePasswordView.findViewById(R.id.old_password)).getText().toString();
303 String password1 = ((EditText)changePasswordView.findViewById(R.id.password1)).getText().toString();
304 String password2 = ((EditText)changePasswordView.findViewById(R.id.password2)).getText().toString();
305
306 if (!password1.equals(password2)) {
307 new AlertDialog.Builder(PubkeyListActivity.this)
308 .setMessage(R.string.alert_passwords_do_not_match_msg)
309 .setPositiveButton(android.R.string.ok, null)
310 .create().show();
311 return;
312 }
313
314 try {
315 if (!pubkey.changePassword(oldPassword, password1))
316 new AlertDialog.Builder(PubkeyListActivity.this)
317 .setMessage(R.string.alert_wrong_password_msg)
318 .setPositiveButton(android.R.string.ok, null)
319 .create().show();
320 else {
321 pubkeydb.savePubkey(pubkey);
322 updateList();
323 }
324 }
325 catch (Exception e) {
326 Log.e(TAG, "Could not change private key password", e);
327 new AlertDialog.Builder(PubkeyListActivity.this)
328 .setMessage(R.string.alert_key_corrupted_msg)
329 .setPositiveButton(android.R.string.ok, null)
330 .create().show();
331 }
332 }
333 })
334 .setNegativeButton(android.R.string.cancel, null).create().show();
335 return true;
336 }
337 });
338 confirmUse = menu.add(R.string.pubkey_confirm_use);
339 confirmUse.setCheckable(true);
340 confirmUse.setChecked(pubkey.isConfirmUse());
341 confirmUse.setOnMenuItemClickListener(new OnMenuItemClickListener() {
342 public boolean onMenuItemClick(MenuItem item) {
343 // toggle confirm use
344 pubkey.setConfirmUse(!pubkey.isConfirmUse());
345 pubkeydb.savePubkey(pubkey);
346 updateList();
347 return true;
348 }
349 });
350 MenuItem copyPublicToClipboard = menu.add(R.string.pubkey_copy_public);
351 copyPublicToClipboard.setVisible(!imported);
352 copyPublicToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() {
353 public boolean onMenuItemClick(MenuItem item) {
354 try {
355 PublicKey pk = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType());
356 String openSSHPubkey = PubkeyUtils.convertToOpenSSHFormat(pk, pubkey.getNickname());
357 clipboard.setText(openSSHPubkey);
358 }
359 catch (Exception e) {
360 e.printStackTrace();
361 }
362
363 return true;
364 }
365 });
366 MenuItem exportPublic = menu.add(R.string.pubkey_export_public);
367 exportPublic.setVisible(!imported);
368 exportPublic.setOnMenuItemClickListener(new OnMenuItemClickListener() {
369 public boolean onMenuItemClick(MenuItem item) {
370 String keyString = PubkeyUtils.getPubkeyString(pubkey);
371
372 if (keyString != null)
373 saveKeyToFile(keyString, pubkey.getNickname(), KEYTYPE_PUBLIC);
374
375 return true;
376 }
377 });
378 MenuItem copyPrivateToClipboard = menu.add(R.string.pubkey_copy_private);
379 copyPrivateToClipboard.setVisible(!pubkey.isEncrypted() || imported);
380 copyPrivateToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() {
381 public boolean onMenuItemClick(MenuItem item) {
382 String keyString = PubkeyUtils.getPrivkeyString(pubkey, null);
383
384 if (keyString != null)
385 clipboard.setText(keyString);
386
387 return true;
388 }
389 });
390 MenuItem exportPrivate = menu.add(R.string.pubkey_export_private);
391 exportPrivate.setVisible(!pubkey.isEncrypted() || imported);
392 exportPrivate.setOnMenuItemClickListener(new OnMenuItemClickListener() {
393 public boolean onMenuItemClick(MenuItem item) {
394 /* if (pubkey.isEncrypted()) {
395 final View view = inflater.inflate(R.layout.dia_password, null);
396 final EditText passwordField = (EditText)view.findViewById(android.R.id.text1);
397
398 new AlertDialog.Builder(PubkeyListActivity.this)
399 .setView(view)
400 .setPositiveButton(R.string.pubkey_unlock, new DialogInterface.OnClickListener() {
401 public void onClick(DialogInterface dialog, int which) {
402 String keyString = PubkeyUtils.getPrivkeyString(pubkey, passwordField.getText().toString());
403 if (keyString != null)
404 saveKeyToFile(keyString, pubkey.getNickname(), KEYTYPE_PRIVATE);
405 }
406 })
407 .setNegativeButton(android.R.string.cancel, null).create().show();
408 } else { */
409 String keyString = PubkeyUtils.getPrivkeyString(pubkey, null);
410
411 if (keyString != null)
412 saveKeyToFile(keyString, pubkey.getNickname(), KEYTYPE_PRIVATE);
413
414 // }
415 return true;
416 }
417 });
418 MenuItem delete = menu.add(R.string.pubkey_delete);
419 delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
420 public boolean onMenuItemClick(MenuItem item) {
421 // prompt user to make sure they really want this
422 new AlertDialog.Builder(PubkeyListActivity.this)
423 .setMessage(getString(R.string.delete_message, pubkey.getNickname()))
424 .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() {
425 public void onClick(DialogInterface dialog, int which) {
426 // dont forget to remove from in-memory
427 if (loaded)
428 bound.removeKey(pubkey.getNickname());
429
430 // delete from backend database and update gui
431 pubkeydb.deletePubkey(pubkey);
432 updateList();
433 }
434 })
435 .setNegativeButton(R.string.delete_neg, null).create().show();
436 return true;
437 }
438 });
439 }
440
441 protected void updateList() {
442 if (pubkeydb == null) return;
443
444 pubkeys = pubkeydb.allPubkeys();
445 PubkeyAdapter adapter = new PubkeyAdapter(this, pubkeys);
446 this.setListAdapter(adapter);
447 }
448
449 @Override
450 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
451 super.onActivityResult(requestCode, resultCode, intent);
452
453 switch (requestCode) {
454 case FileChooser.REQUEST_CODE_SELECT_FILE:
455 if (resultCode == RESULT_OK && intent != null) {
456 File file = FileChooser.getSelectedFile(intent);
457
458 if (file != null)
459 readKeyFromFile(file);
460 }
461
462 break;
463 }
464 }
465
466 /**
467 * @param name
468 */
469 private void readKeyFromFile(File file) {
470 PubkeyBean pubkey = new PubkeyBean();
471 // find the exact file selected
472 pubkey.setNickname(file.getName());
473
474 if (file.length() > MAX_KEYFILE_SIZE) {
475 Toast.makeText(PubkeyListActivity.this,
476 R.string.pubkey_import_parse_problem,
477 Toast.LENGTH_LONG).show();
478 return;
479 }
480
481 // parse the actual key once to check if its encrypted
482 // then save original file contents into our database
483 try {
484 byte[] raw = readRaw(file);
485 String data = new String(raw);
486
487 if (data.startsWith(PubkeyUtils.PKCS8_START)) {
488 int start = data.indexOf(PubkeyUtils.PKCS8_START) + PubkeyUtils.PKCS8_START.length();
489 int end = data.indexOf(PubkeyUtils.PKCS8_END);
490
491 if (end > start) {
492 char[] encoded = data.substring(start, end - 1).toCharArray();
493 Log.d(TAG, "encoded: " + new String(encoded));
494 byte[] decoded = Base64.decode(encoded);
495 KeyPair kp = PubkeyUtils.recoverKeyPair(decoded);
496 pubkey.setType(kp.getPrivate().getAlgorithm());
497 pubkey.setPrivateKey(kp.getPrivate().getEncoded());
498 pubkey.setPublicKey(kp.getPublic().getEncoded());
499 }
500 else {
501 Log.e(TAG, "Problem parsing PKCS#8 file; corrupt?");
502 Toast.makeText(PubkeyListActivity.this,
503 R.string.pubkey_import_parse_problem,
504 Toast.LENGTH_LONG).show();
505 }
506 }
507 else {
508 PEMStructure struct = PEMDecoder.parsePEM(new String(raw).toCharArray());
509 pubkey.setEncrypted(PEMDecoder.isPEMEncrypted(struct));
510 pubkey.setType(PubkeyDatabase.KEY_TYPE_IMPORTED);
511 pubkey.setPrivateKey(raw);
512 }
513
514 // write new value into database
515 if (pubkeydb == null)
516 pubkeydb = new PubkeyDatabase(this);
517
518 pubkeydb.savePubkey(pubkey);
519 updateList();
520 }
521 catch (Exception e) {
522 Log.e(TAG, "Problem parsing imported private key", e);
523 Toast.makeText(PubkeyListActivity.this, R.string.pubkey_import_parse_problem, Toast.LENGTH_LONG).show();
524 }
525 }
526
527 private void saveKeyToFile(final String keyString, final String nickName, int keyType) {
528 final int titleId, messageId, successId, errorId;
529 final String errorString;
530
531 if (keyType == KEYTYPE_PRIVATE) {
532 titleId = R.string.pubkey_private_save_as;
533 messageId = R.string.pubkey_private_save_as_desc;
534 successId = R.string.pubkey_private_export_success;
535 errorId = R.string.pubkey_private_export_problem;
536 errorString = "Error exporting private key";
537 }
538 else {
539 titleId = R.string.pubkey_public_save_as;
540 messageId = R.string.pubkey_public_save_as_desc;
541 errorId = R.string.pubkey_public_export_problem;
542 successId = R.string.pubkey_public_export_success;
543 errorString = "Error exporting public key";
544 }
545
546 final String sdcard = Environment.getExternalStorageDirectory().toString();
547 final EditText fileName = new EditText(PubkeyListActivity.this);
548 fileName.setSingleLine();
549
550 if (nickName != null) {
551 if (keyType == KEYTYPE_PRIVATE)
552 fileName.setText(sdcard + "/" + nickName.trim());
553 else
554 fileName.setText(sdcard + "/" + nickName.trim() + ".pub");
555 }
556
557 new AlertDialog.Builder(PubkeyListActivity.this)
558 .setTitle(titleId)
559 .setMessage(messageId)
560 .setView(fileName)
561 .setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
562 public void onClick(DialogInterface dialog, int whichButton) {
563 File keyFile = new File(fileName.getText().toString());
564
565 if (!keyFile.exists()) {
566 try {
567 keyFile.createNewFile();
568 }
569 catch (IOException e) {
570 Log.e(TAG, errorString);
571 Toast.makeText(PubkeyListActivity.this,
572 errorId,
573 Toast.LENGTH_LONG).show();
574 return;
575 }
576 }
577
578 FileOutputStream fout = null;
579
580 try {
581 fout = new FileOutputStream(keyFile);
582 fout.write(keyString.getBytes(), 0, keyString.getBytes().length);
583 fout.flush();
584 }
585 catch (Exception e) {
586 Log.e(TAG, errorString);
587 Toast.makeText(PubkeyListActivity.this,
588 errorId,
589 Toast.LENGTH_LONG).show();
590 return;
591 }
592
593 Toast.makeText(PubkeyListActivity.this,
594 getResources().getString(successId, keyFile.getPath().toString()),
595 Toast.LENGTH_LONG).show();
596 }
597 }).setNegativeButton(android.R.string.cancel, null).create().show();
598 }
599
600 public void fileSelected(File f) {
601 Log.d(TAG, "File chooser returned " + f);
602 readKeyFromFile(f);
603 }
604
605 class PubkeyAdapter extends ArrayAdapter<PubkeyBean> {
606 private List<PubkeyBean> pubkeys;
607
608 class ViewHolder {
609 public TextView nickname;
610 public TextView caption;
611 public ImageView icon;
612 }
613
614 public PubkeyAdapter(Context context, List<PubkeyBean> pubkeys) {
615 super(context, R.layout.item_pubkey, pubkeys);
616 this.pubkeys = pubkeys;
617 }
618
619 @Override
620 public View getView(int position, View convertView, ViewGroup parent) {
621 ViewHolder holder;
622
623 if (convertView == null) {
624 convertView = inflater.inflate(R.layout.item_pubkey, null, false);
625 holder = new ViewHolder();
626 holder.nickname = (TextView) convertView.findViewById(android.R.id.text1);
627 holder.caption = (TextView) convertView.findViewById(android.R.id.text2);
628 holder.icon = (ImageView) convertView.findViewById(android.R.id.icon1);
629 convertView.setTag(holder);
630 }
631 else
632 holder = (ViewHolder) convertView.getTag();
633
634 PubkeyBean pubkey = pubkeys.get(position);
635 holder.nickname.setText(pubkey.getNickname());
636 boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType());
637
638 if (imported) {
639 try {
640 PEMStructure struct = PEMDecoder.parsePEM(new String(pubkey.getPrivateKey()).toCharArray());
641 String type = (struct.pemType == PEMDecoder.PEM_RSA_PRIVATE_KEY) ? "RSA" : "DSA";
642 holder.caption.setText(String.format("%s unknown-bit", type));
643 }
644 catch (IOException e) {
645 Log.e(TAG, "Error decoding IMPORTED public key at " + pubkey.getId(), e);
646 }
647 }
648 else {
649 try {
650 holder.caption.setText(pubkey.getDescription());
651 }
652 catch (Exception e) {
653 Log.e(TAG, "Error decoding public key at " + pubkey.getId(), e);
654 holder.caption.setText(R.string.pubkey_unknown_format);
655 }
656 }
657
658 if (bound == null) {
659 holder.icon.setVisibility(View.GONE);
660 }
661 else {
662 holder.icon.setVisibility(View.VISIBLE);
663
664 if (bound.isKeyLoaded(pubkey.getNickname()))
665 holder.icon.setImageState(new int[] { android.R.attr.state_checked }, true);
666 else
667 holder.icon.setImageState(new int[] { }, true);
668 }
669
670 return convertView;
671 }
672 }
673 }