2013-09-05 01:37:59 +02:00
|
|
|
package org.kde.kdeconnect;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2013-06-17 12:23:08 +02:00
|
|
|
import android.content.Context;
|
2013-09-03 17:58:59 +02:00
|
|
|
import android.content.SharedPreferences;
|
|
|
|
import android.preference.PreferenceManager;
|
2013-08-08 18:45:55 +02:00
|
|
|
import android.provider.Settings;
|
2013-09-03 17:58:59 +02:00
|
|
|
import android.util.Base64;
|
2013-06-17 12:23:08 +02:00
|
|
|
import android.util.Log;
|
|
|
|
|
2013-07-30 20:18:30 +02:00
|
|
|
import org.json.JSONArray;
|
2013-06-06 05:57:06 +02:00
|
|
|
import org.json.JSONObject;
|
2013-11-06 18:33:38 +01:00
|
|
|
import org.kde.kdeconnect.Helpers.DeviceHelper;
|
2013-10-30 01:36:00 +01:00
|
|
|
import org.kde.kdeconnect.UserInterface.MainSettingsActivity;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2013-09-17 15:55:00 +02:00
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
import java.io.InputStream;
|
2013-09-03 17:58:59 +02:00
|
|
|
import java.nio.charset.Charset;
|
|
|
|
import java.security.PrivateKey;
|
|
|
|
import java.security.PublicKey;
|
2013-07-30 20:18:30 +02:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
import javax.crypto.Cipher;
|
|
|
|
|
2013-06-06 05:57:06 +02:00
|
|
|
public class NetworkPackage {
|
|
|
|
|
2013-09-20 15:44:58 +02:00
|
|
|
public final static int ProtocolVersion = 5;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2013-11-06 18:33:38 +01:00
|
|
|
//TODO: Move these to their respective plugins
|
2013-07-04 19:17:22 +02:00
|
|
|
public final static String PACKAGE_TYPE_IDENTITY = "kdeconnect.identity";
|
2013-09-03 17:58:59 +02:00
|
|
|
public final static String PACKAGE_TYPE_PAIR = "kdeconnect.pair";
|
|
|
|
public final static String PACKAGE_TYPE_ENCRYPTED = "kdeconnect.encrypted";
|
2013-07-04 19:17:22 +02:00
|
|
|
public final static String PACKAGE_TYPE_PING = "kdeconnect.ping";
|
2013-08-16 10:31:01 +02:00
|
|
|
public final static String PACKAGE_TYPE_TELEPHONY = "kdeconnect.telephony";
|
2013-08-01 02:30:58 +02:00
|
|
|
public final static String PACKAGE_TYPE_BATTERY = "kdeconnect.battery";
|
2014-01-06 19:30:55 +04:00
|
|
|
public final static String PACKAGE_TYPE_SFTP = "kdeconnect.sftp";
|
2013-08-01 02:30:58 +02:00
|
|
|
public final static String PACKAGE_TYPE_NOTIFICATION = "kdeconnect.notification";
|
2013-07-23 17:50:09 +02:00
|
|
|
public final static String PACKAGE_TYPE_CLIPBOARD = "kdeconnect.clipboard";
|
2013-07-29 18:42:12 +02:00
|
|
|
public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris";
|
2014-06-26 17:37:32 +00:00
|
|
|
public final static String PACKAGE_TYPE_MOUSEPAD = "kdeconnect.mousepad";
|
2013-10-01 03:25:18 +02:00
|
|
|
public final static String PACKAGE_TYPE_SHARE = "kdeconnect.share";
|
2013-07-04 19:17:22 +02:00
|
|
|
|
2013-06-06 05:57:06 +02:00
|
|
|
private long mId;
|
2013-07-04 14:24:46 +02:00
|
|
|
private String mType;
|
|
|
|
private JSONObject mBody;
|
2013-09-17 15:55:00 +02:00
|
|
|
private InputStream mPayload;
|
|
|
|
private JSONObject mPayloadTransferInfo;
|
2013-09-20 15:44:58 +02:00
|
|
|
private int mPayloadSize;
|
2013-06-06 05:57:06 +02:00
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
private NetworkPackage() {
|
2013-09-17 16:05:03 +02:00
|
|
|
|
2013-06-06 05:57:06 +02:00
|
|
|
}
|
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
public NetworkPackage(String type) {
|
|
|
|
mId = System.currentTimeMillis();
|
|
|
|
mType = type;
|
|
|
|
mBody = new JSONObject();
|
2013-09-18 17:23:53 +02:00
|
|
|
mPayload = null;
|
2013-09-20 15:44:58 +02:00
|
|
|
mPayloadSize = 0;
|
2013-09-17 15:55:00 +02:00
|
|
|
mPayloadTransferInfo = new JSONObject();
|
2013-06-06 05:57:06 +02:00
|
|
|
}
|
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
public String getType() {
|
2013-06-06 05:57:06 +02:00
|
|
|
return mType;
|
|
|
|
}
|
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
//Most commons getters and setters defined for convenience
|
|
|
|
public String getString(String key) { return mBody.optString(key,""); }
|
|
|
|
public String getString(String key, String defaultValue) { return mBody.optString(key,defaultValue); }
|
2013-08-16 10:31:01 +02:00
|
|
|
public void set(String key, String value) { if (value == null) return; try { mBody.put(key,value); } catch(Exception e) { } }
|
2013-07-04 14:24:46 +02:00
|
|
|
public int getInt(String key) { return mBody.optInt(key,-1); }
|
|
|
|
public int getInt(String key, int defaultValue) { return mBody.optInt(key,defaultValue); }
|
|
|
|
public void set(String key, int value) { try { mBody.put(key,value); } catch(Exception e) { } }
|
|
|
|
public boolean getBoolean(String key) { return mBody.optBoolean(key,false); }
|
|
|
|
public boolean getBoolean(String key, boolean defaultValue) { return mBody.optBoolean(key,defaultValue); }
|
|
|
|
public void set(String key, boolean value) { try { mBody.put(key,value); } catch(Exception e) { } }
|
2013-07-26 16:23:26 +02:00
|
|
|
public double getDouble(String key) { return mBody.optDouble(key,Double.NaN); }
|
|
|
|
public double getDouble(String key, double defaultValue) { return mBody.optDouble(key,defaultValue); }
|
|
|
|
public void set(String key, double value) { try { mBody.put(key,value); } catch(Exception e) { } }
|
2013-10-30 01:13:40 +01:00
|
|
|
public JSONArray getJSONArray(String key) { return mBody.optJSONArray(key); }
|
|
|
|
public void set(String key, JSONArray value) { try { mBody.put(key,value); } catch(Exception e) { } }
|
|
|
|
|
2013-07-30 20:18:30 +02:00
|
|
|
public ArrayList<String> getStringList(String key) {
|
|
|
|
JSONArray jsonArray = mBody.optJSONArray(key);
|
|
|
|
ArrayList<String> list = new ArrayList<String>();
|
|
|
|
int length = jsonArray.length();
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
|
|
try {
|
|
|
|
String str = jsonArray.getString(i);
|
|
|
|
list.add(str);
|
|
|
|
} catch(Exception e) {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
public ArrayList<String> getStringList(String key, ArrayList<String> defaultValue) {
|
|
|
|
if (mBody.has(key)) return getStringList(key);
|
|
|
|
else return defaultValue;
|
|
|
|
}
|
|
|
|
public void set(String key, ArrayList<String> value) {
|
|
|
|
try {
|
|
|
|
JSONArray jsonArray = new JSONArray();
|
|
|
|
for(String str : value) {
|
|
|
|
jsonArray.put(str);
|
|
|
|
}
|
|
|
|
mBody.put(key,jsonArray);
|
|
|
|
} catch(Exception e) {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public boolean has(String key) { return mBody.has(key); }
|
2013-07-04 14:24:46 +02:00
|
|
|
|
2013-09-20 15:44:58 +02:00
|
|
|
public boolean isEncrypted() { return mType.equals(PACKAGE_TYPE_ENCRYPTED); }
|
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
public String serialize() {
|
|
|
|
JSONObject jo = new JSONObject();
|
|
|
|
try {
|
2013-09-18 17:23:53 +02:00
|
|
|
jo.put("id", mId);
|
|
|
|
jo.put("type", mType);
|
|
|
|
jo.put("body", mBody);
|
|
|
|
if (hasPayload()) {
|
2013-09-20 15:44:58 +02:00
|
|
|
jo.put("payloadSize", mPayloadSize);
|
2013-09-18 17:23:53 +02:00
|
|
|
jo.put("payloadTransferInfo", mPayloadTransferInfo);
|
|
|
|
}
|
2013-07-04 14:24:46 +02:00
|
|
|
} catch(Exception e) {
|
2013-09-18 17:23:53 +02:00
|
|
|
e.printStackTrace();
|
|
|
|
Log.e("NetworkPackage", "Serialization exception");
|
2013-07-04 14:24:46 +02:00
|
|
|
}
|
2013-09-18 17:23:53 +02:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
//QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
|
|
|
|
String json = jo.toString().replace("\\/","/")+"\n";
|
2013-09-18 17:23:53 +02:00
|
|
|
|
2013-09-20 15:44:58 +02:00
|
|
|
if (!isEncrypted()) {
|
2013-10-01 03:28:12 +02:00
|
|
|
//Log.e("NetworkPackage.serialize", json);
|
2013-09-20 15:44:58 +02:00
|
|
|
}
|
2013-09-18 17:23:53 +02:00
|
|
|
|
2013-08-07 19:14:17 +02:00
|
|
|
return json;
|
2013-06-06 05:57:06 +02:00
|
|
|
}
|
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
static public NetworkPackage unserialize(String s) {
|
2013-09-18 17:23:53 +02:00
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
NetworkPackage np = new NetworkPackage();
|
|
|
|
try {
|
|
|
|
JSONObject jo = new JSONObject(s);
|
|
|
|
np.mId = jo.getLong("id");
|
|
|
|
np.mType = jo.getString("type");
|
|
|
|
np.mBody = jo.getJSONObject("body");
|
2013-09-20 15:44:58 +02:00
|
|
|
if (jo.has("payloadSize")) {
|
2013-09-17 15:55:00 +02:00
|
|
|
np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo");
|
2013-09-20 15:44:58 +02:00
|
|
|
np.mPayloadSize = jo.getInt("payloadSize");
|
2013-09-17 15:55:00 +02:00
|
|
|
} else {
|
|
|
|
np.mPayloadTransferInfo = new JSONObject();
|
2013-09-20 15:44:58 +02:00
|
|
|
np.mPayloadSize = 0;
|
2013-09-17 15:55:00 +02:00
|
|
|
}
|
2013-07-04 14:24:46 +02:00
|
|
|
} catch (Exception e) {
|
2013-09-18 17:23:53 +02:00
|
|
|
e.printStackTrace();
|
2013-09-27 00:07:14 +02:00
|
|
|
Log.e("NetworkPackage", "Unserialization exception unserializing "+s);
|
2013-07-04 14:24:46 +02:00
|
|
|
return null;
|
|
|
|
}
|
2013-09-20 15:44:58 +02:00
|
|
|
|
|
|
|
if (!np.isEncrypted()) {
|
2013-10-01 03:28:12 +02:00
|
|
|
//Log.e("NetworkPackage.unserialize", s);
|
2013-09-20 15:44:58 +02:00
|
|
|
}
|
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
return np;
|
2013-06-06 05:57:06 +02:00
|
|
|
}
|
2013-06-17 12:23:08 +02:00
|
|
|
|
2013-10-30 01:13:40 +01:00
|
|
|
public NetworkPackage encrypt(PublicKey publicKey) throws Exception {
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
String serialized = serialize();
|
|
|
|
|
|
|
|
int chunkSize = 128;
|
|
|
|
|
|
|
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
|
|
|
|
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
|
|
|
|
|
|
|
|
JSONArray chunks = new JSONArray();
|
|
|
|
while (serialized.length() > 0) {
|
|
|
|
if (serialized.length() < chunkSize) {
|
|
|
|
chunkSize = serialized.length();
|
|
|
|
}
|
|
|
|
String chunk = serialized.substring(0, chunkSize);
|
|
|
|
serialized = serialized.substring(chunkSize);
|
|
|
|
byte[] chunkBytes = chunk.getBytes(Charset.defaultCharset());
|
|
|
|
byte[] encryptedChunk;
|
|
|
|
encryptedChunk = cipher.doFinal(chunkBytes);
|
|
|
|
chunks.put(Base64.encodeToString(encryptedChunk, Base64.NO_WRAP));
|
|
|
|
}
|
|
|
|
|
2013-10-30 01:13:40 +01:00
|
|
|
//Log.i("NetworkPackage", "Encrypted " + chunks.length()+" chunks");
|
2013-09-03 17:58:59 +02:00
|
|
|
|
2013-10-30 01:13:40 +01:00
|
|
|
NetworkPackage encrypted = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_ENCRYPTED);
|
|
|
|
encrypted.set("data", chunks);
|
|
|
|
return encrypted;
|
2013-09-03 17:58:59 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public NetworkPackage decrypt(PrivateKey privateKey) throws Exception {
|
|
|
|
|
|
|
|
JSONArray chunks = mBody.getJSONArray("data");
|
|
|
|
|
|
|
|
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
|
|
|
|
cipher.init(Cipher.DECRYPT_MODE, privateKey);
|
|
|
|
|
|
|
|
String decryptedJson = "";
|
|
|
|
for (int i = 0; i < chunks.length(); i++) {
|
|
|
|
byte[] encryptedChunk = Base64.decode(chunks.getString(i), Base64.NO_WRAP);
|
|
|
|
String decryptedChunk = new String(cipher.doFinal(encryptedChunk));
|
|
|
|
decryptedJson += decryptedChunk;
|
|
|
|
}
|
|
|
|
|
|
|
|
return unserialize(decryptedJson);
|
|
|
|
}
|
|
|
|
|
2013-07-04 14:24:46 +02:00
|
|
|
static public NetworkPackage createIdentityPackage(Context context) {
|
2013-06-17 12:23:08 +02:00
|
|
|
|
2013-07-04 19:17:22 +02:00
|
|
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_IDENTITY);
|
2013-07-04 14:24:46 +02:00
|
|
|
|
2013-08-08 18:45:55 +02:00
|
|
|
String deviceId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
|
2013-07-04 14:24:46 +02:00
|
|
|
try {
|
2013-08-08 18:45:55 +02:00
|
|
|
np.mBody.put("deviceId", deviceId);
|
2013-10-30 01:36:00 +01:00
|
|
|
np.mBody.put("deviceName",
|
|
|
|
PreferenceManager.getDefaultSharedPreferences(context).getString(
|
|
|
|
MainSettingsActivity.KEY_DEVICE_NAME_PREFERENCE,
|
2013-11-06 18:33:38 +01:00
|
|
|
DeviceHelper.getDeviceName()));
|
2013-09-03 17:58:59 +02:00
|
|
|
np.mBody.put("protocolVersion", NetworkPackage.ProtocolVersion);
|
2013-11-06 18:33:38 +01:00
|
|
|
np.mBody.put("deviceType", DeviceHelper.isTablet()? "tablet" : "phone");
|
|
|
|
} catch (Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
Log.e("NetworkPacakge","Exception on createIdentityPackage");
|
2013-07-04 14:24:46 +02:00
|
|
|
}
|
2013-06-17 12:23:08 +02:00
|
|
|
|
|
|
|
return np;
|
2013-07-04 14:24:46 +02:00
|
|
|
|
2013-06-17 12:23:08 +02:00
|
|
|
}
|
|
|
|
|
2013-11-06 18:33:38 +01:00
|
|
|
|
2013-09-03 17:58:59 +02:00
|
|
|
static public NetworkPackage createPublicKeyPackage(Context context) {
|
|
|
|
|
|
|
|
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_PAIR);
|
|
|
|
|
|
|
|
np.set("pair", true);
|
|
|
|
|
|
|
|
SharedPreferences globalSettings = PreferenceManager.getDefaultSharedPreferences(context);
|
|
|
|
String publicKey = "-----BEGIN PUBLIC KEY-----\n" + globalSettings.getString("publicKey", "").trim()+ "\n-----END PUBLIC KEY-----\n";
|
|
|
|
np.set("publicKey", publicKey);
|
|
|
|
|
|
|
|
return np;
|
|
|
|
|
|
|
|
}
|
2013-06-17 12:23:08 +02:00
|
|
|
|
2013-09-17 15:55:00 +02:00
|
|
|
public void setPayload(byte[] data) {
|
2013-09-20 15:44:58 +02:00
|
|
|
setPayload(new ByteArrayInputStream(data), data.length);
|
2013-09-17 15:55:00 +02:00
|
|
|
}
|
|
|
|
|
2013-09-20 15:44:58 +02:00
|
|
|
public void setPayload(InputStream stream, int size) {
|
2013-09-17 15:55:00 +02:00
|
|
|
mPayload = stream;
|
2013-09-20 15:44:58 +02:00
|
|
|
mPayloadSize = size;
|
2013-09-17 15:55:00 +02:00
|
|
|
}
|
|
|
|
|
2013-09-20 15:44:58 +02:00
|
|
|
/*public void setPayload(InputStream stream) {
|
|
|
|
setPayload(stream, -1);
|
|
|
|
}*/
|
|
|
|
|
2013-09-17 15:55:00 +02:00
|
|
|
public InputStream getPayload() {
|
|
|
|
return mPayload;
|
|
|
|
}
|
|
|
|
|
2013-09-20 15:44:58 +02:00
|
|
|
public int getPayloadSize() {
|
|
|
|
return mPayloadSize;
|
|
|
|
}
|
|
|
|
|
2013-09-17 15:55:00 +02:00
|
|
|
public boolean hasPayload() {
|
2013-09-18 17:23:53 +02:00
|
|
|
return (mPayload != null);
|
2013-09-17 15:55:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean hasPayloadTransferInfo() {
|
|
|
|
return (mPayloadTransferInfo.length() > 0);
|
|
|
|
}
|
|
|
|
|
2013-09-18 17:23:53 +02:00
|
|
|
public JSONObject getPayloadTransferInfo() {
|
|
|
|
return mPayloadTransferInfo;
|
2013-09-17 16:05:03 +02:00
|
|
|
}
|
|
|
|
|
2013-09-18 17:23:53 +02:00
|
|
|
public void setPayloadTransferInfo(JSONObject payloadTransferInfo) {
|
|
|
|
mPayloadTransferInfo = payloadTransferInfo;
|
2013-09-17 16:05:03 +02:00
|
|
|
}
|
2013-06-06 05:57:06 +02:00
|
|
|
}
|