android: Add property for NAT-T keepalive interval
[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
56 private DatabaseHelper mDbHelper;
57 private SQLiteDatabase mDatabase;
58 private final Context mContext;
59
60 private static final String DATABASE_NAME = "strongswan.db";
61 private static final String TABLE_VPNPROFILE = "vpnprofile";
62
63 private static final int DATABASE_VERSION = 13;
64
65 public static final String DATABASE_CREATE =
66 "CREATE TABLE " + TABLE_VPNPROFILE + " (" +
67 KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
68 KEY_UUID + " TEXT UNIQUE," +
69 KEY_NAME + " TEXT NOT NULL," +
70 KEY_GATEWAY + " TEXT NOT NULL," +
71 KEY_VPN_TYPE + " TEXT NOT NULL," +
72 KEY_USERNAME + " TEXT," +
73 KEY_PASSWORD + " TEXT," +
74 KEY_CERTIFICATE + " TEXT," +
75 KEY_USER_CERTIFICATE + " TEXT," +
76 KEY_MTU + " INTEGER," +
77 KEY_PORT + " INTEGER," +
78 KEY_SPLIT_TUNNELING + " INTEGER," +
79 KEY_LOCAL_ID + " TEXT," +
80 KEY_REMOTE_ID + " TEXT," +
81 KEY_EXCLUDED_SUBNETS + " TEXT," +
82 KEY_INCLUDED_SUBNETS + " TEXT," +
83 KEY_SELECTED_APPS + " INTEGER," +
84 KEY_SELECTED_APPS_LIST + " TEXT," +
85 KEY_NAT_KEEPALIVE + " INTEGER" +
86 ");";
87 private static final String[] ALL_COLUMNS = new String[] {
88 KEY_ID,
89 KEY_UUID,
90 KEY_NAME,
91 KEY_GATEWAY,
92 KEY_VPN_TYPE,
93 KEY_USERNAME,
94 KEY_PASSWORD,
95 KEY_CERTIFICATE,
96 KEY_USER_CERTIFICATE,
97 KEY_MTU,
98 KEY_PORT,
99 KEY_SPLIT_TUNNELING,
100 KEY_LOCAL_ID,
101 KEY_REMOTE_ID,
102 KEY_EXCLUDED_SUBNETS,
103 KEY_INCLUDED_SUBNETS,
104 KEY_SELECTED_APPS,
105 KEY_SELECTED_APPS_LIST,
106 KEY_NAT_KEEPALIVE,
107 };
108
109 private static class DatabaseHelper extends SQLiteOpenHelper
110 {
111 public DatabaseHelper(Context context)
112 {
113 super(context, DATABASE_NAME, null, DATABASE_VERSION);
114 }
115
116 @Override
117 public void onCreate(SQLiteDatabase database)
118 {
119 database.execSQL(DATABASE_CREATE);
120 }
121
122 @Override
123 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
124 {
125 Log.w(TAG, "Upgrading database from version " + oldVersion +
126 " to " + newVersion);
127 if (oldVersion < 2)
128 {
129 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_USER_CERTIFICATE +
130 " TEXT;");
131 }
132 if (oldVersion < 3)
133 {
134 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_VPN_TYPE +
135 " TEXT DEFAULT '';");
136 }
137 if (oldVersion < 4)
138 { /* remove NOT NULL constraint from username column */
139 updateColumns(db);
140 }
141 if (oldVersion < 5)
142 {
143 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_MTU +
144 " INTEGER;");
145 }
146 if (oldVersion < 6)
147 {
148 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_PORT +
149 " INTEGER;");
150 }
151 if (oldVersion < 7)
152 {
153 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SPLIT_TUNNELING +
154 " INTEGER;");
155 }
156 if (oldVersion < 8)
157 {
158 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_LOCAL_ID +
159 " TEXT;");
160 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_REMOTE_ID +
161 " TEXT;");
162 }
163 if (oldVersion < 9)
164 {
165 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_UUID +
166 " TEXT;");
167 updateColumns(db);
168 }
169 if (oldVersion < 10)
170 {
171 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_EXCLUDED_SUBNETS +
172 " TEXT;");
173 }
174 if (oldVersion < 11)
175 {
176 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_INCLUDED_SUBNETS +
177 " TEXT;");
178 }
179 if (oldVersion < 12)
180 {
181 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SELECTED_APPS +
182 " INTEGER;");
183 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_SELECTED_APPS_LIST +
184 " TEXT;");
185 }
186 if (oldVersion < 13)
187 {
188 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " ADD " + KEY_NAT_KEEPALIVE +
189 " INTEGER;");
190 }
191 }
192
193 private void updateColumns(SQLiteDatabase db)
194 {
195 db.beginTransaction();
196 try
197 {
198 db.execSQL("ALTER TABLE " + TABLE_VPNPROFILE + " RENAME TO tmp_" + TABLE_VPNPROFILE + ";");
199 db.execSQL(DATABASE_CREATE);
200 StringBuilder insert = new StringBuilder("INSERT INTO " + TABLE_VPNPROFILE + " SELECT ");
201 SQLiteQueryBuilder.appendColumns(insert, ALL_COLUMNS);
202 db.execSQL(insert.append(" FROM tmp_" + TABLE_VPNPROFILE + ";").toString());
203 db.execSQL("DROP TABLE tmp_" + TABLE_VPNPROFILE + ";");
204 db.setTransactionSuccessful();
205 }
206 finally
207 {
208 db.endTransaction();
209 }
210 }
211 }
212
213 /**
214 * Construct a new VPN profile data source. The context is used to
215 * open/create the database.
216 * @param context context used to access the database
217 */
218 public VpnProfileDataSource(Context context)
219 {
220 this.mContext = context;
221 }
222
223 /**
224 * Open the VPN profile data source. The database is automatically created
225 * if it does not yet exist. If that fails an exception is thrown.
226 * @return itself (allows to chain initialization calls)
227 * @throws SQLException if the database could not be opened or created
228 */
229 public VpnProfileDataSource open() throws SQLException
230 {
231 if (mDbHelper == null)
232 {
233 mDbHelper = new DatabaseHelper(mContext);
234 mDatabase = mDbHelper.getWritableDatabase();
235 }
236 return this;
237 }
238
239 /**
240 * Close the data source.
241 */
242 public void close()
243 {
244 if (mDbHelper != null)
245 {
246 mDbHelper.close();
247 mDbHelper = null;
248 }
249 }
250
251 /**
252 * Insert the given VPN profile into the database. On success the Id of
253 * the object is updated and the object returned.
254 *
255 * @param profile the profile to add
256 * @return the added VPN profile or null, if failed
257 */
258 public VpnProfile insertProfile(VpnProfile profile)
259 {
260 ContentValues values = ContentValuesFromVpnProfile(profile);
261 long insertId = mDatabase.insert(TABLE_VPNPROFILE, null, values);
262 if (insertId == -1)
263 {
264 return null;
265 }
266 profile.setId(insertId);
267 return profile;
268 }
269
270 /**
271 * Updates the given VPN profile in the database.
272 * @param profile the profile to update
273 * @return true if update succeeded, false otherwise
274 */
275 public boolean updateVpnProfile(VpnProfile profile)
276 {
277 long id = profile.getId();
278 ContentValues values = ContentValuesFromVpnProfile(profile);
279 return mDatabase.update(TABLE_VPNPROFILE, values, KEY_ID + " = " + id, null) > 0;
280 }
281
282 /**
283 * Delete the given VPN profile from the database.
284 * @param profile the profile to delete
285 * @return true if deleted, false otherwise
286 */
287 public boolean deleteVpnProfile(VpnProfile profile)
288 {
289 long id = profile.getId();
290 return mDatabase.delete(TABLE_VPNPROFILE, KEY_ID + " = " + id, null) > 0;
291 }
292
293 /**
294 * Get a single VPN profile from the database.
295 * @param id the ID of the VPN profile
296 * @return the profile or null, if not found
297 */
298 public VpnProfile getVpnProfile(long id)
299 {
300 VpnProfile profile = null;
301 Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS,
302 KEY_ID + "=" + id, null, null, null, null);
303 if (cursor.moveToFirst())
304 {
305 profile = VpnProfileFromCursor(cursor);
306 }
307 cursor.close();
308 return profile;
309 }
310
311 /**
312 * Get a single VPN profile from the database by its UUID.
313 * @param uuid the UUID of the VPN profile
314 * @return the profile or null, if not found
315 */
316 public VpnProfile getVpnProfile(UUID uuid)
317 {
318 VpnProfile profile = null;
319 Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS,
320 KEY_UUID + "='" + uuid.toString() + "'", null, null, null, null);
321 if (cursor.moveToFirst())
322 {
323 profile = VpnProfileFromCursor(cursor);
324 }
325 cursor.close();
326 return profile;
327 }
328
329 /**
330 * Get a list of all VPN profiles stored in the database.
331 * @return list of VPN profiles
332 */
333 public List<VpnProfile> getAllVpnProfiles()
334 {
335 List<VpnProfile> vpnProfiles = new ArrayList<VpnProfile>();
336
337 Cursor cursor = mDatabase.query(TABLE_VPNPROFILE, ALL_COLUMNS, null, null, null, null, null);
338 cursor.moveToFirst();
339 while (!cursor.isAfterLast())
340 {
341 VpnProfile vpnProfile = VpnProfileFromCursor(cursor);
342 vpnProfiles.add(vpnProfile);
343 cursor.moveToNext();
344 }
345 cursor.close();
346 return vpnProfiles;
347 }
348
349 private VpnProfile VpnProfileFromCursor(Cursor cursor)
350 {
351 VpnProfile profile = new VpnProfile();
352 profile.setId(cursor.getLong(cursor.getColumnIndex(KEY_ID)));
353 profile.setUUID(getUUID(cursor, cursor.getColumnIndex(KEY_UUID)));
354 profile.setName(cursor.getString(cursor.getColumnIndex(KEY_NAME)));
355 profile.setGateway(cursor.getString(cursor.getColumnIndex(KEY_GATEWAY)));
356 profile.setVpnType(VpnType.fromIdentifier(cursor.getString(cursor.getColumnIndex(KEY_VPN_TYPE))));
357 profile.setUsername(cursor.getString(cursor.getColumnIndex(KEY_USERNAME)));
358 profile.setPassword(cursor.getString(cursor.getColumnIndex(KEY_PASSWORD)));
359 profile.setCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_CERTIFICATE)));
360 profile.setUserCertificateAlias(cursor.getString(cursor.getColumnIndex(KEY_USER_CERTIFICATE)));
361 profile.setMTU(getInt(cursor, cursor.getColumnIndex(KEY_MTU)));
362 profile.setPort(getInt(cursor, cursor.getColumnIndex(KEY_PORT)));
363 profile.setSplitTunneling(getInt(cursor, cursor.getColumnIndex(KEY_SPLIT_TUNNELING)));
364 profile.setLocalId(cursor.getString(cursor.getColumnIndex(KEY_LOCAL_ID)));
365 profile.setRemoteId(cursor.getString(cursor.getColumnIndex(KEY_REMOTE_ID)));
366 profile.setExcludedSubnets(cursor.getString(cursor.getColumnIndex(KEY_EXCLUDED_SUBNETS)));
367 profile.setIncludedSubnets(cursor.getString(cursor.getColumnIndex(KEY_INCLUDED_SUBNETS)));
368 profile.setSelectedAppsHandling(getInt(cursor, cursor.getColumnIndex(KEY_SELECTED_APPS)));
369 profile.setSelectedApps(cursor.getString(cursor.getColumnIndex(KEY_SELECTED_APPS_LIST)));
370 profile.setNATKeepAlive(getInt(cursor, cursor.getColumnIndex(KEY_NAT_KEEPALIVE)));
371 return profile;
372 }
373
374 private ContentValues ContentValuesFromVpnProfile(VpnProfile profile)
375 {
376 ContentValues values = new ContentValues();
377 values.put(KEY_UUID, profile.getUUID() != null ? profile.getUUID().toString() : null);
378 values.put(KEY_NAME, profile.getName());
379 values.put(KEY_GATEWAY, profile.getGateway());
380 values.put(KEY_VPN_TYPE, profile.getVpnType().getIdentifier());
381 values.put(KEY_USERNAME, profile.getUsername());
382 values.put(KEY_PASSWORD, profile.getPassword());
383 values.put(KEY_CERTIFICATE, profile.getCertificateAlias());
384 values.put(KEY_USER_CERTIFICATE, profile.getUserCertificateAlias());
385 values.put(KEY_MTU, profile.getMTU());
386 values.put(KEY_PORT, profile.getPort());
387 values.put(KEY_SPLIT_TUNNELING, profile.getSplitTunneling());
388 values.put(KEY_LOCAL_ID, profile.getLocalId());
389 values.put(KEY_REMOTE_ID, profile.getRemoteId());
390 values.put(KEY_EXCLUDED_SUBNETS, profile.getExcludedSubnets());
391 values.put(KEY_INCLUDED_SUBNETS, profile.getIncludedSubnets());
392 values.put(KEY_SELECTED_APPS, profile.getSelectedAppsHandling().getValue());
393 values.put(KEY_SELECTED_APPS_LIST, profile.getSelectedApps());
394 values.put(KEY_NAT_KEEPALIVE, profile.getNATKeepAlive());
395 return values;
396 }
397
398 private Integer getInt(Cursor cursor, int columnIndex)
399 {
400 return cursor.isNull(columnIndex) ? null : cursor.getInt(columnIndex);
401 }
402
403 private UUID getUUID(Cursor cursor, int columnIndex)
404 {
405 try
406 {
407 return cursor.isNull(columnIndex) ? null : UUID.fromString(cursor.getString(columnIndex));
408 }
409 catch (Exception e)
410 {
411 return null;
412 }
413 }
414 }