Merge branch 'android-certreq'
[strongswan.git] / src / frontends / android / app / src / main / java / org / strongswan / android / data / VpnProfileDataSource.java
1 /*
2 * Copyright (C) 2012-2017 Tobias Brunner
3 * Copyright (C) 2012 Giuliano Grassi
4 * Copyright (C) 2012 Ralf Sager
5 * HSR Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 package org.strongswan.android.data;
19
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.database.Cursor;
23 import android.database.SQLException;
24 import android.database.sqlite.SQLiteDatabase;
25 import android.database.sqlite.SQLiteOpenHelper;
26 import android.database.sqlite.SQLiteQueryBuilder;
27 import android.util.Log;
28
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.UUID;
32
33 public class VpnProfileDataSource
34 {
35 private static final String TAG = VpnProfileDataSource.class.getSimpleName();
36 public static final String KEY_ID = "_id";
37 public static final String KEY_UUID = "_uuid";
38 public static final String KEY_NAME = "name";
39 public static final String KEY_GATEWAY = "gateway";
40 public static final String KEY_VPN_TYPE = "vpn_type";
41 public static final String KEY_USERNAME = "username";
42 public static final String KEY_PASSWORD = "password";
43 public static final String KEY_CERTIFICATE = "certificate";
44 public static final String KEY_USER_CERTIFICATE = "user_certificate";
45 public static final String KEY_MTU = "mtu";
46 public static final String KEY_PORT = "port";
47 public static final String KEY_SPLIT_TUNNELING = "split_tunneling";
48 public static final String KEY_LOCAL_ID = "local_id";
49 public static final String KEY_REMOTE_ID = "remote_id";
50 public static final String KEY_EXCLUDED_SUBNETS = "excluded_subnets";
51 public static final String KEY_INCLUDED_SUBNETS = "included_subnets";
52 public static final String KEY_SELECTED_APPS = "selected_apps";
53 public static final String KEY_SELECTED_APPS_LIST = "selected_apps_list";
54 public static final String KEY_NAT_KEEPALIVE = "nat_keepalive";
55 public static final String KEY_FLAGS = "flags";
56
57 private DatabaseHelper mDbHelper;
58 private SQLiteDatabase mDatabase;
59 private final Context mContext;
60
61 private static final String DATABASE_NAME = "strongswan.db";
62 private static final String TABLE_VPNPROFILE = "vpnprofile";
63
64 private static final int DATABASE_VERSION = 14;
65
66 public static final String DATABASE_CREATE =
67 "CREATE TABLE " + TABLE_VPNPROFILE + " (" +
68 KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
69 KEY_UUID + " TEXT UNIQUE," +
70 KEY_NAME + " TEXT NOT NULL," +
71 KEY_GATEWAY + " TEXT NOT NULL," +
72 KEY_VPN_TYPE + " TEXT NOT NULL," +
73 KEY_USERNAME + " TEXT," +
74 KEY_PASSWORD + " TEXT," +
75 KEY_CERTIFICATE + " TEXT," +
76 KEY_USER_CERTIFICATE + " TEXT," +
77 KEY_MTU + " INTEGER," +
78 KEY_PORT + " INTEGER," +
79 KEY_SPLIT_TUNNELING + " INTEGER," +
80 KEY_LOCAL_ID + " TEXT," +
81 KEY_REMOTE_ID + " TEXT," +
82 KEY_EXCLUDED_SUBNETS + " TEXT," +
83 KEY_INCLUDED_SUBNETS + " TEXT," +
84 KEY_SELECTED_APPS + " INTEGER," +
85 KEY_SELECTED_APPS_LIST + " TEXT," +
86 KEY_NAT_KEEPALIVE + " INTEGER," +
87 KEY_FLAGS + " INTEGER" +
88 ");";
89 private static final String[] ALL_COLUMNS = new String[] {
90 KEY_ID,
91 KEY_UUID,
92 KEY_NAME,
93 KEY_GATEWAY,
94 KEY_VPN_TYPE,
95 KEY_USERNAME,
96 KEY_PASSWORD,
97 KEY_CERTIFICATE,
98 KEY_USER_CERTIFICATE,
99 KEY_MTU,
100 KEY_PORT,
101 KEY_SPLIT_TUNNELING,
102 KEY_LOCAL_ID,
103 KEY_REMOTE_ID,
104 KEY_EXCLUDED_SUBNETS,
105 KEY_INCLUDED_SUBNETS,
106 KEY_SELECTED_APPS,
107 KEY_SELECTED_APPS_LIST,
108 KEY_NAT_KEEPALIVE,
109 KEY_FLAGS,
110 };
111
112 private static class DatabaseHelper extends SQLiteOpenHelper
113 {
114 public DatabaseHelper(Context context)
115 {
116 super(context, DATABASE_NAME, null, DATABASE_VERSION);
117 }
118
119 @Override
120 public void onCreate(SQLiteDatabase database)
121 {
122 database.execSQL(DATABASE_CREATE);
123 }
124
125 @Override
126 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
127 {
128 Log.w(TAG, "Upgrading database from version " + oldVersion +
129 " to " + newVersion);
130 if (oldVersion < 2)
131 {
132 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_USER_CERTIFICATE +
133 " TEXT;");
134 }
135 if (oldVersion < 3)
136 {
137 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_VPN_TYPE +
138 " TEXT DEFAULT '';");
139 }
140 if (oldVersion < 4)
141 { /* remove NOT NULL constraint from username column */
142 updateColumns(db);
143 }
144 if (oldVersion < 5)
145 {
146 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_MTU +
147 " INTEGER;");
148 }
149 if (oldVersion < 6)
150 {
151 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_PORT +
152 " INTEGER;");
153 }
154 if (oldVersion < 7)
155 {
156 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SPLIT_TUNNELING +
157 " INTEGER;");
158 }
159 if (oldVersion < 8)
160 {
161 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_LOCAL_ID +
162 " TEXT;");
163 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_REMOTE_ID +
164 " TEXT;");
165 }
166 if (oldVersion < 9)
167 {
168 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_UUID +
169 " TEXT;");
170 updateColumns(db);
171 }
172 if (oldVersion < 10)
173 {
174 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_EXCLUDED_SUBNETS +
175 " TEXT;");
176 }
177 if (oldVersion < 11)
178 {
179 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_INCLUDED_SUBNETS +
180 " TEXT;");
181 }
182 if (oldVersion < 12)
183 {
184 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SELECTED_APPS +
185 " INTEGER;");
186 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SELECTED_APPS_LIST +
187 " TEXT;");
188 }
189 if (oldVersion < 13)
190 {
191 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_NAT_KEEPALIVE +
192 " INTEGER;");
193 }
194 if (oldVersion < 14)
195 {
196 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_FLAGS +
197 " INTEGER;");
198 }
199 }
200
201 private void updateColumns(SQLiteDatabase db)
202 {
203 db.beginTransaction();
204 try
205 {
206 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " RENAME TO tmp_" + TABLE_VPNPROFILE + ";");
207 db.execSQL(DATABASE_CREATE);
208 StringBuilder insert = new StringBuilder("INSERT INTO " + TABLE_VPNPROFILE + " SELECT ");
209 SQLiteQueryBuilder.appendColumns(insert, ALL_COLUMNS);
210 db.execSQL(insert.append(" FROM tmp_" + TABLE_VPNPROFILE + ";").toString());
211 db.execSQL("DROP TABLE tmp_" + TABLE_VPNPROFILE + ";");
212 db.setTransactionSuccessful();
213 }
214 finally
215 {
216 db.endTransaction();
217 }
218 }
219 }
220
221 /**
222 * Construct a new VPN profile data source. The context is used to
223 * open/create the database.
224 * @param context context used to access the database
225 */
226 public VpnProfileDataSource(Context context)
227 {
228 this.mContext = context;
229 }
230
231 /**
232 * Open the VPN profile data source. The database is automatically created
233 * if it does not yet exist. If that fails an exception is thrown.
234 * @return itself (allows to chain initialization calls)
235 * @throws SQLException if the database could not be opened or created
236 */
237 public VpnProfileDataSource open() throws SQLException
238 {
239 if (mDbHelper == null)
240 {
241 mDbHelper = new DatabaseHelper(mContext);
242 mDatabase = mDbHelper.getWritableDatabase();
243 }
244 return this;
245 }
246
247 /**
248 * Close the data source.
249 */
250 public void close()
251 {
252 if (mDbHelper != null)
253 {
254 mDbHelper.close();
255 mDbHelper = null;
256 }
257 }
258
259 /**
260 * Insert the given VPN profile into the database. On success the Id of
261 * the object is updated and the object returned.
262 *
263 * @param profile the profile to add
264 * @return the added VPN profile or null, if failed
265 */
266 public VpnProfile insertProfile(VpnProfile profile)
267 {
268 ContentValues values = ContentValuesFromVpnProfile(profile);
269 long insertId = mDatabase.insert(TABLE_VPNPROFILE, null, values);
270 if (insertId == -1)
271 {
272 return null;
273 }
274 profile.setId(insertId);
275 return profile;
276 }
277
278 /**
279 * Updates the given VPN profile in the database.
280 * @param profile the profile to update
281 * @return true if update succeeded, false otherwise
282 */
283 public boolean updateVpnProfile(VpnProfile profile)
284 {
285 long id = profile.getId();
286 ContentValues values = ContentValuesFromVpnProfile(profile);
287 return mDatabase.update(TABLE_VPNPROFILE, values, KEY_ID + " = " + id, null) > 0;
288 }
289
290 /**
291 * Delete the given VPN profile from the database.
292 * @param profile the profile to delete
293 * @return true if deleted, false otherwise
294 */
295 public boolean deleteVpnProfile(VpnProfile profile)
296 {
297 long id = profile.getId();
298 return mDatabase.delete(TABLE_VPNPROFILE, KEY_ID + " = " + id, null) > 0;
299 }
300
301 /**
302 * Get a single VPN profile from the database.
303 * @param id the ID of the VPN profile
304 * @return the profile or null, if not found
305 */
306 public VpnProfile getVpnProfile(long id)
307 {
308 VpnProfile profile = null;
309 Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS,
310 KEY_ID + "=" + id, null, null, null, null);
311 if (cursor.moveToFirst())
312 {
313 profile = VpnProfileFromCursor(cursor);
314 }
315 cursor.close();
316 return profile;
317 }
318
319 /**
320 * Get a single VPN profile from the database by its UUID.
321 * @param uuid the UUID of the VPN profile
322 * @return the profile or null, if not found
323 */
324 public VpnProfile getVpnProfile(UUID uuid)
325 {
326 VpnProfile profile = null;
327 Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS,
328 KEY_UUID + "='" + uuid.toString() + "'", null, null, null, null);
329 if (cursor.moveToFirst())
330 {
331 profile = VpnProfileFromCursor(cursor);
332 }
333 cursor.close();
334 return profile;
335 }
336
337 /**
338 * Get a list of all VPN profiles stored in the database.
339 * @return list of VPN profiles
340 */
341 public List<VpnProfile> getAllVpnProfiles()
342 {
343 List<VpnProfile> vpnProfiles = new ArrayList<VpnProfile>();
344
345 Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS, null, null, null, null, null);
346 cursor.moveToFirst();
347 while (!cursor.isAfterLast())
348 {
349 VpnProfile vpnProfile = VpnProfileFromCursor(cursor);
350 vpnProfiles.add(vpnProfile);
351 cursor.moveToNext();
352 }
353 cursor.close();
354 return vpnProfiles;
355 }
356
357 private VpnProfile VpnProfileFromCursor(Cursor cursor)
358 {
359 VpnProfile profile = new VpnProfile();
360 profile.setId(cursor.getLong(cursor.getColumnIndex(KEY_ID)));
361 profile.setUUID(getUUID(cursor, cursor.getColumnIndex(KEY_UUID)));
362 profile.setName(cursor.getString(cursor.getColumnIndex(KEY_NAME)));
363 profile.setGateway(cursor.getString(cursor.getColumnIndex(KEY_GATEWAY)));
364 profile.setVpnType(VpnType.fromIdentifier(cursor.getString(cursor.getColumnIndex(KEY_VPN_TYPE))));
365 profile.setUsername(cursor.getString(cursor.getColumnIndex(KEY_USERNAME)));
366 profile.setPassword(cursor.getString(cursor.getColumnIndex(KEY_PASSWORD)));
367 profile.setCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_CERTIFICATE)));
368 profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_USER_CERTIFICATE)));
369 profile.setMTU(getInt(cursor, cursor.getColumnIndex(KEY_MTU)));
370 profile.setPort(getInt(cursor, cursor.getColumnIndex(KEY_PORT)));
371 profile.setSplitTunneling(getInt(cursor, cursor.getColumnIndex(KEY_SPLIT_TUNNELING)));
372 profile.setLocalId(cursor.getString(cursor.getColumnIndex(KEY_LOCAL_ID)));
373 profile.setRemoteId(cursor.getString(cursor.getColumnIndex(KEY_REMOTE_ID)));
374 profile.setExcludedSubnets(cursor.getString(cursor.getColumnIndex(KEY_EXCLUDED_SUBNETS)));
375 profile.setIncludedSubnets(cursor.getString(cursor.getColumnIndex(KEY_INCLUDED_SUBNETS)));
376 profile.setSelectedAppsHandling(getInt(cursor, cursor.getColumnIndex(KEY_SELECTED_APPS)));
377 profile.setSelectedApps(cursor.getString(cursor.getColumnIndex(KEY_SELECTED_APPS_LIST)));
378 profile.setNATKeepAlive(getInt(cursor, cursor.getColumnIndex(KEY_NAT_KEEPALIVE)));
379 profile.setFlags(getInt(cursor, cursor.getColumnIndex(KEY_FLAGS)));
380 return profile;
381 }
382
383 private ContentValues ContentValuesFromVpnProfile(VpnProfile profile)
384 {
385 ContentValues values = new ContentValues();
386 values.put(KEY_UUID, profile.getUUID() != null ? profile.getUUID().toString() : null);
387 values.put(KEY_NAME, profile.getName());
388 values.put(KEY_GATEWAY, profile.getGateway());
389 values.put(KEY_VPN_TYPE, profile.getVpnType().getIdentifier());
390 values.put(KEY_USERNAME, profile.getUsername());
391 values.put(KEY_PASSWORD, profile.getPassword());
392 values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
393 values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias());
394 values.put(KEY_MTU, profile.getMTU());
395 values.put(KEY_PORT, profile.getPort());
396 values.put(KEY_SPLIT_TUNNELING, profile.getSplitTunneling());
397 values.put(KEY_LOCAL_ID, profile.getLocalId());
398 values.put(KEY_REMOTE_ID, profile.getRemoteId());
399 values.put(KEY_EXCLUDED_SUBNETS, profile.getExcludedSubnets());
400 values.put(KEY_INCLUDED_SUBNETS, profile.getIncludedSubnets());
401 values.put(KEY_SELECTED_APPS, profile.getSelectedAppsHandling().getValue());
402 values.put(KEY_SELECTED_APPS_LIST, profile.getSelectedApps());
403 values.put(KEY_NAT_KEEPALIVE, profile.getNATKeepAlive());
404 values.put(KEY_FLAGS, profile.getFlags());
405 return values;
406 }
407
408 private Integer getInt(Cursor cursor, int columnIndex)
409 {
410 return cursor.isNull(columnIndex) ? null : cursor.getInt(columnIndex);
411 }
412
413 private UUID getUUID(Cursor cursor, int columnIndex)
414 {
415 try
416 {
417 return cursor.isNull(columnIndex) ? null : UUID.fromString(cursor.getString(columnIndex));
418 }
419 catch (Exception e)
420 {
421 return null;
422 }
423 }
424 }