1 /*
2  * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
3  * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 package w1.fi.wpadebug;
10 
11 import java.util.ArrayList;
12 import java.util.ListIterator;
13 import java.io.BufferedReader;
14 import java.io.InputStreamReader;
15 import java.io.InputStream;
16 import java.io.IOException;
17 
18 import android.app.ListActivity;
19 import android.app.ActionBar;
20 import android.content.Intent;
21 import android.os.Bundle;
22 import android.view.View;
23 import android.view.Menu;
24 import android.view.MenuItem;
25 import android.widget.ListView;
26 import android.widget.ArrayAdapter;
27 import android.widget.Toast;
28 import android.widget.AdapterView.AdapterContextMenuInfo;
29 
30 class Credential
31 {
32     int id;
33     String realm;
34     String username;
35     String domain;
36     String imsi;
37 
Credential(String entry)38     public Credential(String entry)
39     {
40 	String fields[] = entry.split("\t");
41 	id = Integer.parseInt(fields[0]);
42 	if (fields.length > 1)
43 	    realm = fields[1];
44 	else
45 	    realm = "";
46 	if (fields.length > 2)
47 	    username = fields[2];
48 	else
49 	    username = "";
50 	if (fields.length > 3 && fields[3].length() > 0)
51 	    domain = fields[3];
52 	else
53 	    domain = null;
54 	if (fields.length > 4 && fields[4].length() > 0)
55 	    imsi = fields[4];
56 	else
57 	    imsi = null;
58     }
59 
Credential(int _id, String _username, String _realm, String _domain, String _imsi)60     public Credential(int _id, String _username, String _realm, String _domain,
61 		      String _imsi)
62     {
63 	id = _id;
64 	username = _username;
65 	realm = _realm;
66 	domain = _domain;
67 	imsi = _imsi;
68     }
69 
70 
71     @Override
toString()72     public String toString()
73     {
74 	String res = id + " - " + username + "@" + realm;
75 	if (domain != null)
76 	    res += " (domain=" + domain + ")";
77 	if (imsi != null)
78 	    res += " (imsi=" + imsi + ")";
79 	return res;
80     }
81 }
82 
83 public class WpaCredActivity extends ListActivity
84 {
85     private static final String TAG = "wpadebug";
86     static final int CRED_EDIT_REQ = 0;
87     private ArrayList<Credential> mList;
88     private ArrayAdapter<Credential> mListAdapter;
89 
90     @Override
onCreate(Bundle savedInstanceState)91     public void onCreate(Bundle savedInstanceState)
92     {
93         super.onCreate(savedInstanceState);
94 
95 	mList = new ArrayList<Credential>();
96 
97 	String res = run("LIST_CREDS");
98 	if (res == null) {
99 	    Toast.makeText(this, "Could not get credential list",
100 			   Toast.LENGTH_LONG).show();
101 	    finish();
102 	    return;
103 	}
104 
105 	String creds[] = res.split("\n");
106 	for (String cred: creds) {
107 	    if (Character.isDigit(cred.charAt(0)))
108 		mList.add(new Credential(cred));
109 	}
110 
111 	mListAdapter = new ArrayAdapter<Credential>(this, android.R.layout.simple_list_item_1, mList);
112 
113 	setListAdapter(mListAdapter);
114 	registerForContextMenu(getListView());
115 
116 	ActionBar abar = getActionBar();
117     }
118 
119     @Override
onCreateOptionsMenu(Menu menu)120     public boolean onCreateOptionsMenu(Menu menu)
121     {
122 	menu.add(0, 0, 0, "Add credential");
123 	return true;
124     }
125 
onActivityResult(int requestCode, int resultCode, Intent data)126     protected void onActivityResult(int requestCode, int resultCode,
127 				    Intent data)
128     {
129 	if (requestCode == CRED_EDIT_REQ) {
130 	    if (resultCode != RESULT_OK)
131 		return;
132 
133 	    String username = data.getStringExtra("username");
134 
135 	    String realm = data.getStringExtra("realm");
136 
137 	    String domain = data.getStringExtra("domain");
138 	    if (domain != null && domain.length() == 0)
139 		domain = null;
140 
141 	    String imsi = data.getStringExtra("imsi");
142 	    if (imsi != null && imsi.length() == 0)
143 		imsi = null;
144 
145 	    String password = data.getStringExtra("password");
146 	    if (password != null && password.length() == 0)
147 		password = null;
148 
149 	    String res = run("ADD_CRED");
150 	    if (res == null || res.contains("FAIL")) {
151 		Toast.makeText(this, "Failed to add credential",
152 			       Toast.LENGTH_LONG).show();
153 		return;
154 	    }
155 
156 	    int id = -1;
157 	    String lines[] = res.split("\n");
158 	    for (String line: lines) {
159 		if (Character.isDigit(line.charAt(0))) {
160 		    id = Integer.parseInt(line);
161 		    break;
162 		}
163 	    }
164 
165 	    if (id < 0) {
166 		Toast.makeText(this, "Failed to add credential (invalid id)",
167 			       Toast.LENGTH_LONG).show();
168 		return;
169 	    }
170 
171 	    if (!set_cred_quoted(id, "username", username) ||
172 		!set_cred_quoted(id, "realm", realm) ||
173 		(password != null &&
174 		 !set_cred_quoted(id, "password", password)) ||
175 		(domain != null && !set_cred_quoted(id, "domain", domain)) ||
176 		(imsi != null && !set_cred_quoted(id, "imsi", imsi))) {
177 		run("REMOVE_CRED " + id);
178 		Toast.makeText(this, "Failed to set credential field",
179 			       Toast.LENGTH_LONG).show();
180 		return;
181 	    }
182 
183 	    mListAdapter.add(new Credential(id, username, realm, domain, imsi));
184 	}
185     }
186 
187     @Override
onOptionsItemSelected(MenuItem item)188     public boolean onOptionsItemSelected(MenuItem item)
189     {
190 	if (item.getTitle().equals("Add credential")) {
191 	    startActivityForResult(new Intent(this, WpaCredEditActivity.class),
192 				   CRED_EDIT_REQ);
193 	    return true;
194 	}
195 	return false;
196     }
197 
onCreateContextMenu(android.view.ContextMenu menu, View v, android.view.ContextMenu.ContextMenuInfo menuInfo)198     public void onCreateContextMenu(android.view.ContextMenu menu, View v,
199 				    android.view.ContextMenu.ContextMenuInfo menuInfo)
200     {
201 	menu.add(0, v.getId(), 0, "Delete");
202     }
203 
204     @Override
onContextItemSelected(MenuItem item)205     public boolean onContextItemSelected(MenuItem item)
206     {
207 	if (item.getTitle().equals("Delete")) {
208 	    AdapterContextMenuInfo info =
209 		(AdapterContextMenuInfo) item.getMenuInfo();
210 	    Credential cred = (Credential) getListAdapter().getItem(info.position);
211 	    String res = run("REMOVE_CRED " + cred.id);
212 	    if (res == null || !res.contains("OK")) {
213 		Toast.makeText(this, "Failed to delete credential",
214 			       Toast.LENGTH_LONG).show();
215 	    } else
216 		mListAdapter.remove(cred);
217 	    return true;
218 	}
219 	return super.onContextItemSelected(item);
220     }
221 
222     @Override
onListItemClick(ListView l, View v, int position, long id)223     protected void onListItemClick(ListView l, View v, int position, long id)
224     {
225 	Credential item = (Credential) getListAdapter().getItem(position);
226 	Toast.makeText(this, "Credential selected: " + item,
227 		       Toast.LENGTH_SHORT).show();
228     }
229 
run(String cmd)230     private String run(String cmd)
231     {
232 	try {
233 	    Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", "wpa_cli " + cmd});
234 	    BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
235 	    StringBuffer output = new StringBuffer();
236 	    int read;
237 	    char[] buffer = new char[1024];
238 	    while ((read = reader.read(buffer)) > 0)
239 		output.append(buffer, 0, read);
240 	    reader.close();
241 	    proc.waitFor();
242 	    return output.toString();
243 	} catch (IOException e) {
244 	    Toast.makeText(this, "Could not run command",
245 			   Toast.LENGTH_LONG).show();
246 	    return null;
247 	} catch (InterruptedException e) {
248 	    throw new RuntimeException(e);
249 	}
250     }
251 
set_cred(int id, String field, String value)252     private boolean set_cred(int id, String field, String value)
253     {
254 	String res = run("SET_CRED " + id + " " + field + " " + value);
255 	return res != null && res.contains("OK");
256     }
257 
set_cred_quoted(int id, String field, String value)258     private boolean set_cred_quoted(int id, String field, String value)
259     {
260 	String value2 = "'\"" + value + "\"'";
261 	return set_cred(id, field, value2);
262     }
263 }
264