File indexing completed on 2024-12-22 04:41:42

0001 /*
0002  * SPDX-FileCopyrightText: 2014 Albert Vaca Cintora <albertvaka@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 package org.kde.kdeconnect;
0008 
0009 import org.apache.commons.io.IOUtils;
0010 import org.json.JSONArray;
0011 import org.json.JSONException;
0012 import org.json.JSONObject;
0013 
0014 import java.io.ByteArrayInputStream;
0015 import java.io.IOException;
0016 import java.io.InputStream;
0017 import java.net.Socket;
0018 import java.util.ArrayList;
0019 import java.util.HashSet;
0020 import java.util.List;
0021 import java.util.Set;
0022 
0023 public class NetworkPacket {
0024 
0025     public final static String PACKET_TYPE_IDENTITY = "kdeconnect.identity";
0026     public final static String PACKET_TYPE_PAIR = "kdeconnect.pair";
0027 
0028     public final static int PACKET_REPLACEID_MOUSEMOVE = 0;
0029     public final static int PACKET_REPLACEID_PRESENTERPOINTER = 1;
0030 
0031     public static Set<String> protocolPacketTypes = new HashSet<String>() {{
0032         add(PACKET_TYPE_IDENTITY);
0033         add(PACKET_TYPE_PAIR);
0034     }};
0035 
0036     private long mId;
0037     String mType;
0038     private JSONObject mBody;
0039     private Payload mPayload;
0040     private JSONObject mPayloadTransferInfo;
0041     private volatile boolean canceled;
0042 
0043     private NetworkPacket() {
0044 
0045     }
0046 
0047     public NetworkPacket(String type) {
0048         mId = System.currentTimeMillis();
0049         mType = type;
0050         mBody = new JSONObject();
0051         mPayload = null;
0052         mPayloadTransferInfo = new JSONObject();
0053     }
0054 
0055     public boolean isCanceled() { return canceled; }
0056     public void cancel() { canceled = true; }
0057 
0058     public String getType() {
0059         return mType;
0060     }
0061 
0062     public long getId() {
0063         return mId;
0064     }
0065 
0066     //Most commons getters and setters defined for convenience
0067     public String getString(String key) {
0068         return mBody.optString(key, "");
0069     }
0070 
0071     public String getString(String key, String defaultValue) {
0072         return mBody.optString(key, defaultValue);
0073     }
0074 
0075     public void set(String key, String value) {
0076         if (value == null) return;
0077         try {
0078             mBody.put(key, value);
0079         } catch (Exception ignored) {
0080         }
0081     }
0082 
0083     public int getInt(String key) {
0084         return mBody.optInt(key, -1);
0085     }
0086 
0087     public int getInt(String key, int defaultValue) {
0088         return mBody.optInt(key, defaultValue);
0089     }
0090 
0091     public void set(String key, int value) {
0092         try {
0093             mBody.put(key, value);
0094         } catch (Exception ignored) {
0095         }
0096     }
0097 
0098     public long getLong(String key) {
0099         return mBody.optLong(key, -1);
0100     }
0101 
0102     public long getLong(String key, long defaultValue) {
0103         return mBody.optLong(key, defaultValue);
0104     }
0105 
0106     public void set(String key, long value) {
0107         try {
0108             mBody.put(key, value);
0109         } catch (Exception ignored) {
0110         }
0111     }
0112 
0113     public boolean getBoolean(String key) {
0114         return mBody.optBoolean(key, false);
0115     }
0116 
0117     public boolean getBoolean(String key, boolean defaultValue) {
0118         return mBody.optBoolean(key, defaultValue);
0119     }
0120 
0121     public void set(String key, boolean value) {
0122         try {
0123             mBody.put(key, value);
0124         } catch (Exception ignored) {
0125         }
0126     }
0127 
0128     public double getDouble(String key) {
0129         return mBody.optDouble(key, Double.NaN);
0130     }
0131 
0132     public double getDouble(String key, double defaultValue) {
0133         return mBody.optDouble(key, defaultValue);
0134     }
0135 
0136     public void set(String key, double value) {
0137         try {
0138             mBody.put(key, value);
0139         } catch (Exception ignored) {
0140         }
0141     }
0142 
0143     public JSONArray getJSONArray(String key) {
0144         return mBody.optJSONArray(key);
0145     }
0146 
0147     public void set(String key, JSONArray value) {
0148         try {
0149             mBody.put(key, value);
0150         } catch (Exception ignored) {
0151         }
0152     }
0153 
0154     public JSONObject getJSONObject(String key) {
0155         return mBody.optJSONObject(key);
0156     }
0157 
0158     public void set(String key, JSONObject value) {
0159         try {
0160             mBody.put(key, value);
0161         } catch (JSONException ignored) {
0162         }
0163     }
0164 
0165     public Set<String> getStringSet(String key) {
0166         JSONArray jsonArray = mBody.optJSONArray(key);
0167         if (jsonArray == null) return null;
0168         Set<String> list = new HashSet<>();
0169         int length = jsonArray.length();
0170         for (int i = 0; i < length; i++) {
0171             try {
0172                 String str = jsonArray.getString(i);
0173                 list.add(str);
0174             } catch (Exception ignored) {
0175             }
0176         }
0177         return list;
0178     }
0179 
0180     public Set<String> getStringSet(String key, Set<String> defaultValue) {
0181         if (mBody.has(key)) return getStringSet(key);
0182         else return defaultValue;
0183     }
0184 
0185     public void set(String key, Set<String> value) {
0186         try {
0187             JSONArray jsonArray = new JSONArray();
0188             for (String str : value) {
0189                 jsonArray.put(str);
0190             }
0191             mBody.put(key, jsonArray);
0192         } catch (Exception ignored) {
0193         }
0194     }
0195 
0196     public List<String> getStringList(String key) {
0197         JSONArray jsonArray = mBody.optJSONArray(key);
0198         if (jsonArray == null) return null;
0199         List<String> list = new ArrayList<>();
0200         int length = jsonArray.length();
0201         for (int i = 0; i < length; i++) {
0202             try {
0203                 String str = jsonArray.getString(i);
0204                 list.add(str);
0205             } catch (Exception ignored) {
0206             }
0207         }
0208         return list;
0209     }
0210 
0211     public List<String> getStringList(String key, List<String> defaultValue) {
0212         if (mBody.has(key)) return getStringList(key);
0213         else return defaultValue;
0214     }
0215 
0216     public void set(String key, List<String> value) {
0217         try {
0218             JSONArray jsonArray = new JSONArray();
0219             for (String str : value) {
0220                 jsonArray.put(str);
0221             }
0222             mBody.put(key, jsonArray);
0223         } catch (Exception ignored) {
0224         }
0225     }
0226 
0227     public boolean has(String key) {
0228         return mBody.has(key);
0229     }
0230 
0231     public String serialize() throws JSONException {
0232         JSONObject jo = new JSONObject();
0233         jo.put("id", mId);
0234         jo.put("type", mType);
0235         jo.put("body", mBody);
0236         if (hasPayload()) {
0237             jo.put("payloadSize", mPayload.payloadSize);
0238             jo.put("payloadTransferInfo", mPayloadTransferInfo);
0239         }
0240         //QJSon does not escape slashes, but Java JSONObject does. Converting to QJson format.
0241         return jo.toString().replace("\\/", "/") + "\n";
0242     }
0243 
0244     static public NetworkPacket unserialize(String s) throws JSONException {
0245 
0246         NetworkPacket np = new NetworkPacket();
0247         JSONObject jo = new JSONObject(s);
0248         np.mId = jo.getLong("id");
0249         np.mType = jo.getString("type");
0250         np.mBody = jo.getJSONObject("body");
0251         if (jo.has("payloadSize")) {
0252             np.mPayloadTransferInfo = jo.getJSONObject("payloadTransferInfo");
0253             np.mPayload = new Payload(jo.getLong("payloadSize"));
0254         } else {
0255             np.mPayloadTransferInfo = new JSONObject();
0256             np.mPayload = new Payload(0);
0257         }
0258         return np;
0259     }
0260 
0261     public void setPayload(Payload payload) { mPayload = payload; }
0262 
0263     public Payload getPayload() {
0264         return mPayload;
0265     }
0266 
0267     public long getPayloadSize() {
0268         return mPayload == null ? 0 : mPayload.payloadSize;
0269     }
0270 
0271     public boolean hasPayload() {
0272         return (mPayload != null && mPayload.payloadSize != 0);
0273     }
0274 
0275     public boolean hasPayloadTransferInfo() {
0276         return (mPayloadTransferInfo.length() > 0);
0277     }
0278 
0279     public JSONObject getPayloadTransferInfo() {
0280         return mPayloadTransferInfo;
0281     }
0282 
0283     public void setPayloadTransferInfo(JSONObject payloadTransferInfo) {
0284         mPayloadTransferInfo = payloadTransferInfo;
0285     }
0286 
0287     public static class Payload {
0288         private final InputStream inputStream;
0289         private final Socket inputSocket;
0290         private final long payloadSize;
0291 
0292         public Payload(long payloadSize) {
0293             this((InputStream)null, payloadSize);
0294         }
0295 
0296         public Payload(byte[] data) {
0297             this(new ByteArrayInputStream(data), data.length);
0298         }
0299 
0300         /**
0301          * <b>NOTE: Do not use this to set an SSLSockets InputStream as the payload, use Payload(Socket, long) instead because of this <a href="https://issuetracker.google.com/issues/37018094">bug</a></b>
0302          */
0303         public Payload(InputStream inputStream, long payloadSize) {
0304             this.inputSocket = null;
0305             this.inputStream = inputStream;
0306             this.payloadSize = payloadSize;
0307         }
0308 
0309         public Payload(Socket inputSocket, long payloadSize) throws IOException {
0310             this.inputSocket = inputSocket;
0311             this.inputStream = inputSocket.getInputStream();
0312             this.payloadSize = payloadSize;
0313         }
0314 
0315         /**
0316          * <b>NOTE: Do not close the InputStream directly call Payload.close() instead, this is because of this <a href="https://issuetracker.google.com/issues/37018094">bug</a></b>
0317          */
0318         public InputStream getInputStream() { return inputStream; }
0319         long getPayloadSize() { return payloadSize; }
0320 
0321         public void close() {
0322             //TODO: If socket only close socket if that also closes the streams that is
0323             try {
0324                 IOUtils.close(inputStream);
0325             } catch(IOException ignored) {}
0326 
0327             try {
0328                 IOUtils.close(inputSocket);
0329             } catch (IOException ignored) {}
0330         }
0331     }
0332 }