/*
Whitelist.java
Copyright (c) 2015 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.manager.policy;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import android.support.v4.BuildConfig;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Whitelist of origins.
*
* @author NTT DOCOMO, INC.
*/
public class Whitelist {
/** The origin database. */
private final OriginDB mCache;
/**
* Constructor.
*
* @param context Context
*/
public Whitelist(final Context context) {
mCache = new OriginDB(context);
}
/**
* Returns whether requests from the specified origin are allowed.
*
* @param origin Origin of requests
* @return <code>true</code> if requests from the specified origin are allowed,
* otherwise <code>false</code>.
*/
public boolean allows(final Origin origin) {
List<OriginInfo> patterns = getOrigins();
for (OriginInfo p : patterns) {
if (p.matches(origin)) {
return true;
}
}
return false;
}
/**
* Returns origin.
*
* @return Origin
*/
public synchronized List<OriginInfo> getOrigins() {
return mCache.getOrigins();
}
/**
* Returns whether the specified origin is included in this whitelist.
* @param originExp a string expression of origin
* @return <code>true</code> if origin is included, otherwise <code>false</code>
*/
public synchronized boolean hasOrigin(final String originExp) {
for (OriginInfo info : mCache.getOrigins()) {
if (info.matches(OriginParser.parse(originExp))) {
return true;
}
}
return false;
}
/**
* Adds an origin to this whitelist.
*
* @param origin an origin to be added.
* @param title the title of origin.
* @return An instance of {@link OriginInfo}
* @throws WhitelistException if origin can not be stored.
*/
public synchronized OriginInfo addOrigin(final Origin origin, final String title)
throws WhitelistException {
try {
long date = System.currentTimeMillis();
long id = mCache.addOrigin(origin, title, date);
return new OriginInfo(id, origin, title, date);
} catch (OriginDBException e) {
throw new WhitelistException("Failed to store origin: " + origin, e);
}
}
/**
* Update an origin on this whitelist.
*
* @param info an origin to be updated.
* @throws WhitelistException if origin can not be updated.
*/
public synchronized void updateOrigin(final OriginInfo info) throws WhitelistException {
try {
mCache.updateOrigin(info);
} catch (OriginDBException e) {
throw new WhitelistException("Failed to store origin: " + info.mOrigin, e);
}
}
/**
* Removes an origin from this whitelist.
*
* @param info an origin to be removed.
* @throws WhitelistException if origin can not be removed.
*/
public synchronized void removeOrigin(final OriginInfo info) throws WhitelistException {
try {
mCache.removeOrigin(info);
} catch (OriginDBException e) {
throw new WhitelistException("Failed to remove origin: " + info.mOrigin, e);
}
}
/**
* Origin database.
*/
private static class OriginDB extends SQLiteOpenHelper {
/**
* The DB file name.
*/
private static final String DB_NAME = "__device_connect_whitelist.db";
/**
* The DB Version.
*/
private static final int DB_VERSION = 1;
/**
* The table name.
*/
private static final String TABLE_NAME = "Origins";
/**
* The unique ID for a row.
* <p>Type: INTEGER (long)</p>
*/
private static final String ID = BaseColumns._ID;
/**
* The origin.
* <p>Type: TEXT</p>
*/
private static final String ORIGIN = "origin";
/**
* The title of origin.
* <p>Type: TEXT</p>
*/
private static final String TITLE = "title";
/**
* The registration date.
* <p>Type: INTEGER</p>
*/
private static final String DATE = "date";
/**
* CREATE TABLE statement.
*/
private static final String CREATE = "CREATE TABLE " + TABLE_NAME + " ("
+ ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ ORIGIN + " TEXT NOT NULL, "
+ TITLE + " TEXT NOT NULL, "
+ DATE + " INTEGER);";
/**
* DROP TABLE statement.
*/
private static final String DROP = "DROP TABLE IF EXISTS " + TABLE_NAME;
/**
* Constructor.
* @param context Context
*/
public OriginDB(final Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(final SQLiteDatabase db) {
db.execSQL(CREATE);
try {
addOrigin(db, new HttpOrigin("localhost", 80), "Manager (HTTP)", System.currentTimeMillis());
} catch (OriginDBException e) {
if (BuildConfig.DEBUG) {
Log.e("Origin", "error.");
}
}
}
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
db.execSQL(DROP);
}
/**
* Gets all origins in the database.
* @return all origins in the database
*/
List<OriginInfo> getOrigins() {
List<OriginInfo> patterns = new ArrayList<OriginInfo>();
SQLiteDatabase db = openDB();
if (db == null) {
return patterns;
}
Cursor c = db.query(TABLE_NAME, null, null, null, null, null, null);
if (c.moveToFirst()) {
do {
long id = c.getLong(0);
String originExp = c.getString(1);
Origin origin = OriginParser.parse(originExp);
String title = c.getString(2);
long date = c.getLong(3);
patterns.add(new OriginInfo(id, origin, title, date));
} while (c.moveToNext());
}
c.close();
db.close();
return patterns;
}
/**
* Adds a origin to this database.
* @param origin the origin
* @param title the title of origin
* @param date the registration date
* @return row ID
* @throws OriginDBException throws if database error occurred
*/
long addOrigin(final Origin origin, final String title, final long date) throws OriginDBException {
SQLiteDatabase db = openDB();
if (db == null) {
throw new OriginDBException("Failed to open the database.");
}
try {
return addOrigin(db, origin, title, date);
} finally {
db.close();
}
}
/**
* Adds a origin to this database.
* @param db database
* @param origin the origin
* @param title the title of origin
* @param date the registration date
* @return row ID
* @throws OriginDBException throws if database error occurred
*/
long addOrigin(SQLiteDatabase db, final Origin origin, final String title, final long date) throws OriginDBException {
long id;
try {
db.beginTransaction();
ContentValues values = new ContentValues();
values.put(ORIGIN, origin.toString());
values.put(TITLE, title);
values.put(DATE, date);
id = db.insert(TABLE_NAME, null, values);
if (id == -1) {
throw new OriginDBException("Failed to store origin.");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
return id;
}
/**
* Updates a origin to this database.
* @param origin the origin to be updated
* @throws OriginDBException throws if database error occurred
*/
void updateOrigin(final OriginInfo origin) throws OriginDBException {
SQLiteDatabase db = openDB();
if (db == null) {
throw new OriginDBException("Failed to open the database.");
}
try {
db.beginTransaction();
ContentValues values = new ContentValues();
values.put(ORIGIN, origin.mOrigin.toString());
values.put(TITLE, origin.mTitle);
int count = db.update(TABLE_NAME, values, ID + "=" + origin.mId, null);
if (count != 1) {
throw new OriginDBException("Failed to store origin.");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
db.close();
}
}
/**
* Removes a origin from this database.
* @param origin the origin to be removed
* @throws OriginDBException if the origin cannot be removed
* @throws IllegalArgumentException if <code>id</code> equals -1.
*/
void removeOrigin(final OriginInfo origin) throws OriginDBException {
if (origin.mId == -1) {
throw new IllegalArgumentException("The row ID is illegal.");
}
SQLiteDatabase db = openDB();
if (db == null) {
throw new OriginDBException("Failed to open origin whitelist database.");
}
try {
db.beginTransaction();
int count = db.delete(TABLE_NAME, ID + "=" + origin.mId, null);
if (count != 1) {
throw new OriginDBException("Failed to remove origin: " + origin.mOrigin);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
db.close();
}
}
/**
* Opens the database.
*
* @return An instance of the database
*/
SQLiteDatabase openDB() {
SQLiteDatabase db;
try {
db = getWritableDatabase();
} catch (SQLiteException e) {
db = null;
}
return db;
}
}
/**
* Exception of origin database management.
*/
private static class OriginDBException extends Exception {
/** Serial Version UID. */
private static final long serialVersionUID = 1L;
/**
* Constructor.
* @param message the detail message for this exception.
*/
public OriginDBException(final String message) {
super(message);
}
}
}