mirror of
https://github.com/jech/galene.git
synced 2024-11-22 00:25:58 +01:00
Client-side support for protocol version 2.
This does not yet support the new 'need-username' error.
This commit is contained in:
parent
397892d906
commit
fae045fb61
3 changed files with 78 additions and 29 deletions
|
@ -70,6 +70,14 @@ message types:
|
|||
- `dest`, the client-id of the destination client;
|
||||
- `privileged`, set by the server to indicate that the originating client
|
||||
had the `op` privilege at the time when it sent the message.
|
||||
- `value`, the value of the message (which can be of any type).
|
||||
|
||||
There are two kinds of errors. Unsolicited errors are sent using messages
|
||||
of type `usermessage` of kind `error` or `warning`. Errors sent in reply
|
||||
to a message use the same type as the usual reply, but with a specific
|
||||
kind (such as `fail`). In either case, the field `value` contains
|
||||
a human-readable error message, while the field `error`, if present,
|
||||
contains a stable, program-readable identifier for the error.
|
||||
|
||||
## Establishing and maintaining a connection
|
||||
|
||||
|
@ -81,15 +89,15 @@ start pipelining messages to the server.
|
|||
```javascript
|
||||
{
|
||||
type: 'handshake',
|
||||
version: ["1"],
|
||||
version: ["2"],
|
||||
id: id
|
||||
}
|
||||
```
|
||||
|
||||
The version field contains an array of supported protocol versions; the
|
||||
client may announce multiple versions, but the server will always reply
|
||||
with a singleton. If the field `id` is absent, then the peer doesn't
|
||||
originate streams.
|
||||
The version field contains an array of supported protocol versions, in
|
||||
decreasing preference order; the client may announce multiple versions,
|
||||
but the server will always reply with a singleton. If the field `id` is
|
||||
absent, then the peer doesn't originate streams.
|
||||
|
||||
A peer may, at any time, send a `ping` message.
|
||||
|
||||
|
@ -302,6 +310,7 @@ A chat message may be sent using a `chat` message.
|
|||
username: username,
|
||||
dest: dest-id,
|
||||
privileged: boolean,
|
||||
time: time,
|
||||
noecho: false,
|
||||
value: message
|
||||
}
|
||||
|
@ -313,8 +322,10 @@ originated by the server. The message is forwarded by the server without
|
|||
interpretation, the server only validates that the `source` and `username`
|
||||
fields are authentic. The field `privileged` is set to true by the server
|
||||
if the message was originated by a client with the `op` permission. The
|
||||
field `noecho` is set by the client if it doesn't wish to receive a copy
|
||||
of its own message.
|
||||
field `time` is the timestamp of the message, coded as a number in version
|
||||
1 of the protocol, and as a string in ISO 8601 format in later versions.
|
||||
The field `noecho` is set by the client if it doesn't wish to receive
|
||||
a copy of its own message.
|
||||
|
||||
The `chathistory` message is similar to the `chat` message, but carries
|
||||
a message taken from the chat history. Most clients should treat
|
||||
|
|
|
@ -2520,7 +2520,7 @@ function gotFileTransferEvent(state, data) {
|
|||
* @param {string} id
|
||||
* @param {string} dest
|
||||
* @param {string} username
|
||||
* @param {number} time
|
||||
* @param {Date} time
|
||||
* @param {boolean} privileged
|
||||
* @param {string} kind
|
||||
* @param {any} message
|
||||
|
@ -2605,16 +2605,15 @@ function formatLines(lines) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {number} time
|
||||
* @param {Date} time
|
||||
* @returns {string}
|
||||
*/
|
||||
function formatTime(time) {
|
||||
let delta = Date.now() - time;
|
||||
let date = new Date(time);
|
||||
let m = date.getMinutes();
|
||||
let delta = Date.now() - time.getTime();
|
||||
let m = time.getMinutes();
|
||||
if(delta > -30000)
|
||||
return date.getHours() + ':' + ((m < 10) ? '0' : '') + m;
|
||||
return date.toLocaleString();
|
||||
return time.getHours() + ':' + ((m < 10) ? '0' : '') + m;
|
||||
return time.toLocaleString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2622,7 +2621,7 @@ function formatTime(time) {
|
|||
* @property {string} [nick]
|
||||
* @property {string} [peerId]
|
||||
* @property {string} [dest]
|
||||
* @property {number} [time]
|
||||
* @property {Date} [time]
|
||||
*/
|
||||
|
||||
/** @type {lastMessage} */
|
||||
|
@ -2632,7 +2631,7 @@ let lastMessage = {};
|
|||
* @param {string} peerId
|
||||
* @param {string} dest
|
||||
* @param {string} nick
|
||||
* @param {number} time
|
||||
* @param {Date} time
|
||||
* @param {boolean} privileged
|
||||
* @param {boolean} history
|
||||
* @param {string} kind
|
||||
|
@ -2662,7 +2661,7 @@ function addToChatbox(peerId, dest, nick, time, privileged, history, kind, messa
|
|||
!time || !lastMessage.time) {
|
||||
doHeader = true;
|
||||
} else {
|
||||
let delta = time - lastMessage.time;
|
||||
let delta = time.getTime() - lastMessage.time.getTime();
|
||||
doHeader = delta < 0 || delta > 60000;
|
||||
}
|
||||
|
||||
|
@ -2725,7 +2724,7 @@ function addToChatbox(peerId, dest, nick, time, privileged, history, kind, messa
|
|||
* @param {string} message
|
||||
*/
|
||||
function localMessage(message) {
|
||||
return addToChatbox(null, null, null, Date.now(), false, false, '', message);
|
||||
return addToChatbox(null, null, null, new Date(), false, false, '', message);
|
||||
}
|
||||
|
||||
function clearChat() {
|
||||
|
@ -2960,7 +2959,7 @@ commands.msg = {
|
|||
throw new Error(`Unknown user ${p[0]}`);
|
||||
serverConnection.chat('', id, p[1]);
|
||||
addToChatbox(serverConnection.id, id, serverConnection.username,
|
||||
Date.now(), false, false, '', p[1]);
|
||||
new Date(), false, false, '', p[1]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -106,6 +106,12 @@ function ServerConnection() {
|
|||
* @type {WebSocket}
|
||||
*/
|
||||
this.socket = null;
|
||||
/**
|
||||
* The negotiated protocol version.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
this.version = null;
|
||||
/**
|
||||
* The set of all up streams, indexed by their id.
|
||||
*
|
||||
|
@ -187,7 +193,7 @@ function ServerConnection() {
|
|||
/**
|
||||
* onchat is called whenever a new chat message is received.
|
||||
*
|
||||
* @type {(this: ServerConnection, id: string, dest: string, username: string, time: number, privileged: boolean, history: boolean, kind: string, message: unknown) => void}
|
||||
* @type {(this: ServerConnection, id: string, dest: string, username: string, time: Date, privileged: boolean, history: boolean, kind: string, message: unknown) => void}
|
||||
*/
|
||||
this.onchat = null;
|
||||
/**
|
||||
|
@ -199,7 +205,7 @@ function ServerConnection() {
|
|||
* 'id' is non-null, 'privileged' indicates whether the message was
|
||||
* sent by an operator.
|
||||
*
|
||||
* @type {(this: ServerConnection, id: string, dest: string, username: string, time: number, privileged: boolean, kind: string, message: unknown) => void}
|
||||
* @type {(this: ServerConnection, id: string, dest: string, username: string, time: Date, privileged: boolean, kind: string, message: unknown) => void}
|
||||
*/
|
||||
this.onusermessage = null;
|
||||
/**
|
||||
|
@ -225,6 +231,7 @@ function ServerConnection() {
|
|||
* @property {string} type
|
||||
* @property {Array<string>} [version]
|
||||
* @property {string} [kind]
|
||||
* @property {string} [error]
|
||||
* @property {string} [id]
|
||||
* @property {string} [replace]
|
||||
* @property {string} [source]
|
||||
|
@ -239,7 +246,7 @@ function ServerConnection() {
|
|||
* @property {string} [group]
|
||||
* @property {unknown} [value]
|
||||
* @property {boolean} [noecho]
|
||||
* @property {number} [time]
|
||||
* @property {string|number} [time]
|
||||
* @property {string} [sdp]
|
||||
* @property {RTCIceCandidate} [candidate]
|
||||
* @property {string} [label]
|
||||
|
@ -291,7 +298,7 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
this.socket.onopen = function(e) {
|
||||
sc.send({
|
||||
type: 'handshake',
|
||||
version: ["1"],
|
||||
version: ["2", "1"],
|
||||
id: sc.id,
|
||||
});
|
||||
if(sc.onconnected)
|
||||
|
@ -324,10 +331,23 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
this.socket.onmessage = function(e) {
|
||||
let m = JSON.parse(e.data);
|
||||
switch(m.type) {
|
||||
case 'handshake':
|
||||
if(!m.version || !m.version.includes('1'))
|
||||
console.warn(`Unexpected protocol version ${m.version}.`);
|
||||
case 'handshake': {
|
||||
/** @type {string} */
|
||||
let v;
|
||||
if(!m.version || !(m.version instanceof Array) ||
|
||||
m.version.length < 1 || typeof(m.version[0]) !== 'string') {
|
||||
v = null;
|
||||
} else {
|
||||
v = m.version[0];
|
||||
}
|
||||
if(v === "1" || v === "2") {
|
||||
sc.version = v;
|
||||
} else {
|
||||
console.warn(`Unknown protocol version ${v || m.version}`);
|
||||
sc.version = "1"
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'offer':
|
||||
sc.gotOffer(m.id, m.label, m.source, m.username,
|
||||
m.sdp, m.replace);
|
||||
|
@ -419,8 +439,8 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
case 'chathistory':
|
||||
if(sc.onchat)
|
||||
sc.onchat.call(
|
||||
sc, m.source, m.dest, m.username, m.time, m.privileged,
|
||||
m.type === 'chathistory', m.kind, m.value,
|
||||
sc, m.source, m.dest, m.username, parseTime(m.time),
|
||||
m.privileged, m.type === 'chathistory', m.kind, m.value,
|
||||
);
|
||||
break;
|
||||
case 'usermessage':
|
||||
|
@ -428,7 +448,7 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
sc.fileTransfer(m.source, m.username, m.value);
|
||||
else if(sc.onusermessage)
|
||||
sc.onusermessage.call(
|
||||
sc, m.source, m.dest, m.username, m.time,
|
||||
sc, m.source, m.dest, m.username, parseTime(m.time),
|
||||
m.privileged, m.kind, m.value,
|
||||
);
|
||||
break;
|
||||
|
@ -448,6 +468,25 @@ ServerConnection.prototype.connect = async function(url) {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Protocol version 1 uses integers for dates, later versions use dates in
|
||||
* ISO 8601 format. This function takes a date in either format and
|
||||
* returns a Date object.
|
||||
*
|
||||
* @param {string|number} value
|
||||
* @returns {Date}
|
||||
*/
|
||||
function parseTime(value) {
|
||||
if(!value)
|
||||
return null;
|
||||
try {
|
||||
return new Date(value);
|
||||
} catch(e) {
|
||||
console.warn(`Couldn't parse ${value}:`, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* join requests to join a group. The onjoined callback will be called
|
||||
* when we've effectively joined.
|
||||
|
|
Loading…
Reference in a new issue