diff --git a/Client/Android/app/src/main/java/com/neptune/app/Backend/ConnectionManager.java b/Client/Android/app/src/main/java/com/neptune/app/Backend/ConnectionManager.java index 276c929..c5cd0e9 100644 --- a/Client/Android/app/src/main/java/com/neptune/app/Backend/ConnectionManager.java +++ b/Client/Android/app/src/main/java/com/neptune/app/Backend/ConnectionManager.java @@ -9,12 +9,16 @@ import org.json.JSONException; import org.json.JSONObject; +import java.io.BufferedOutputStream; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.OutputStreamWriter; import java.math.BigInteger; import java.net.HttpURLConnection; +import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.Socket; import java.net.URL; @@ -46,10 +50,9 @@ import kotlin.NotImplementedError; public class ConnectionManager { - protected boolean hasNegotiated; - protected Date lastCommunicatedTime; - protected java.net.Socket Socket; - // protected AsyncHttpClient AsyncHttpClient; // Eh maybe not + private boolean hasNegotiated; + private Date lastCommunicatedTime; + // protected AsyncHttpClient AsyncHttpClient; // Eh maybe not .. but probably private IPAddress IPAddress; private ConfigItem Configuration; @@ -76,10 +79,79 @@ public void sendRequest(JSONObject requestData) { // This sends a POST request // apiURL is the 'api' or command you're calling, request data the ... data, and callback ... nothing for now - public JSONObject sendRequest(String apiURL, JSONObject requestData) throws JSONException { //, Callable callback) { + public JSONObject sendRequest(String apiURL, JSONObject requestData) throws JSONException, MalformedURLException { //, Callable callback) { + String response = "{}"; + /* + { + connectionId: "conInitUUID", + command: "/api/v1/server/....", + data: "{}" + } + + example: + { + "conInitUUID": "{{conInitUUID}}", + "command":"/api/v1/echo", + "data":"{\"sampleKey\":\"sampleValue\",\"message\":\"Hey!\",\"boolean\":true}" + } + */ + + String packet = "{ \"conInitUUID\": \"" + this.conInitUUID + "\", \"command\": \"" + apiURL + "\" }"; // \"data\": \"" + requestData.toString().replaceAll("\"", "\\\\\"") + "\" }"; + JSONObject obj = new JSONObject(packet); + obj.put("data", requestData.toString()); + + response = sendHTTPPostRequest(new URL("http://" + this.IPAddress.toString() + "/api/v1/server/socket/" + this.socketUUID + "/http"), obj.toString()); + + return new JSONObject(response); + } - return new JSONObject("{}"); + public String sendHTTPPostRequest(URL url, String data) { + Log.d("Connection-Manager", "Sending request to: " + url); + try { + HttpURLConnection connectionA = null; + connectionA = (HttpURLConnection)url.openConnection(); + connectionA.setRequestMethod("POST"); + connectionA.setRequestProperty("Content-Type", "application/json"); + connectionA.setRequestProperty("Accept", "application/json"); + connectionA.setDoOutput(true); + //connection.setConnectTimeout(10000); + //connection.setReadTimeout(12500); + + // Write the data + Log.d("Connection-Manager", "Data to be sent: " + data); + //try(OutputStream stream = connectionA.getOutputStream()) { + // byte[] input = data.getBytes(StandardCharsets.UTF_8); + // stream.write(input, 0, input.length); + //} + + OutputStream out = new BufferedOutputStream(connectionA.getOutputStream()); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); + writer.write(data); + writer.flush(); + writer.close(); + out.close(); + + + int responseCode = connectionA.getResponseCode(); + + StringBuilder response = new StringBuilder(); + try(BufferedReader br = new BufferedReader(new InputStreamReader(connectionA.getInputStream(), StandardCharsets.UTF_8))) { + String responseLine = null; + while ((responseLine = br.readLine()) != null) { + response.append(responseLine.trim()); + } + } + connectionA.disconnect(); + + Log.d("Connection-Manager", "Got code: " + responseCode + "\nReceived response: " + response.toString()); + this.lastCommunicatedTime = new Date(); + return response.toString(); + } catch (IOException e) { + Log.d("Connection-Manager", "Server does not exist, or we cannot not connect"); + e.printStackTrace(); + } + return "{}"; } public boolean initiateConnection() { @@ -207,6 +279,10 @@ public boolean initiateConnection() { // validate confMsg this.socketUUID = serverResponse2.getString("socketUUID"); + this.hasNegotiated = true; + + // at some point + Log.d("Connection-Manager", "SocketUUID: " + this.socketUUID); // we good @@ -261,12 +337,12 @@ public IPAddress getIPAddress() { return IPAddress; } - public Socket getSocket() { - return Socket; - } - public Date getLastCommunicatedTime() { return lastCommunicatedTime; } + public boolean getHasNegotiated() { + return this.hasNegotiated; + } + } diff --git a/Client/Android/app/src/main/java/com/neptune/app/Backend/NeptuneNotification.java b/Client/Android/app/src/main/java/com/neptune/app/Backend/NeptuneNotification.java index 16cc0e6..c3b707c 100644 --- a/Client/Android/app/src/main/java/com/neptune/app/Backend/NeptuneNotification.java +++ b/Client/Android/app/src/main/java/com/neptune/app/Backend/NeptuneNotification.java @@ -1,18 +1,66 @@ package com.neptune.app.Backend; import android.app.Notification; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.service.notification.StatusBarNotification; +import android.util.Log; + import java.util.Map; -public class NeptuneNotification extends NotificationListenerService { +public class NeptuneNotification { - private Notification statusBarNotification; + private StatusBarNotification statusBarNotification; private Map pushedServers; - public Notification statusBarNotification(Notification statusBarNotification, Map pushedServers) { + public String title = ""; + public String text = ""; + public String appPackageName = ""; + public String subtext = ""; + public String appName = ""; + public int id = 0; + + + public NeptuneNotification(StatusBarNotification notification, Context context) throws Exception { + this.statusBarNotification = notification; + + Bundle extras = notification.getNotification().extras; + + if (extras.getString("android.title") == null) { //Some notifications are not handled correctly, so we'll just skip em + throw new Exception("Invalid notification"); + } + + String title = extras.getString("android.title"); + String text = ""; + if (extras.getCharSequence("android.text") != null) { + text = extras.getCharSequence("android.text").toString(); + } + int id1 = extras.getInt(Notification.EXTRA_SMALL_ICON); + Bitmap id = notification.getNotification().largeIcon; + + this.title = title; + this.appPackageName = notification.getPackageName(); + this.text = text; + this.subtext = extras.getString("android.subtext"); + + PackageManager pm = context.getPackageManager(); + ApplicationInfo ai; + try { + ai = pm.getApplicationInfo( this.appPackageName, 0); + } catch (final PackageManager.NameNotFoundException e) { + ai = null; + } + this.appName = (String) (ai != null ? pm.getApplicationLabel(ai) : "(unknown)"); + + this.id = notification.getId(); + } + + public NeptuneNotification(StatusBarNotification statusBarNotification, Map pushedServers) { this.statusBarNotification = statusBarNotification; this.pushedServers = pushedServers; - - return null; } public void activate() { @@ -23,7 +71,8 @@ public void dismiss() { } - public void pushToServer() { - + @Override + public String toString() { + return "{ \"action\": \"create\", \"title\": \"" + this.title + "\", \"contents\": { \"text\": \"" + this.text + "\" }, \"applicationPackage\": \"" + this.appPackageName + "\", \"applicationName\": \"" + this.appName + "\", \"notificationId\": \"" + this.id + "\", \"type\": \"text\" }"; } } diff --git a/Client/Android/app/src/main/java/com/neptune/app/Backend/NotificationListenerService.java b/Client/Android/app/src/main/java/com/neptune/app/Backend/NotificationListenerService.java index 329ce42..a682741 100644 --- a/Client/Android/app/src/main/java/com/neptune/app/Backend/NotificationListenerService.java +++ b/Client/Android/app/src/main/java/com/neptune/app/Backend/NotificationListenerService.java @@ -13,6 +13,8 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import com.neptune.app.MainActivity; + import java.io.ByteArrayOutputStream; public class NotificationListenerService extends android.service.notification.NotificationListenerService { @@ -73,6 +75,9 @@ public void onNotificationPosted(StatusBarNotification notification) { i.putExtra("notification_event", "onNotificationPosted :" + notification.getPackageName() + "\n"); sendBroadcast(i); */ + // Note, only do global filtering here. Things like THIS app get filtered.. + // The Server class will filter out whatever it doesn't want + //Different Approach to see if this would be better/works (May need help with figuring this out) String pack = notification.getPackageName(); String ticker = ""; @@ -113,6 +118,14 @@ public void onNotificationPosted(StatusBarNotification notification) { message.putExtra("icon", byteArray); } + NeptuneNotification notify = null; + try { + notify = new NeptuneNotification(notification, getApplicationContext()); + MainActivity.serverManager.processNotification(notify); + } catch (Exception e) { + e.printStackTrace(); + } + LocalBroadcastManager.getInstance(context).sendBroadcast(message); } diff --git a/Client/Android/app/src/main/java/com/neptune/app/Backend/Server.java b/Client/Android/app/src/main/java/com/neptune/app/Backend/Server.java index 1b1f057..aa11e5e 100644 --- a/Client/Android/app/src/main/java/com/neptune/app/Backend/Server.java +++ b/Client/Android/app/src/main/java/com/neptune/app/Backend/Server.java @@ -4,8 +4,10 @@ import android.app.Notification; import android.util.Log; +import org.json.JSONException; import org.json.JSONObject; +import java.net.MalformedURLException; import java.util.Date; import kotlin.NotImplementedError; @@ -44,9 +46,16 @@ public void setupConnectionManager() { connectionManager.initiateConnection(); } - public boolean sendNotification(Notification notification) { - - return false; + public void sendNotification(NeptuneNotification notification) { + try { + if (connectionManager.getHasNegotiated()) { + connectionManager.sendRequest("/api/v1/server/sendNotification", new JSONObject(notification.toString())); + } + } catch (JSONException e) { + e.printStackTrace(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } } public boolean sendClipboard(Object object ) { diff --git a/Client/Android/app/src/main/java/com/neptune/app/Backend/ServerManager.java b/Client/Android/app/src/main/java/com/neptune/app/Backend/ServerManager.java index dc1f961..7438f23 100644 --- a/Client/Android/app/src/main/java/com/neptune/app/Backend/ServerManager.java +++ b/Client/Android/app/src/main/java/com/neptune/app/Backend/ServerManager.java @@ -5,10 +5,10 @@ public class ServerManager { - private static Map servers; + private static Map servers = new HashMap(); + + public ServerManager() { - public ServerManager () { - servers = new HashMap(); } public void removeServer(Server s) { @@ -19,15 +19,9 @@ public void addServer(Server s) { servers.put(s.getFriendlyName(), s); } - public ServerManager serverManager() { - this.servers = this.servers; //I think we should set this equal to a new HashMap or something. A type of map that lets the frontend access the info and - //add, delete, or edit the information the easiest. - return null; - } - public Server pair(String name, IPAddress ipAddress) { + public void pair(String name, IPAddress ipAddress) { - return null; } public boolean unpair(Server server) { @@ -36,12 +30,10 @@ public boolean unpair(Server server) { } public Server getServer(String serverId) { - return this.servers.get(serverId); } public Server[] getServers() { - return servers.values().toArray(new Server[0]); // Maybe? } @@ -53,4 +45,10 @@ public void saveServers() { } + public void processNotification(NeptuneNotification notification) { + for (Server server : servers.values()) { + server.sendNotification(notification); + } + } + } diff --git a/Client/Android/app/src/main/java/com/neptune/app/MainActivity.java b/Client/Android/app/src/main/java/com/neptune/app/MainActivity.java index 19deb7d..ab473c0 100644 --- a/Client/Android/app/src/main/java/com/neptune/app/MainActivity.java +++ b/Client/Android/app/src/main/java/com/neptune/app/MainActivity.java @@ -28,7 +28,7 @@ import com.neptune.app.Backend.ServerManager; public class MainActivity extends AppCompatActivity implements RenameDialog.RenameDialogListener{ - public ServerManager serverManager = new ServerManager(); + public static ServerManager serverManager = new ServerManager(); public Server server; //public Config config private TextView devName; diff --git a/Documents/API.md b/Documents/API.md index 91aa0bd..3f3ef4d 100644 --- a/Documents/API.md +++ b/Documents/API.md @@ -9,15 +9,15 @@ "Packet" set between `Server<->client` ```json { - "connectionId": "{currentConnectionId}", + "conInitUUID": "{currentConnectionId}", "command": "{command being called (URL)}", "data": "{data being sent}", } ``` This data is a layer on the _actual_ data the client is sending. The `data` portion is encrypted using the shared AES key and contains the command, clientId, and command parameters. The server or client would receive this "packet" of data and peel it (decrypt the `data` portion) to read the request/response of the other application. This is to provide always applied encryption to requests and responses.\ -Before we have a connectionId, we add `"negotiating": true` to signify we're setting that up.\ -If the server receives a packet without a connectionId, only the `newSocketConnection` command will be accepted. +Before we have a conInitUUID, we add `"negotiating": true` to signify we're setting that up.\ +If the server receives a packet without a conInitUUID, only the `newSocketConnection` command will be accepted. ## Key negotiation, pairing diff --git a/Server/src/Classes/Notification.js b/Server/src/Classes/Notification.js index 0ba785c..d7c42e3 100644 --- a/Server/src/Classes/Notification.js +++ b/Server/src/Classes/Notification.js @@ -136,7 +136,7 @@ class Notification extends EventEmitter { // send the notification this.#notifierNotification = Notifier.notify({ title: data.title, - message: data.contents.subtext + "\n" + data.contents.text, + message: data.contents.text, // data.contents.subtext + "\n" + id: data.notificationId, }, function(err, response, metadata) { // this is kinda temporary, windows gets funky blah blah blah read note at top if (err) { diff --git a/Server/src/index.js b/Server/src/index.js index 1c0d431..36ed4c5 100644 --- a/Server/src/index.js +++ b/Server/src/index.js @@ -24,7 +24,7 @@ const debug = process.env; const displaySilly = false; // output the silly log level to console (it goes every other level > silly, silly is the lowest priority, literal spam) Error.stackTraceLimit = (debug)? 8 : 4; -Neptune.version = new Version(0, 0, 1, ((debug)?"debug":"release"), "notifier"); +Neptune.version = new Version(0, 0, 1, ((debug)?"debug":"release"), "PoC"); global.Neptune = Neptune; // Anywhere down the chain you can use process.Neptune. Allows us to get around providing `Neptune` to everything @@ -598,7 +598,6 @@ async function main() { if (req.socket.remoteAddress !== "::1") { client.IPAddress = new IPAddress(req.socket.remoteAddress, "25560"); - console.log(client.IPAddress); }