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