Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"htmlparser2": "^4.1.0",
"leven": "^3.1.0",
"marked": "^2.0.0",
"matrix-appservice-bridge": "^2.4.1",
"matrix-appservice-bridge": "^2.5.0",
"parse-entities": "^1.2.0",
"pg": "8.3.3",
"prom-client": "^11.2.1",
Expand Down
22 changes: 17 additions & 5 deletions src/xmppjs/XJSGateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { StzaPresenceItem, StzaMessage, StzaMessageSubject,
StzaPresenceError, StzaBase, StzaPresenceKick, PresenceAffiliation, PresenceRole } from "./Stanzas";
import { IGateway } from "../bifrost/Gateway";
import { GatewayMUCMembership, IGatewayMemberXmpp, IGatewayMemberMatrix } from "./GatewayMUCMembership";
import { XMPPStatusCode } from "./XMPPConstants";
import { XMPPFeatures, XMPPStatusCode } from "./XMPPConstants";
import { AutoRegistration } from "../AutoRegistration";
import { GatewayStateResolve } from "./GatewayStateResolve";
import { MatrixMembershipEvent } from "../MatrixTypes";
Expand Down Expand Up @@ -190,10 +190,21 @@ export class XmppJsGateway implements IGateway {
const preserveFrom = stanza.attrs.from;
try {
stanza.attrs.from = member!.anonymousJid;
const devices = this.members.getXmppMembersDevices(chatName);
for (const deviceJid of devices) {
stanza.attrs.to = deviceJid;
this.xmpp.xmppWriteToStream(stanza);
const members = this.members.getXmppMembers(chatName);
for (const member of members) {
// This is cached.
const canMulticast = (await this.xmpp.checkFeaturesForServer(member.realJid.domain)).includes(
XMPPFeatures.ExtendedStanzaAddressing,
)
if (canMulticast) {
stanza.attrs.to = member.realJid;
this.xmpp.xmppSend(stanza.toString());
} else {
member.devices.forEach((device) => {
stanza.attrs.to = device;
this.xmpp.xmppSend(stanza.toString());
});
}
}
} catch (err) {
log.warn("Failed to reflect XMPP message:", err);
Expand All @@ -205,6 +216,7 @@ export class XmppJsGateway implements IGateway {
}

public reflectXMPPStanza(chatName: string, stanza: StzaBase) {
// TODO: Consider optimising by using multicast
const xmppDevices = [...this.members.getXmppMembersDevices(chatName)];
return Promise.all(xmppDevices.map((device) => {
stanza.to = device;
Expand Down
51 changes: 34 additions & 17 deletions src/xmppjs/XJSInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { XmppJsGateway } from "./XJSGateway";
import { IStza, StzaBase, StzaIqDisco, StzaIqDiscoInfo, StzaIqPing, StzaIqPingError, StzaIqVcardRequest } from "./Stanzas";
import { Util } from "../Util";
import uuid from "uuid/v4";
import { XMPPFeatures } from "./XMPPConstants";

const xLog = Logging.get("XMPP-conn");
const log = Logging.get("XmppJsInstance");
Expand Down Expand Up @@ -58,6 +59,7 @@ class XmppProtocol extends BifrostProtocol {

export const XMPP_PROTOCOL = new XmppProtocol();
const SEEN_MESSAGES_SIZE = 16384;
const FEATURE_SET_EXPIRES_AFTER_MS = 24 * 60 * 60 * 1000; // 24hs

export class XmppJsInstance extends EventEmitter implements IBifrostInstance {
public readonly presenceCache: PresenceCache;
Expand All @@ -75,6 +77,7 @@ export class XmppJsInstance extends EventEmitter implements IBifrostInstance {
private xmppGateway: XmppJsGateway|null;
private activeMUCUsers: Set<string>;
private lastMessageInMUC: Map<string, {originIsMatrix: boolean, id: string}>;
private remoteServerFeatures = new Map<string, {expiresAfter: number, features: XMPPFeatures[]}>();
constructor(private config: Config) {
super();
this.canWrite = false;
Expand Down Expand Up @@ -427,23 +430,38 @@ export class XmppJsInstance extends EventEmitter implements IBifrostInstance {
const whoJid = jid(who);
who = `${whoJid.local}@${whoJid.domain}`;
log.info(`Fetching vCard for ${who}`);
const res = new Promise((resolve: (e: Element) => void, reject) => {
const timeout = setTimeout(() => reject(Error("Timeout")), 5000);
this.once(`iq.${id}`, (stanza: Element) => {
clearTimeout(timeout);
const vCard = (stanza.getChild("vCard") as unknown as Element); // Bad typigns.
if (vCard) {
resolve(vCard);
}
reject(Error("No vCard given"));
});
});
// Remove the resource
await this.xmppSend(
new StzaIqVcardRequest(sender || this.xmppAddress.toString(), who, id),
);
const iqRequest = new StzaIqVcardRequest(sender || this.xmppAddress.toString(), who, id);
Metrics.remoteCall("xmpp.iq.vc2");
return res;
const stanza = await this.sendIq(iqRequest);
const vCard = (stanza.getChild("vCard") as unknown as Element); // Bad typigns.
if (vCard) {
return vCard;
}
throw Error("No vCard in response");
}

public async checkFeaturesForServer(server: string): Promise<XMPPFeatures[]> {
const cachedFeatures = this.remoteServerFeatures.get(server);
if (cachedFeatures && cachedFeatures.expiresAfter < Date.now()) {
return cachedFeatures.features;
}
const iqRequest = new StzaIqDiscoInfo(this.myAddress.toString(), server, uuid());
try {
const result = await this.sendIq(iqRequest);
const features: XMPPFeatures[] = result.getChild("query").getChildren("feature").map((e) => e.attr('var'));
this.remoteServerFeatures.set(
server,
{
expiresAfter: Date.now() + FEATURE_SET_EXPIRES_AFTER_MS,
features,
}
);
return features;
}
catch (ex) {
log.warn(`Failed to determine server features`);
return [];
}
}

private generateIdforMsg(stanza: Element) {
Expand Down Expand Up @@ -540,7 +558,6 @@ export class XmppJsInstance extends EventEmitter implements IBifrostInstance {
}
}


private async handleMessageStanza(stanza: Element, alias: string|null) {
if (!stanza.attrs.from || !stanza.attrs.to) {
return;
Expand Down
1 change: 1 addition & 0 deletions src/xmppjs/XMPPConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export enum XMPPFeatures {
IqSearch = "jabber:iq:search",
MessageCorrection = "urn:xmpp:message-correct:0",
XHTMLIM = "http://jabber.org/protocol/xhtml-im",
ExtendedStanzaAddressing = "http://jabber.org/protocol/address"
}