}
}
else if (strcmp(azColName[i], "command") == 0) {
- // null command means to match all commands (whitelist all from uid)
- command_match = argv[i] == NULL || strcmp(argv[i], get_command(&(data->ctx->to))) == 0;
+ // null or empty command means to match all commands (whitelist all from uid)
+ command_match = argv[i] == NULL || strlen(argv[i]) == 0 || strcmp(argv[i], get_command(&(data->ctx->to))) == 0;
}
else if (strcmp(azColName[i], "until") == 0) {
if (argv[i] != NULL) {
#define xstr(a) str(a)
#define str(a) #a
-#define VERSION_CODE 4
+#define VERSION_CODE 5
#define VERSION xstr(VERSION_CODE) " " REQUESTOR
#define PROTO_VERSION 1
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingEnd="@dimen/section_padding"
+ android:paddingStart="@dimen/section_padding"
+ android:paddingBottom="@dimen/log_item_padding"
+ android:paddingTop="@dimen/log_item_padding" >
+
+ <include layout="@layout/log_item_base" />
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Switch xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
\ No newline at end of file
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
- android:paddingLeft="@dimen/section_padding"
+ android:paddingEnd="?attr/listItemMarginHorizontal"
+ android:paddingStart="?attr/listItemMarginHorizontal"
android:paddingBottom="@dimen/log_item_padding"
android:paddingTop="@dimen/log_item_padding" >
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/notification"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
\ No newline at end of file
android:padding="@dimen/section_padding" >
<LinearLayout
- android:id="@+id/title_container"
- android:layout_width="fill_parent"
+ android:id="@+id/policy_header"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
+ android:id="@+id/notification_container"
android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:orientation="vertical" >
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
- <LinearLayout
- android:layout_width="match_parent"
+ <TextView
+ style="@android:style/TextAppearance.Medium"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <TextView
- style="@android:style/TextAppearance.Medium"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingBottom="@dimen/section_padding"
- android:paddingLeft="@dimen/section_padding"
- android:text="@string/logs" />
-
- <include
- android:layout_weight="@dimen/golden_ratio"
- layout="@layout/log_toggle" />
- </LinearLayout>
-
- <ListView
- android:id="@+id/listview"
- android:layout_width="fill_parent"
- android:layout_height="0dp"
android:layout_weight="1"
- android:scrollbars="none" />
+ android:paddingBottom="@dimen/section_padding"
+ android:paddingLeft="@dimen/section_padding"
+ android:text="@string/notifications" />
- <TextView
- android:id="@+id/empty"
- android:layout_width="fill_parent"
- android:layout_height="0dp"
- android:layout_gravity="center"
- android:layout_weight="1"
- android:gravity="center"
- android:orientation="vertical"
- android:textAppearance="@android:style/TextAppearance.Medium"
- android:visibility="gone" />
+ <include
+ android:layout_weight="@dimen/golden_ratio"
+ layout="@layout/notification_toggle" />
</LinearLayout>
<LinearLayout
- android:id="@+id/footer_container"
- android:layout_width="fill_parent"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical" />
+ android:orientation="horizontal" >
+
+ <TextView
+ style="@android:style/TextAppearance.Medium"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingBottom="@dimen/section_padding"
+ android:paddingLeft="@dimen/section_padding"
+ android:text="@string/logs" />
+
+ <include
+ android:layout_weight="@dimen/golden_ratio"
+ layout="@layout/log_toggle" />
+ </LinearLayout>
</LinearLayout>
\ No newline at end of file
import java.util.ArrayList;
+import android.content.Context;
import android.os.Bundle;
+import android.text.TextUtils;
import android.text.format.DateFormat;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
protected int getListItemResource() {
return R.layout.log_item;
}
-
- @Override
- protected int getListFragmentResource() {
- return R.layout.policy_fragment;
- }
-
+
@Override
protected void onCreate(Bundle savedInstanceState, View view) {
super.onCreate(savedInstanceState, view);
+ LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ getListView().addHeaderView(inflater.inflate(R.layout.policy_header, null));
+
getFragment().setHasOptionsMenu(true);
if (up == null) {
name.setText(up.name);
((TextView)view.findViewById(R.id.uid_header)).setText(Integer.toString(up.desiredUid));
- ((TextView)view.findViewById(R.id.command_header)).setText(up.command == null ? getString(R.string.all_commands) : up.command);
+ ((TextView)view.findViewById(R.id.command_header)).setText(TextUtils.isEmpty(up.command) ? getString(R.string.all_commands) : up.command);
String app = up.username;
if (app == null || app.length() == 0)
app = String.valueOf(up.uid);
logs = SuperuserDatabaseHelper.getLogs(getActivity(), up, -1);
}
else {
- view.findViewById(R.id.title_container).setVisibility(View.GONE);
+ setEmpty(R.string.no_logs);
+ view.findViewById(R.id.policy_header).setVisibility(View.GONE);
logs = SuperuserDatabaseHelper.getLogs(getActivity());
}
- setEmpty(R.string.no_logs);
-
for (LogEntry log: logs) {
final String date = time.format(log.getDate());
String title = date;
});
}
- final CompoundButton cb = (CompoundButton)view.findViewById(R.id.logging);
- cb.setOnClickListener(new OnClickListener() {
+ final CompoundButton logging = (CompoundButton)view.findViewById(R.id.logging);
+ logging.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (up == null) {
+ Settings.setLogging(getActivity(), logging.isChecked());
+ }
+ else {
+ up.logging = logging.isChecked();
+ SuDatabaseHelper.setPolicy(getActivity(), up);
+ }
+ }
+ });
+ if (up == null) {
+ logging.setChecked(Settings.getLogging(getActivity()));
+ }
+ else {
+ logging.setChecked(up.logging);
+ }
+
+ final CompoundButton notification = (CompoundButton)view.findViewById(R.id.notification);
+ notification.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (up == null) {
- Settings.setLogging(getActivity(), cb.isChecked());
}
else {
- up.logging = cb.isChecked();
+ up.notification = notification.isChecked();
SuDatabaseHelper.setPolicy(getActivity(), up);
}
}
});
if (up == null) {
- cb.setChecked(Settings.getLogging(getActivity()));
+ view.findViewById(R.id.notification_container).setVisibility(View.GONE);
}
else {
- cb.setChecked(up.logging);
+ notification.setChecked(up.notification);
}
}
}
le.username = fromName;
le.date = (int)(System.currentTimeMillis() / 1000);
le.getPackageInfo(context);
- // wait a bit before logging... lots of concurrent su requests at the same time
- // cause a db lock
- // TODO: this hack should no longer be necessary
- new Handler().postDelayed(new Runnable() {
- public void run() {
- try {
- SuperuserDatabaseHelper.addLog(context, le);
- }
- catch (Exception e) {
- }
- };
- }, 5000L);
+
+ UidPolicy u = SuperuserDatabaseHelper.addLog(context, le);
String toast;
if (UidPolicy.ALLOW.equals(action)) {
toast = context.getString(R.string.superuser_denied, le.getName());
}
+ if (u != null && !u.notification)
+ return;
+
switch (Settings.getNotificationType(context)) {
case Settings.NOTIFICATION_TYPE_NOTIFICATION:
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.text.TextUtils;
public class SuDatabaseHelper extends SQLiteOpenHelper {
- private static final int CURRENT_VERSION = 4;
+ private static final int CURRENT_VERSION = 6;
Context mContext;
public SuDatabaseHelper(Context context) {
super(context, "su.sqlite", null, CURRENT_VERSION);
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 0) {
- db.execSQL("create table if not exists uid_policy (logging integer, desired_name text, username text, policy text, until integer, command text, uid integer, desired_uid integer, package_name text, name text, primary key(uid, command, desired_uid))");
+ db.execSQL("create table if not exists uid_policy (logging integer, desired_name text, username text, policy text, until integer, command text not null, uid integer, desired_uid integer, package_name text, name text, primary key(uid, command, desired_uid))");
// skip past to v4, as the next migrations have legacy tables, which were moved
oldVersion = 4;
}
SuperuserDatabaseHelper.addLog(superuser, log);
}
- Cursor c = db.query("settings", null, null,null, null, null, null);
+ Cursor c = db.query("settings", null, null, null, null, null, null);
while (c.moveToNext()) {
String key = c.getString(c.getColumnIndex("key"));
String value = c.getString(c.getColumnIndex("value"));
db.execSQL("drop table if exists settings");
oldVersion = 4;
}
+
+ if (oldVersion == 4) {
+ db.execSQL("alter table uid_policy add column notification integer");
+ db.execSQL("update uid_policy set notification = 1");
+ oldVersion = 5;
+ }
+
+ if (oldVersion == 5) {
+ // fix bug where null commands are unique from other nulls. eww!
+ ArrayList<UidPolicy> policies = getPolicies(db);
+ db.delete("uid_policy", null, null);
+ for (UidPolicy policy: policies) {
+ setPolicy(db, policy);
+ }
+ oldVersion = 6;
+ }
}
-
public static void setPolicy(Context context, UidPolicy policy) {
- SQLiteDatabase db = new SuDatabaseHelper(context).getWritableDatabase();
-
policy.getPackageInfo(context);
-
+ SQLiteDatabase db = new SuDatabaseHelper(context).getWritableDatabase();
+ try {
+ setPolicy(db, policy);
+ }
+ finally {
+ db.close();
+ }
+ }
+ public static void setPolicy(SQLiteDatabase db, UidPolicy policy) {
ContentValues values = new ContentValues();
values.put("logging", policy.logging);
+ values.put("notification", policy.notification);
values.put("uid", policy.uid);
+ // nulls are considered unique, even from other nulls. blerg.
+ // http://stackoverflow.com/questions/3906811/null-permitted-in-primary-key-why-and-in-which-dbms
+ if (policy.command == null)
+ policy.command = "";
values.put("command", policy.command);
values.put("policy", policy.policy);
values.put("until", policy.until);
values.put("desired_name", policy.desiredName);
values.put("username", policy.username);
db.replace("uid_policy", null, values);
- db.close();
}
- static UidPolicy getPolicy(Context context, Cursor c) {
+ static UidPolicy getPolicy(Cursor c) {
UidPolicy u = new UidPolicy();
u.getUidCommand(c);
u.policy = c.getString(c.getColumnIndex("policy"));
u.until = c.getInt(c.getColumnIndex("until"));
u.logging = c.getInt(c.getColumnIndex("logging")) != 0;
+ u.notification = c.getInt(c.getColumnIndex("notification")) != 0;
return u;
}
- public static ArrayList<UidPolicy> getPolicies(Context context) {
+
+ public static ArrayList<UidPolicy> getPolicies(SQLiteDatabase db) {
ArrayList<UidPolicy> ret = new ArrayList<UidPolicy>();
- SQLiteDatabase db = new SuDatabaseHelper(context).getWritableDatabase();
db.delete("uid_policy", "until > 0 and until < ?", new String[] { String.valueOf(System.currentTimeMillis()) });
Cursor c = db.query("uid_policy", null, null, null, null, null, null);
try {
while (c.moveToNext()) {
- UidPolicy u = getPolicy(context, c);
+ UidPolicy u = getPolicy(c);
ret.add(u);
}
}
}
finally {
c.close();
- db.close();
}
return ret;
}
+ public static ArrayList<UidPolicy> getPolicies(Context context) {
+ SQLiteDatabase db = new SuDatabaseHelper(context).getWritableDatabase();
+ try {
+ return getPolicies(db);
+ }
+ finally {
+ db.close();
+ }
+ }
+
public static void delete(Context context, UidPolicy policy) {
SQLiteDatabase db = new SuDatabaseHelper(context).getWritableDatabase();
- if (policy.command != null)
+ if (!TextUtils.isEmpty(policy.command))
db.delete("uid_policy", "uid = ? and command = ? and desired_uid = ?", new String[] { String.valueOf(policy.uid), policy.command, String.valueOf(policy.desiredUid) });
else
db.delete("uid_policy", "uid = ? and desired_uid = ?", new String[] { String.valueOf(policy.uid), String.valueOf(policy.desiredUid) });
public static UidPolicy get(Context context, int uid, int desiredUid, String command) {
SQLiteDatabase db = new SuDatabaseHelper(context).getReadableDatabase();
Cursor c;
- if (command != null)
+ if (!TextUtils.isEmpty(command))
c = db.query("uid_policy", null, "uid = ? and command = ? and desired_uid = ?", new String[] { String.valueOf(uid), command, String.valueOf(desiredUid) }, null, null, null);
else
c = db.query("uid_policy", null, "uid = ? and desired_uid = ?", new String[] { String.valueOf(uid), String.valueOf(desiredUid) }, null, null, null);
try {
if (c.moveToNext()) {
- return getPolicy(context, c);
+ return getPolicy(c);
}
}
finally {
}
return null;
}
- private static final String LOGTAG = "SuReceiver";
}
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.text.TextUtils;
import com.koushikdutta.superuser.util.Settings;
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion == 0) {
- db.execSQL("create table if not exists log (id integer primary key autoincrement, desired_name text, username text, uid integer, desired_uid integer, command text, date integer, action text, package_name text, name text)");
+ db.execSQL("create table if not exists log (id integer primary key autoincrement, desired_name text, username text, uid integer, desired_uid integer, command text not null, date integer, action text, package_name text, name text)");
db.execSQL("create index if not exists log_uid_index on log(uid)");
db.execSQL("create index if not exists log_desired_uid_index on log(desired_uid)");
db.execSQL("create index if not exists log_command_index on log(command)");
db.execSQL("create index if not exists log_date_index on log(date)");
- db.execSQL("create table if not exists settings (key TEXT PRIMARY KEY, value TEXT)");
+ db.execSQL("create table if not exists settings (key text primary key not null, value text)");
oldVersion = 1;
}
}
public static ArrayList<LogEntry> getLogs(SQLiteDatabase db, UidPolicy policy, int limit) {
ArrayList<LogEntry> ret = new ArrayList<LogEntry>();
Cursor c;
- if (policy.command != null)
+ if (!TextUtils.isEmpty(policy.command))
c = db.query("log", null, "uid = ? and desired_uid = ? and command = ?", new String[] { String.valueOf(policy.uid), String.valueOf(policy.desiredUid), policy.command }, null, null, "date DESC", limit == -1 ? null : String.valueOf(limit));
else
c = db.query("log", null, "uid = ? and desired_uid = ?", new String[] { String.valueOf(policy.uid), String.valueOf(policy.desiredUid) }, null, null, "date DESC", limit == -1 ? null : String.valueOf(limit));
static void addLog(SQLiteDatabase db, LogEntry log) {
ContentValues values = new ContentValues();
values.put("uid", log.uid);
+ // nulls are considered unique, even from other nulls. blerg.
+ // http://stackoverflow.com/questions/3906811/null-permitted-in-primary-key-why-and-in-which-dbms
+ if (log.command == null)
+ log.command = "";
values.put("command", log.command);
values.put("action", log.action);
values.put("date", log.date);
db.insert("log", null, values);
}
- public static void addLog(Context context, LogEntry log) {
- if (!Settings.getLogging(context))
- return;
-
- SQLiteDatabase db = new SuDatabaseHelper(context).getReadableDatabase();
- Cursor c = db.query("uid_policy", null, "uid = ? and command = ? and desired_uid = ?", new String[] { String.valueOf(log.uid), log.command, String.valueOf(log.desiredUid) }, null, null, null, null);
+ public static UidPolicy addLog(Context context, LogEntry log) {
+ // nulls are considered unique, even from other nulls. blerg.
+ // http://stackoverflow.com/questions/3906811/null-permitted-in-primary-key-why-and-in-which-dbms
+ if (log.command == null)
+ log.command = "";
+
+ // grab the policy and add a log
+ UidPolicy u = null;
+ SQLiteDatabase su = new SuDatabaseHelper(context).getReadableDatabase();
+ Cursor c = su.query("uid_policy", null, "uid = ? and (command = ? or command = ?) and desired_uid = ?", new String[] { String.valueOf(log.uid), log.command, "", String.valueOf(log.desiredUid) }, null, null, null, null);
try {
if (c.moveToNext()) {
- UidPolicy u = SuDatabaseHelper.getPolicy(context, c);
- if (!u.logging) {
- db.close();
- return;
- }
+ u = SuDatabaseHelper.getPolicy(c);
}
}
finally {
c.close();
- db.close();
+ su.close();
}
- db = new SuperuserDatabaseHelper(context).getReadableDatabase();
+ if (u != null && !u.logging)
+ return u;
+
+ if (!Settings.getLogging(context))
+ return u;
+
+ SQLiteDatabase superuser = new SuperuserDatabaseHelper(context).getWritableDatabase();
try {
- addLog(db, log);
+ addLog(superuser, log);
}
finally {
- db.close();
+ superuser.close();
}
+
+ return u;
}
}
public String policy;
public int until;
public boolean logging = true;
+ public boolean notification = true;
public Date getUntilDate() {
return new Date((long)until * 1000);
}
throw new Exception("unknown su");
String[] parts = result.split(" ");
- if (!"4".equals(parts[0]))
+ if (!"5".equals(parts[0]))
throw new Exception("binary is old");
}