1package eu.siacs.conversations.ui;
2
3import android.content.Intent;
4import android.os.Bundle;
5import android.view.View;
6import android.widget.Button;
7import android.widget.EditText;
8import android.widget.RelativeLayout;
9import android.widget.TextView;
10import android.widget.Toast;
11
12import net.java.otr4j.OtrException;
13import net.java.otr4j.session.Session;
14
15import eu.siacs.conversations.R;
16import eu.siacs.conversations.entities.Account;
17import eu.siacs.conversations.entities.Conversation;
18import eu.siacs.conversations.services.XmppConnectionService;
19import eu.siacs.conversations.utils.CryptoHelper;
20import eu.siacs.conversations.xmpp.jid.InvalidJidException;
21import eu.siacs.conversations.xmpp.jid.Jid;
22
23public class VerifyOTRActivity extends XmppActivity implements XmppConnectionService.OnConversationUpdate {
24
25 public static final String ACTION_VERIFY_CONTACT = "verify_contact";
26
27 private RelativeLayout mVerificationAreaOne;
28 private RelativeLayout mVerificationAreaTwo;
29 private TextView mErrorNoSession;
30 private TextView mRemoteJid;
31 private TextView mRemoteFingerprint;
32 private TextView mYourFingerprint;
33 private EditText mSharedSecretHint;
34 private EditText mSharedSecretSecret;
35 private Button mButtonVerifyFingerprint;
36 private Button mButtonSharedSecretPositive;
37 private Button mButtonSharedSecretNegative;
38 private TextView mStatusMessage;
39 private Account mAccount;
40 private Conversation mConversation;
41
42 private View.OnClickListener mVerifyFingerprintListener = new View.OnClickListener() {
43
44 @Override
45 public void onClick(View view) {
46 mConversation.verifyOtrFingerprint();
47 finish();
48 }
49 };
50
51 private View.OnClickListener mCreateSharedSecretListener = new View.OnClickListener() {
52 @Override
53 public void onClick(final View view) {
54 if (isAccountOnline()) {
55 final String question = mSharedSecretHint.getText().toString();
56 final String secret = mSharedSecretSecret.getText().toString();
57 initSmp(question, secret);
58 updateView();
59 }
60 }
61 };
62 private View.OnClickListener mCancelSharedSecretListener = new View.OnClickListener() {
63 @Override
64 public void onClick(View view) {
65 if (isAccountOnline()) {
66 abortSmp();
67 updateView();
68 }
69 }
70 };
71 private View.OnClickListener mRespondSharedSecretListener = new View.OnClickListener() {
72
73 @Override
74 public void onClick(View view) {
75 if (isAccountOnline()) {
76 final String question = mSharedSecretHint.getText().toString();
77 final String secret = mSharedSecretSecret.getText().toString();
78 respondSmp(question, secret);
79 updateView();
80 }
81 }
82 };
83 private View.OnClickListener mRetrySharedSecretListener = new View.OnClickListener() {
84 @Override
85 public void onClick(View view) {
86 mConversation.smp().status = Conversation.Smp.STATUS_NONE;
87 mConversation.smp().hint = null;
88 mConversation.smp().secret = null;
89 updateView();
90 }
91 };
92 private View.OnClickListener mFinishListener = new View.OnClickListener() {
93 @Override
94 public void onClick(View view) {
95 mConversation.smp().status = Conversation.Smp.STATUS_NONE;
96 finish();
97 }
98 };
99
100 protected boolean initSmp(final String question, final String secret) {
101 final Session session = mConversation.getOtrSession();
102 if (session!=null) {
103 try {
104 session.initSmp(question, secret);
105 mConversation.smp().status = Conversation.Smp.STATUS_WE_REQUESTED;
106 return true;
107 } catch (OtrException e) {
108 return false;
109 }
110 } else {
111 return false;
112 }
113 }
114
115 protected boolean abortSmp() {
116 final Session session = mConversation.getOtrSession();
117 if (session!=null) {
118 try {
119 session.abortSmp();
120 mConversation.smp().status = Conversation.Smp.STATUS_NONE;
121 mConversation.smp().hint = null;
122 mConversation.smp().secret = null;
123 return true;
124 } catch (OtrException e) {
125 return false;
126 }
127 } else {
128 return false;
129 }
130 }
131
132 protected boolean respondSmp(final String question, final String secret) {
133 final Session session = mConversation.getOtrSession();
134 if (session!=null) {
135 try {
136 session.respondSmp(question,secret);
137 return true;
138 } catch (OtrException e) {
139 return false;
140 }
141 } else {
142 return false;
143 }
144 }
145
146 protected boolean isAccountOnline() {
147 if (this.mAccount.getStatus() != Account.State.ONLINE) {
148 Toast.makeText(this,R.string.not_connected_try_again,Toast.LENGTH_SHORT).show();
149 return false;
150 } else {
151 return true;
152 }
153 }
154
155 protected boolean handleIntent(Intent intent) {
156 if (intent.getAction().equals(ACTION_VERIFY_CONTACT)) {
157 try {
158 this.mAccount = this.xmppConnectionService.findAccountByJid(Jid.fromString(intent.getExtras().getString("account")));
159 } catch (final InvalidJidException ignored) {
160 return false;
161 }
162 try {
163 this.mConversation = this.xmppConnectionService.find(this.mAccount,Jid.fromString(intent.getExtras().getString("contact")));
164 if (this.mConversation == null) {
165 return false;
166 }
167 } catch (final InvalidJidException ignored) {
168 return false;
169 }
170 return true;
171 } else {
172 return false;
173 }
174 }
175
176 @Override
177 protected void onBackendConnected() {
178 if (handleIntent(getIntent())) {
179 updateView();
180 }
181 }
182
183 protected void updateView() {
184 if (this.mConversation.hasValidOtrSession()) {
185 this.mVerificationAreaOne.setVisibility(View.VISIBLE);
186 this.mVerificationAreaTwo.setVisibility(View.VISIBLE);
187 this.mErrorNoSession.setVisibility(View.GONE);
188 this.mYourFingerprint.setText(CryptoHelper.prettifyFingerprint(this.mAccount.getOtrFingerprint()));
189 this.mRemoteFingerprint.setText(this.mConversation.getOtrFingerprint());
190 this.mRemoteJid.setText(this.mConversation.getContact().getJid().toBareJid().toString());
191 Conversation.Smp smp = mConversation.smp();
192 Session session = mConversation.getOtrSession();
193 if (mConversation.isOtrFingerprintVerified()) {
194 deactivateButton(mButtonVerifyFingerprint, R.string.verified);
195 } else {
196 activateButton(mButtonVerifyFingerprint, R.string.verify, mVerifyFingerprintListener);
197 }
198 if (smp.status == Conversation.Smp.STATUS_NONE) {
199 activateButton(mButtonSharedSecretPositive, R.string.create, mCreateSharedSecretListener);
200 deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
201 this.mSharedSecretHint.setFocusableInTouchMode(true);
202 this.mSharedSecretSecret.setFocusableInTouchMode(true);
203 this.mSharedSecretSecret.setText("");
204 this.mSharedSecretHint.setText("");
205 this.mSharedSecretHint.setVisibility(View.VISIBLE);
206 this.mSharedSecretSecret.setVisibility(View.VISIBLE);
207 this.mStatusMessage.setVisibility(View.GONE);
208 } else if (smp.status == Conversation.Smp.STATUS_CONTACT_REQUESTED) {
209 this.mSharedSecretHint.setFocusable(false);
210 this.mSharedSecretHint.setText(smp.hint);
211 this.mSharedSecretSecret.setFocusableInTouchMode(true);
212 this.mSharedSecretHint.setVisibility(View.VISIBLE);
213 this.mSharedSecretSecret.setVisibility(View.VISIBLE);
214 this.mStatusMessage.setVisibility(View.GONE);
215 deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
216 activateButton(mButtonSharedSecretPositive, R.string.respond, mRespondSharedSecretListener);
217 } else if (smp.status == Conversation.Smp.STATUS_FAILED) {
218 activateButton(mButtonSharedSecretNegative, R.string.cancel, mFinishListener);
219 activateButton(mButtonSharedSecretPositive, R.string.try_again, mRetrySharedSecretListener);
220 this.mSharedSecretHint.setVisibility(View.GONE);
221 this.mSharedSecretSecret.setVisibility(View.GONE);
222 this.mStatusMessage.setVisibility(View.VISIBLE);
223 this.mStatusMessage.setText(R.string.secrets_do_not_match);
224 this.mStatusMessage.setTextColor(getWarningTextColor());
225 } else if (smp.status == Conversation.Smp.STATUS_VERIFIED) {
226 this.mSharedSecretHint.setVisibility(View.GONE);
227 this.mSharedSecretSecret.setVisibility(View.GONE);
228 this.mStatusMessage.setVisibility(View.VISIBLE);
229 this.mStatusMessage.setText(R.string.verified);
230 this.mStatusMessage.setTextColor(getPrimaryColor());
231 deactivateButton(mButtonSharedSecretNegative, R.string.cancel);
232 activateButton(mButtonSharedSecretPositive, R.string.finish, mFinishListener);
233 } else if (session != null && session.isSmpInProgress()) {
234 deactivateButton(mButtonSharedSecretPositive, R.string.in_progress);
235 activateButton(mButtonSharedSecretNegative, R.string.cancel, mCancelSharedSecretListener);
236 this.mSharedSecretHint.setVisibility(View.VISIBLE);
237 this.mSharedSecretSecret.setVisibility(View.VISIBLE);
238 this.mSharedSecretHint.setFocusable(false);
239 this.mSharedSecretSecret.setFocusable(false);
240 }
241 } else {
242 this.mVerificationAreaOne.setVisibility(View.GONE);
243 this.mVerificationAreaTwo.setVisibility(View.GONE);
244 this.mErrorNoSession.setVisibility(View.VISIBLE);
245 }
246 }
247
248 protected void activateButton(Button button, int text, View.OnClickListener listener) {
249 button.setEnabled(true);
250 button.setTextColor(getPrimaryTextColor());
251 button.setText(text);
252 button.setOnClickListener(listener);
253 }
254
255 protected void deactivateButton(Button button, int text) {
256 button.setEnabled(false);
257 button.setTextColor(getSecondaryTextColor());
258 button.setText(text);
259 button.setOnClickListener(null);
260 }
261
262 @Override
263 protected void onCreate(Bundle savedInstanceState) {
264 super.onCreate(savedInstanceState);
265 setContentView(R.layout.activity_verify_otr);
266 this.mRemoteFingerprint = (TextView) findViewById(R.id.remote_fingerprint);
267 this.mRemoteJid = (TextView) findViewById(R.id.remote_jid);
268 this.mYourFingerprint = (TextView) findViewById(R.id.your_fingerprint);
269 this.mButtonSharedSecretNegative = (Button) findViewById(R.id.button_shared_secret_negative);
270 this.mButtonSharedSecretPositive = (Button) findViewById(R.id.button_shared_secret_positive);
271 this.mButtonVerifyFingerprint = (Button) findViewById(R.id.button_verify_fingerprint);
272 this.mSharedSecretSecret = (EditText) findViewById(R.id.shared_secret_secret);
273 this.mSharedSecretHint = (EditText) findViewById(R.id.shared_secret_hint);
274 this.mStatusMessage= (TextView) findViewById(R.id.status_message);
275 this.mVerificationAreaOne = (RelativeLayout) findViewById(R.id.verification_area_one);
276 this.mVerificationAreaTwo = (RelativeLayout) findViewById(R.id.verification_area_two);
277 this.mErrorNoSession = (TextView) findViewById(R.id.error_no_session);
278 }
279
280 @Override
281 protected String getShareableUri() {
282 if (mAccount!=null) {
283 return mAccount.getShareableUri();
284 } else {
285 return "";
286 }
287 }
288
289 public void onConversationUpdate() {
290 runOnUiThread(new Runnable() {
291 @Override
292 public void run() {
293 updateView();
294 }
295 });
296 }
297}