From 452455537f3ccad3210d35493725dcd44d210c84 Mon Sep 17 00:00:00 2001
From: Tiger Wang <ztw17@ic.ac.uk>
Date: Mon, 2 Dec 2019 00:47:40 +0000
Subject: [PATCH] Action on incoming XCDP messages

---
 src/connection/XMPP2.js         |  22 +++--
 src/intelligence-exfiltrator.js | 168 +++++++++++++++++++++-----------
 2 files changed, 121 insertions(+), 69 deletions(-)

diff --git a/src/connection/XMPP2.js b/src/connection/XMPP2.js
index 0081ac1..d14398d 100644
--- a/src/connection/XMPP2.js
+++ b/src/connection/XMPP2.js
@@ -1,9 +1,7 @@
 import { client, xml } from "@xmpp/client"
 import uuid from "uuid"
 
-// Assumes that both master and slave connection do not have peer ID conflicts,
-// and that they do not care when they receive a peer ID that does not exist.
-export default class XMPPConnection {
+export default class XMPPConnection extends EventTarget {
   async joinChannel(channel) {
     const channelIdent = `${channel}@conference.xmpp.lets-draw.live/${this.username}`
     const presence = xml(
@@ -34,6 +32,8 @@ export default class XMPPConnection {
   }
 
   constructor() {
+    super()
+
     this.username = uuid.v4().toString()
     this.online = false
     this.queue = []
@@ -51,19 +51,20 @@ export default class XMPPConnection {
 
     xmpp.on("offline", () => {
       this.online = false
-      console.log("⏹", "offline")
     })
 
     xmpp.on("stanza", async (stanza) => {
       if (stanza.is("message")) {
-        // await xmpp.send(xml("presence", { type: "unavailable" }))
-        // console.log(stanza)
-        // await xmpp.stop()
+        this.dispatchEvent(
+          new CustomEvent("stanza", {
+            detail: stanza,
+          }),
+        )
       }
     })
 
     xmpp.on("online", async (address) => {
-      console.log("â–¶", "online as", address.toString())
+      /*eslint no-unused-vars: ["error", { "args": "none" }]*/
 
       // Makes itself available
       await xmpp.send(xml("presence"))
@@ -81,7 +82,8 @@ export default class XMPPConnection {
   }
 
   sneakilySendTheOtherTeamOur(secrets) {
-    this.joinChannel("imperial")
-      .then(this.sendChannelMessage("imperial", secrets))
+    this.joinChannel("imperial").then(
+      this.sendChannelMessage("imperial", secrets),
+    )
   }
 }
diff --git a/src/intelligence-exfiltrator.js b/src/intelligence-exfiltrator.js
index 8cb26f7..86d0762 100644
--- a/src/intelligence-exfiltrator.js
+++ b/src/intelligence-exfiltrator.js
@@ -1,4 +1,6 @@
-import { computeErasureIntervals, combineErasureIntervals } from "./erasure.js"
+import {
+  /* computeErasureIntervals, */ combineErasureIntervals,
+} from "./erasure.js"
 import XMPP from "./connection/XMPP2.js"
 import { connect } from "./room.js"
 
@@ -6,10 +8,11 @@ const DEFAULT_ROOM = "imperial"
 
 let room = null
 let secureLine = null
-let divulgedUpTo = new Map();
+const divulgedUpTo = new Map()
 const pointPresenceMap = new Map()
+const pathIDsByXCDPIdentifier = new Map()
 
-function eraseEverythingAtPosition(x, y, radius, room) {
+/* function eraseEverythingAtPosition(x, y, radius, room) {
   const mousePos = [x, y]
   room.getPaths().forEach((points, pathID) => {
     const prevPathIntervals =
@@ -34,74 +37,115 @@ function eraseEverythingAtPosition(x, y, radius, room) {
       ),
     )
   })
-}
+} */
 
 const onRoomConnect = (room_) => {
   room = room_
-  secureLine = new XMPP();
+  secureLine = new XMPP()
 
   room.addEventListener("addOrUpdatePath", ({ detail: { id, points } }) => {
     if (points.length === 0) {
-      return;
+      return
     }
 
-    let upTo = divulgedUpTo.get(id);
+    let upTo = divulgedUpTo.get(id)
     if (upTo === undefined) {
-      upTo = 0;
+      pathIDsByXCDPIdentifier.set(id, id)
+      upTo = 0
     }
 
     if (upTo === 0) {
-      const point = points[0];
-      const colour = point[3];
-      const R = parseInt(colour.substring(1, 3), 16);
-      const G = parseInt(colour.substring(3, 5), 16);
-      const B = parseInt(colour.substring(5, 7), 16);
-      secureLine.sneakilySendTheOtherTeamOur(JSON.stringify({
-        "type": "ADD",
-        "identifier": id,
-        "weight": point[2],
-        "colour": [R, G, B],
-        "start": [point[0], point[1]]
-      }));
-      upTo++;
+      const point = points[0]
+      const colour = point[3]
+      const R = parseInt(colour.substring(1, 3), 16)
+      const G = parseInt(colour.substring(3, 5), 16)
+      const B = parseInt(colour.substring(5, 7), 16)
+      secureLine.sneakilySendTheOtherTeamOur(
+        JSON.stringify({
+          type: "ADD",
+          identifier: id,
+          weight: point[2],
+          colour: [R, G, B],
+          start: [point[0], point[1]],
+        }),
+      )
+      upTo++
     }
 
-    let batch = []
+    const batch = []
     for (; upTo !== points.length; upTo++) {
-      const point = points[upTo];
-      batch.push([point[0], point[1]]);
+      const point = points[upTo]
+      batch.push([point[0], point[1]])
     }
 
     if (batch.length !== 0) {
-      secureLine.sneakilySendTheOtherTeamOur(JSON.stringify({
-        "type": "APPEND",
-        "identifier": id,
-        "points": batch
-      }));
+      secureLine.sneakilySendTheOtherTeamOur(
+        JSON.stringify({
+          type: "APPEND",
+          identifier: id,
+          points: batch,
+        }),
+      )
     }
 
-    divulgedUpTo.set(id, upTo);
+    divulgedUpTo.set(id, upTo)
   }),
+    room.addEventListener(
+      "removedIntervalsChange",
+      ({ detail: { id, intervals, points } }) => {
+        const currentIntervals = combineErasureIntervals(
+          room.erasureIntervals[id] || {},
+          intervals,
+        )
+
+        room.erasureIntervals[id] = currentIntervals
+
+        const mapping = pointPresenceMap.get(id)
+        if (mapping === undefined || mapping.length != points.length) {
+          pointPresenceMap.set(id, Array(points.length).fill(true))
+        }
+
+        for (const point in currentIntervals) {
+          deletePoint(id, parseInt(point))
+        }
+      },
+    )
 
-  room.addEventListener(
-    "removedIntervalsChange",
-    ({ detail: { id, intervals, points } }) => {
-      const currentIntervals = combineErasureIntervals(
-        room.erasureIntervals[id] || {},
-        intervals,
-      )
+  secureLine.addEventListener("stanza", ({ detail: stanza }) => {
+    const content = stanza.children[0]
+    if (content.name !== "body") {
+      return
+    }
 
-      room.erasureIntervals[id] = currentIntervals;
+    const message = JSON.parse(content.children[0])
+    if (divulgedUpTo.get(message.identifier) !== undefined) {
+      // Don't play ourselves
+      return
+    }
 
-      if (pointPresenceMap.get(id) === undefined) {
-        pointPresenceMap.set(id, Array(points.length).fill(true))
+    const ourID = pathIDsByXCDPIdentifier.get(message.identifier)
+    if (message.type === "ADD") {
+      if (ourID !== undefined) {
+        return
       }
 
-      for (const point in currentIntervals) {
-        deletePoint(id, parseInt(point))
+      pathIDsByXCDPIdentifier.set(
+        message.identifier,
+        room.addPath([message.start[0], message.start[1], 5, "#000000"]),
+      )
+    } else if (message.type === "APPEND") {
+      if (ourID === undefined) {
+        // They're trying to hack us with an ID that wasn't added
+        // TODO: initiate DDOS against them in retaliation
+        return
       }
-    },
-  )
+
+      for (const index in message.points) {
+        const point = message.points[index]
+        room.extendPath(ourID, [point[0], point[1], 5, "#000000"])
+      }
+    }
+  })
 }
 
 const deletePoint = (lineID, offset) => {
@@ -110,33 +154,39 @@ const deletePoint = (lineID, offset) => {
     return
   }
 
-  if (offset > 0 && bLine[offset - 1] && offset < (bLine.length - 1) && bLine[offset + 1]) {
-    secureLine.sneakilySendTheOtherTeamOur(JSON.stringify({
-      "type": "BIFURCATE",
-      "identifier": lineID,
-      "start_offset": offset,
-      "end_offset": offset
-    }))
+  if (
+    offset > 0 &&
+    bLine[offset - 1] &&
+    offset < bLine.length - 1 &&
+    bLine[offset + 1]
+  ) {
+    secureLine.sneakilySendTheOtherTeamOur(
+      JSON.stringify({
+        type: "BIFURCATE",
+        identifier: lineID,
+        start_offset: offset,
+        end_offset: offset,
+      }),
+    )
   }
 
-  secureLine.sneakilySendTheOtherTeamOur(JSON.stringify({
-    "type": "DELETE",
-    "identifier": lineID,
-    "offset": offset
-  }))
+  secureLine.sneakilySendTheOtherTeamOur(
+    JSON.stringify({
+      type: "DELETE",
+      identifier: lineID,
+      offset: offset,
+    }),
+  )
 
   bLine[offset] = false
 }
 
-
 const tryRoomConnect = async (roomID) => {
   return await connect(roomID)
     .then(onRoomConnect)
     .catch((err) => alert(`Error connecting to a room:\n${err}`))
 }
 
-const pathIDsByPointerID = new Map()
-
 window.addEventListener("unload", () => {
   if (room) {
     room.disconnect()
-- 
GitLab