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 trustToggle.setVisibility(View.GONE);
113 verifiedFingerprintSymbol.setOnClickListener(new View.OnClickListener() {
114 @Override
115 public void onClick(View v) {
116 replaceToast(getString(R.string.this_device_has_been_verified), false);
117 }
118 });
119 toast = null;
120 } else {
121 verifiedFingerprintSymbol.setVisibility(View.GONE);
122 trustToggle.setVisibility(View.VISIBLE);
123 trustToggle.setOnCheckedChangeListener(onCheckedChangeListener);
124 if (status.getTrust() == FingerprintStatus.Trust.UNDECIDED && undecidedNeedEnablement) {
125 trustToggle.setOnClickListener(new View.OnClickListener() {
126 @Override
127 public void onClick(View v) {
128 account.getAxolotlService().setFingerprintTrust(fingerprint,FingerprintStatus.createActive(false));
129 v.setEnabled(true);
130 v.setOnClickListener(null);
131 }
132 });
133 trustToggle.setEnabled(false);
134 } else {
135 trustToggle.setOnClickListener(null);
136 trustToggle.setEnabled(true);
137 }
138 toast = new View.OnClickListener() {
139 @Override
140 public void onClick(View v) {
141 hideToast();
142 }
143 };
144 }
145 } else {
146 key.setTextColor(getTertiaryTextColor());
147 keyType.setTextColor(getTertiaryTextColor());
148 toast = new View.OnClickListener() {
149 @Override
150 public void onClick(View v) {
151 replaceToast(getString(R.string.this_device_is_no_longer_in_use), false);
152 }
153 };
154 if (status.isVerified()) {
155 trustToggle.setVisibility(View.GONE);
156 verifiedFingerprintSymbol.setVisibility(View.VISIBLE);
157 verifiedFingerprintSymbol.setOnClickListener(toast);
158 } else {
159 trustToggle.setVisibility(View.VISIBLE);
160 verifiedFingerprintSymbol.setVisibility(View.GONE);
161 trustToggle.setOnClickListener(null);
162 trustToggle.setEnabled(false);
163 trustToggle.setOnClickListener(toast);
164 }
165 }
166
167 view.setOnClickListener(toast);
168 key.setOnClickListener(toast);
169 keyType.setOnClickListener(toast);
170 if (showTag) {
171 keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
172 } else {
173 keyType.setVisibility(View.GONE);
174 }
175 if (highlight) {
176 keyType.setTextColor(getResources().getColor(R.color.accent));
177 keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509_selected_message : R.string.omemo_fingerprint_selected_message));
178 } else {
179 keyType.setText(getString(x509 ? R.string.omemo_fingerprint_x509 : R.string.omemo_fingerprint));
180 }
181
182 key.setText(CryptoHelper.prettifyFingerprint(fingerprint.substring(2)));
183
184 keys.addView(view);
185 return true;
186 }
187
188 public void showPurgeKeyDialog(final Account account, final String fingerprint) {
189 AlertDialog.Builder builder = new AlertDialog.Builder(this);
190 builder.setTitle(getString(R.string.purge_key));
191 builder.setIconAttribute(android.R.attr.alertDialogIcon);
192 builder.setMessage(getString(R.string.purge_key_desc_part1)
193 + "\n\n" + CryptoHelper.prettifyFingerprint(fingerprint.substring(2))
194 + "\n\n" + getString(R.string.purge_key_desc_part2));
195 builder.setNegativeButton(getString(R.string.cancel), null);
196 builder.setPositiveButton(getString(R.string.purge_key),
197 new DialogInterface.OnClickListener() {
198 @Override
199 public void onClick(DialogInterface dialog, int which) {
200 account.getAxolotlService().purgeKey(fingerprint);
201 refreshUi();
202 }
203 });
204 builder.create().show();
205 }
206
207 private void showX509Certificate(Account account, String fingerprint) {
208 X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint);
209 if (x509Certificate != null) {
210 showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate));
211 } else {
212 Toast.makeText(this,R.string.certificate_not_found, Toast.LENGTH_SHORT).show();
213 }
214 }
215
216 private void showCertificateInformationDialog(Bundle bundle) {
217 View view = getLayoutInflater().inflate(R.layout.certificate_information, null);
218 final String not_available = getString(R.string.certicate_info_not_available);
219 TextView subject_cn = (TextView) view.findViewById(R.id.subject_cn);
220 TextView subject_o = (TextView) view.findViewById(R.id.subject_o);
221 TextView issuer_cn = (TextView) view.findViewById(R.id.issuer_cn);
222 TextView issuer_o = (TextView) view.findViewById(R.id.issuer_o);
223 TextView sha1 = (TextView) view.findViewById(R.id.sha1);
224
225 subject_cn.setText(bundle.getString("subject_cn", not_available));
226 subject_o.setText(bundle.getString("subject_o", not_available));
227 issuer_cn.setText(bundle.getString("issuer_cn", not_available));
228 issuer_o.setText(bundle.getString("issuer_o", not_available));
229 sha1.setText(bundle.getString("sha1", not_available));
230
231 AlertDialog.Builder builder = new AlertDialog.Builder(this);
232 builder.setTitle(R.string.certificate_information);
233 builder.setView(view);
234 builder.setPositiveButton(R.string.ok, null);
235 builder.create().show();
236 }
237}