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