1package eu.siacs.conversations;
2
3import android.app.AlertDialog;
4import android.content.DialogInterface;
5import android.os.Bundle;
6import android.view.ContextMenu;
7import android.view.MenuItem;
8import android.view.View;
9import android.widget.CompoundButton;
10import android.widget.ImageView;
11import android.widget.LinearLayout;
12import android.widget.TextView;
13import android.widget.Toast;
14
15import java.security.cert.X509Certificate;
16
17import eu.siacs.conversations.crypto.axolotl.FingerprintStatus;
18import eu.siacs.conversations.entities.Account;
19import eu.siacs.conversations.ui.XmppActivity;
20import eu.siacs.conversations.ui.widget.Switch;
21import eu.siacs.conversations.utils.CryptoHelper;
22
23
24public abstract class OmemoActivity extends XmppActivity {
25
26 private Account mSelectedAccount;
27 private String mSelectedFingerprint;
28
29 @Override
30 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
31 super.onCreateContextMenu(menu,v,menuInfo);
32 Object account = v.getTag(R.id.TAG_ACCOUNT);
33 Object fingerprint = v.getTag(R.id.TAG_FINGERPRINT);
34 if (account != null && fingerprint != null && account instanceof Account && fingerprint instanceof String) {
35 getMenuInflater().inflate(R.menu.omemo_key_context, menu);
36 this.mSelectedAccount = (Account) account;
37 this.mSelectedFingerprint = (String) fingerprint;
38 }
39 }
40
41 @Override
42 public boolean onContextItemSelected(MenuItem item) {
43 switch (item.getItemId()) {
44 case R.id.purge_omemo_key:
45 showPurgeKeyDialog(mSelectedAccount,mSelectedFingerprint);
46 break;
47 case R.id.copy_omemo_key:
48 copyOmemoFingerprint(mSelectedFingerprint);
49 break;
50 }
51 return true;
52 }
53
54 protected void copyOmemoFingerprint(String fingerprint) {
55 if (copyTextToClipboard(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)), R.string.omemo_fingerprint)) {
56 Toast.makeText(
57 this,
58 R.string.toast_message_omemo_fingerprint,
59 Toast.LENGTH_SHORT).show();
60 }
61 }
62
63 protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight) {
64 final FingerprintStatus status = account.getAxolotlService().getFingerprintTrust(fingerprint);
65 return status != null && addFingerprintRowWithListeners(keys, account, fingerprint, highlight, status, true, true, new CompoundButton.OnCheckedChangeListener() {
66 @Override
67 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
68 account.getAxolotlService().setFingerprintTrust(fingerprint, FingerprintStatus.createActive(isChecked));
69 }
70 });
71 }
72
73 protected boolean addFingerprintRowWithListeners(LinearLayout keys, final Account account,
74 final String fingerprint,
75 boolean highlight,
76 FingerprintStatus status,
77 boolean showTag,
78 boolean undecidedNeedEnablement,
79 CompoundButton.OnCheckedChangeListener
80 onCheckedChangeListener) {
81 if (status.isCompromised()) {
82 return false;
83 }
84 View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false);
85 TextView key = (TextView) view.findViewById(R.id.key);
86 TextView keyType = (TextView) view.findViewById(R.id.key_type);
87 if (Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509) {
88 View.OnClickListener listener = new View.OnClickListener() {
89 @Override
90 public void onClick(View v) {
91 showX509Certificate(account,fingerprint);
92 }
93 };
94 key.setOnClickListener(listener);
95 keyType.setOnClickListener(listener);
96 }
97 Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust);
98 ImageView verifiedFingerprintSymbol = (ImageView) view.findViewById(R.id.verified_fingerprint);
99 trustToggle.setVisibility(View.VISIBLE);
100 registerForContextMenu(view);
101 view.setTag(R.id.TAG_ACCOUNT,account);
102 view.setTag(R.id.TAG_FINGERPRINT,fingerprint);
103 boolean x509 = Config.X509_VERIFICATION && status.getTrust() == FingerprintStatus.Trust.VERIFIED_X509;
104 final View.OnClickListener toast;
105 trustToggle.setChecked(status.isTrusted(), false);
106
107 if (status.isActive()){
108 key.setTextColor(getPrimaryTextColor());
109 keyType.setTextColor(getSecondaryTextColor());
110 if (status.isVerified()) {
111 verifiedFingerprintSymbol.setVisibility(View.VISIBLE);
112 verifiedFingerprintSymbol.setAlpha(1.0f);
113 trustToggle.setVisibility(View.GONE);
114 verifiedFingerprintSymbol.setOnClickListener(new View.OnClickListener() {
115 @Override
116 public void onClick(View v) {
117 replaceToast(getString(R.string.this_device_has_been_verified), false);
118 }
119 });
120 toast = null;
121 } else {
122 verifiedFingerprintSymbol.setVisibility(View.GONE);
123 trustToggle.setVisibility(View.VISIBLE);
124 trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
125 if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED && undecidedNeedEnablement) {
126 trustToggle.setOnClickListener(new View.OnClickListener() {
127 @Override
128 public void onClick(View v) {
129 account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(false));
130 v.setEnabled(true);
131 v.setOnClickListener(null);
132 }
133 });
134 trustToggle.setEnabled(false);
135 } else {
136 trustToggle.setOnClickListener(null);
137 trustToggle.setEnabled(true);
138 }
139 toast = new View.OnClickListener() {
140 @Override
141 public void onClick(View v) {
142 hideToast();
143 }
144 };
145 }
146 } else {
147 key.setTextColor(getTertiaryTextColor());
148 keyType.setTextColor(getTertiaryTextColor());
149 toast = new View.OnClickListener() {
150 @Override
151 public void onClick(View v) {
152 replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
153 }
154 };
155 if (status.isVerified()) {
156 trustToggle.setVisibility(View.GONE);
157 verifiedFingerprintSymbol.setVisibility(View.VISIBLE);
158 verifiedFingerprintSymbol.setAlpha(0.4368f);
159 verifiedFingerprintSymbol.setOnClickListener(toast);
160 } else {
161 trustToggle.setVisibility(View.VISIBLE);
162 verifiedFingerprintSymbol.setVisibility(View.GONE);
163 trustToggle.setOnClickListener(null);
164 trustToggle.setEnabled(false);
165 trustToggle.setOnClickListener(toast);
166 }
167 }
168
169 view.setOnClickListener(toast);
170 key.setOnClickListener(toast);
171 keyType.setOnClickListener(toast);
172 if (showTag) {
173 keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
174 } else {
175 keyType.setVisibility(View.GONE);
176 }
177 if (highlight) {
178 keyType.setTextColor(getResources().getColor(R.color.accent));
179 keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
180 } else {
181 keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
182 }
183
184 key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
185
186 keys.addView(view);
187 return true;
188 }
189
190 public void showPurgeKeyDialog(final Account account, final String fingerprint) {
191 AlertDialog.Builder builder = new AlertDialog.Builder(this);
192 builder.setTitle(getString(R.string.purge_key));
193 builder.setIconAttribute(android.R.attr.alertDialogIcon);
194 builder.setMessage(getString(R.string.purge_key_desc_part1)
195 + "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint.substring(2))
196 + "\n\n" + getString(R.string.purge_key_desc_part2));
197 builder.setNegativeButton(getString(R.string.cancel), null);
198 builder.setPositiveButton(getString(R.string.purge_key),
199 new DialogInterface.OnClickListener() {
200 @Override
201 public void onClick(DialogInterface dialog, int which) {
202 account.getAxolotlService().purgeKey(fingerprint);
203 refreshUi();
204 }
205 });
206 builder.create().show();
207 }
208
209 private void showX509Certificate(Account account, String fingerprint) {
210 X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint);
211 if (x509Certificate != null) {
212 showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));
213 } else {
214 Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
215 }
216 }
217
218 private void showCertificateInformationDialog(Bundle bundle) {
219 View view = getLayoutInflater().inflate(R.layout.certificate_information, null);
220 final String not_available = getString(R.string.certicate_info_not_available);
221 TextView subject_cn = (TextView) view.findViewById(R.id.subject_cn);
222 TextView subject_o = (TextView) view.findViewById(R.id.subject_o);
223 TextView issuer_cn = (TextView) view.findViewById(R.id.issuer_cn);
224 TextView issuer_o = (TextView) view.findViewById(R.id.issuer_o);
225 TextView sha1 = (TextView) view.findViewById(R.id.sha1);
226
227 subject_cn.setText(bundle.getString("subject_cn", not_available));
228 subject_o.setText(bundle.getString("subject_o", not_available));
229 issuer_cn.setText(bundle.getString("issuer_cn", not_available));
230 issuer_o.setText(bundle.getString("issuer_o", not_available));
231 sha1.setText(bundle.getString("sha1", not_available));
232
233 AlertDialog.Builder builder = new AlertDialog.Builder(this);
234 builder.setTitle(R.string.certificate_information);
235 builder.setView(view);
236 builder.setPositiveButton(R.string.ok, null);
237 builder.create().show();
238 }
239}