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