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