Refactor WebSocket message interfaces

Changes:
- Replace AccountTextMessage with an extendable ContactMessage interface
- Add accountId parameter to server-side WebSocket callbacks
- Set the accountId for WebRTC messages on server-side for security
- Rename all WebRTC and SDP variables to proper camelCase or PascalCase

GitLab: #147
Change-Id: I125b5431821b03ef4d46b751eb1c13830017ccff
diff --git a/server/src/app.ts b/server/src/app.ts
index 27e6e99..3e27a88 100644
--- a/server/src/app.ts
+++ b/server/src/app.ts
@@ -31,7 +31,7 @@
 import { defaultModeratorsRouter } from './routers/default-moderators-router.js';
 import { nameserverRouter } from './routers/nameserver-router.js';
 import { setupRouter } from './routers/setup-router.js';
-import { bindWebRTCCallbacks } from './websocket/webrtc-handler.js';
+import { bindWebRtcCallbacks } from './websocket/webrtc-handler.js';
 
 @Service()
 export class App {
@@ -57,7 +57,7 @@
     this.app.use('/ns', nameserverRouter);
 
     // Setup WebSocket callbacks
-    bindWebRTCCallbacks();
+    bindWebRtcCallbacks();
 
     // Setup 404 error handling
     this.app.use((_req, res) => {
diff --git a/server/src/jamid/jamid.ts b/server/src/jamid/jamid.ts
index e7107b6..30439ef 100644
--- a/server/src/jamid/jamid.ts
+++ b/server/src/jamid/jamid.ts
@@ -19,7 +19,6 @@
 
 import {
   AccountDetails,
-  AccountTextMessage,
   ConversationMessage,
   Message,
   VolatileDetails,
@@ -227,10 +226,10 @@
     return stringVectToArray(this.jamiSwig.getAccountList());
   }
 
-  sendAccountTextMessage(from: string, to: string, message: string): void {
+  sendAccountTextMessage(accountId: string, contactId: string, message: string): void {
     const messageStringMap: StringMap = new this.jamiSwig.StringMap();
     messageStringMap.set('application/json', message);
-    this.jamiSwig.sendAccountTextMessage(from, to, messageStringMap);
+    this.jamiSwig.sendAccountTextMessage(accountId, contactId, messageStringMap);
   }
 
   // TODO: Add interface for returned type
@@ -379,6 +378,7 @@
         `Received VolatileDetailsChanged: {"accountId":"${accountId}",` +
           `"details":{"Account.registeredName":"${username}", ...}}`
       );
+
       if (username) {
         // Keep map of usernames to account IDs
         this.usernamesToAccountIds.set(username, accountId);
@@ -401,9 +401,10 @@
       log.debug(`Received KnownDevicesChanged: {"accountId":"${accountId}", ...}`);
     });
 
-    this.events.onIncomingAccountMessage.subscribe((signal) => {
+    this.events.onIncomingAccountMessage.subscribe(<T extends WebSocketMessageType>(signal: IncomingAccountMessage) => {
       log.debug('Received IncomingAccountMessage:', JSON.stringify(signal));
-      const message: WebSocketMessage<any> = JSON.parse(signal.payload['application/json']);
+
+      const message: WebSocketMessage<T> = JSON.parse(signal.payload['application/json']);
 
       if (message === undefined) {
         log.warn('Undefined account message');
@@ -420,13 +421,7 @@
         return;
       }
 
-      const data: AccountTextMessage<unknown> = {
-        from: signal.from,
-        to: signal.accountId,
-        message: message.data.message,
-      };
-
-      this.webSocketServer.send(signal.accountId, message.type, data);
+      this.webSocketServer.send(signal.accountId, message.type, message.data);
     });
 
     this.events.onAccountMessageStatusChanged.subscribe((signal) => {
diff --git a/server/src/websocket/webrtc-handler.ts b/server/src/websocket/webrtc-handler.ts
index abef6e7..da4a374 100644
--- a/server/src/websocket/webrtc-handler.ts
+++ b/server/src/websocket/webrtc-handler.ts
@@ -15,51 +15,39 @@
  * License along with this program.  If not, see
  * <https://www.gnu.org/licenses/>.
  */
-import { WebSocketMessageTable, WebSocketMessageType } from 'jami-web-common';
+import { ContactMessage, WebSocketMessageType } from 'jami-web-common';
 import log from 'loglevel';
 import { Container } from 'typedi';
 
 import { Jamid } from '../jamid/jamid.js';
 import { WebSocketServer } from './websocket-server.js';
 
-const jamid = Container.get(Jamid);
-const webSocketServer = Container.get(WebSocketServer);
-
-const webRTCWebSocketMessageTypes = [
-  WebSocketMessageType.IceCandidate,
-  WebSocketMessageType.WebRTCOffer,
-  WebSocketMessageType.WebRTCAnswer,
+const webRtcWebSocketMessageTypes = [
   WebSocketMessageType.CallBegin,
   WebSocketMessageType.CallAccept,
   WebSocketMessageType.CallRefuse,
   WebSocketMessageType.CallEnd,
+  WebSocketMessageType.WebRtcOffer,
+  WebSocketMessageType.WebRtcAnswer,
+  WebSocketMessageType.WebRtcIceCandidate,
 ] as const;
 
-type WebRTCWebSocketMessageType = typeof webRTCWebSocketMessageTypes[number];
+const jamid = Container.get(Jamid);
+const webSocketServer = Container.get(WebSocketServer);
 
-function sendWebRTCData<T extends WebRTCWebSocketMessageType>(
-  type: WebRTCWebSocketMessageType,
-  data: Partial<WebSocketMessageTable[T]>
-) {
-  if (data.from === undefined || data.to === undefined) {
-    log.warn('Message is not a valid AccountTextMessage (missing from or to fields)');
-    return;
-  }
-  log.info('Handling WebRTC message of type:', type);
-  jamid.sendAccountTextMessage(
-    data.from,
-    data.to,
-    JSON.stringify({
-      type,
-      data,
-    })
-  );
-}
-
-export function bindWebRTCCallbacks() {
-  for (const messageType of webRTCWebSocketMessageTypes) {
-    webSocketServer.bind(messageType, (data) => {
-      sendWebRTCData(messageType, data);
+export function bindWebRtcCallbacks() {
+  for (const messageType of webRtcWebSocketMessageTypes) {
+    webSocketServer.bind(messageType, (accountId, data) => {
+      sendWebRtcData(messageType, accountId, data);
     });
   }
 }
+
+function sendWebRtcData(type: WebSocketMessageType, accountId: string, data: Partial<ContactMessage>) {
+  if (data.contactId === undefined) {
+    log.warn('Message is not a valid ContactMessage (missing contactId field)');
+    return;
+  }
+
+  jamid.sendAccountTextMessage(accountId, data.contactId, JSON.stringify({ type, data }));
+}
diff --git a/server/src/websocket/websocket-server.ts b/server/src/websocket/websocket-server.ts
index ff28bb9..c1b9904 100644
--- a/server/src/websocket/websocket-server.ts
+++ b/server/src/websocket/websocket-server.ts
@@ -18,14 +18,7 @@
 import { IncomingMessage } from 'node:http';
 import { Duplex } from 'node:stream';
 
-import {
-  buildWebSocketCallbacks,
-  WebSocketCallback,
-  WebSocketCallbacks,
-  WebSocketMessage,
-  WebSocketMessageTable,
-  WebSocketMessageType,
-} from 'jami-web-common';
+import { WebSocketMessage, WebSocketMessageTable, WebSocketMessageType } from 'jami-web-common';
 import log from 'loglevel';
 import { Service } from 'typedi';
 import { URL } from 'whatwg-url';
@@ -33,13 +26,24 @@
 
 import { verifyJwt } from '../utils/jwt.js';
 
+type WebSocketCallback<T extends WebSocketMessageType> = (accountId: string, data: WebSocketMessageTable[T]) => void;
+
+type WebSocketCallbacks = {
+  [key in WebSocketMessageType]: Set<WebSocketCallback<key>>;
+};
+
 @Service()
 export class WebSocketServer {
   private wss = new WebSocket.WebSocketServer({ noServer: true });
   private sockets = new Map<string, WebSocket.WebSocket[]>();
-  private callbacks: WebSocketCallbacks = buildWebSocketCallbacks();
+  private callbacks: WebSocketCallbacks;
 
   constructor() {
+    this.callbacks = {} as WebSocketCallbacks;
+    for (const messageType of Object.values(WebSocketMessageType)) {
+      this.callbacks[messageType] = new Set<WebSocketCallback<typeof messageType>>();
+    }
+
     this.wss.on('connection', (ws: WebSocket.WebSocket, _request: IncomingMessage, accountId: string) => {
       log.info('New connection for account', accountId);
       const accountSockets = this.sockets.get(accountId);
@@ -63,7 +67,7 @@
 
         const callbacks = this.callbacks[message.type];
         for (const callback of callbacks) {
-          callback(message.data);
+          callback(accountId, message.data);
         }
       });