/* global $, APP, config, Strophe*/
var logger = require("jitsi-meet-logger").getLogger(__filename);
var EventEmitter = require("events");
var Pako = require("pako");
var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
var RTCEvents = require("../../service/RTC/RTCEvents");
var XMPPEvents = require("../../service/xmpp/XMPPEvents");
var JitsiConnectionErrors = require("../../JitsiConnectionErrors");
var JitsiConnectionEvents = require("../../JitsiConnectionEvents");
var RTC = require("../RTC/RTC");
var authenticatedUser = false;
function createConnection(bosh) {
bosh = bosh || '/http-bind';
return new Strophe.Connection(bosh);
};
//!!!!!!!!!! FIXME: ...
function initStrophePlugins(XMPP)
{
require("./strophe.emuc")(XMPP);
require("./strophe.jingle")(XMPP, XMPP.eventEmitter);
// require("./strophe.moderate")(XMPP, eventEmitter);
require("./strophe.util")();
require("./strophe.ping")(XMPP, XMPP.eventEmitter);
require("./strophe.rayo")();
require("./strophe.logger")();
}
//!!!!!!!!!! FIXME: ...
///**
// * If given localStream is video one this method will advertise it's
// * video type in MUC presence.
// * @param localStream new or modified LocalStream.
// */
//function broadcastLocalVideoType(localStream) {
// if (localStream.videoType)
// XMPP.addToPresence('videoType', localStream.videoType);
//}
//
//function registerListeners() {
// RTC.addStreamListener(
// broadcastLocalVideoType,
// StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED
// );
// RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, function (devices) {
// XMPP.addToPresence("devices", devices);
// });
//}
function XMPP(options) {
this.eventEmitter = new EventEmitter();
this.connection = null;
this.disconnectInProgress = false;
this.forceMuted = false;
this.options = options;
initStrophePlugins(this);
// registerListeners();
this.connection = createConnection(options.bosh);
}
XMPP.prototype.getConnection = function(){ return connection; };
XMPP.prototype._connect = function (jid, password) {
var self = this;
// connection.connect() starts the connection process.
//
// As the connection process proceeds, the user supplied callback will
// be triggered multiple times with status updates. The callback should
// take two arguments - the status code and the error condition.
//
// The status code will be one of the values in the Strophe.Status
// constants. The error condition will be one of the conditions defined
// in RFC 3920 or the condition ‘strophe-parsererror’.
//
// The Parameters wait, hold and route are optional and only relevant
// for BOSH connections. Please see XEP 124 for a more detailed
// explanation of the optional parameters.
//
// Connection status constants for use by the connection handler
// callback.
//
// Status.ERROR - An error has occurred (websockets specific)
// Status.CONNECTING - The connection is currently being made
// Status.CONNFAIL - The connection attempt failed
// Status.AUTHENTICATING - The connection is authenticating
// Status.AUTHFAIL - The authentication attempt failed
// Status.CONNECTED - The connection has succeeded
// Status.DISCONNECTED - The connection has been terminated
// Status.DISCONNECTING - The connection is currently being terminated
// Status.ATTACHED - The connection has been attached
var anonymousConnectionFailed = false;
var connectionFailed = false;
var lastErrorMsg;
this.connection.connect(jid, password, function (status, msg) {
logger.log("(TIME) Strophe " + Strophe.getStatusString(status) +
(msg ? "[" + msg + "]" : "") + "\t:" + window.performance.now());
if (status === Strophe.Status.CONNECTED) {
if (self.options.useStunTurn) {
self.connection.jingle.getStunAndTurnCredentials();
}
logger.info("My Jabber ID: " + self.connection.jid);
// Schedule ping ?
var pingJid = self.connection.domain;
self.connection.ping.hasPingSupport(
pingJid,
function (hasPing) {
if (hasPing)
self.connection.ping.startInterval(pingJid);
else
logger.warn("Ping NOT supported by " + pingJid);
}
);
if (password)
authenticatedUser = true;
if (self.connection && self.connection.connected &&
Strophe.getResourceFromJid(self.connection.jid)) {
// .connected is true while connecting?
// self.connection.send($pres());
self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_ESTABLISHED,
Strophe.getResourceFromJid(self.connection.jid));
}
} else if (status === Strophe.Status.CONNFAIL) {
if (msg === 'x-strophe-bad-non-anon-jid') {
anonymousConnectionFailed = true;
}
else
{
connectionFailed = true;
}
lastErrorMsg = msg;
} else if (status === Strophe.Status.DISCONNECTED) {
// Stop ping interval
self.connection.ping.stopInterval();
self.disconnectInProgress = false;
if (anonymousConnectionFailed) {
// prompt user for username and password
self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_FAILED,
JitsiConnectionErrors.PASSWORD_REQUIRED);
} else if(connectionFailed) {
self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_FAILED,
JitsiConnectionErrors.OTHER_ERROR,
msg ? msg : lastErrorMsg);
} else {
self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_DISCONNECTED,
msg ? msg : lastErrorMsg);
}
} else if (status === Strophe.Status.AUTHFAIL) {
// wrong password or username, prompt user
self.eventEmitter.emit(JitsiConnectionEvents.CONNECTION_FAILED,
JitsiConnectionErrors.PASSWORD_REQUIRED);
}
});
}
XMPP.prototype.connect = function (jid, password) {
if(!jid) {
var configDomain = this.options.hosts.anonymousdomain || this.options.hosts.domain;
// Force authenticated domain if room is appended with '?login=true'
if (this.options.hosts.anonymousdomain &&
window.location.search.indexOf("login=true") !== -1) {
configDomain = this.options.hosts.domain;
}
jid = configDomain || window.location.hostname;
}
return this._connect(jid, password);
};
XMPP.prototype.createRoom = function (roomName, options, useNicks, nick) {
var roomjid = roomName + '@' + this.options.hosts.muc;
if (useNicks) {
if (nick) {
roomjid += '/' + nick;
} else {
roomjid += '/' + Strophe.getNodeFromJid(this.connection.jid);
}
} else {
var tmpJid = Strophe.getNodeFromJid(this.connection.jid);
if(!authenticatedUser)
tmpJid = tmpJid.substr(0, 8);
roomjid += '/' + tmpJid;
}
return this.connection.emuc.createRoom(roomjid, null, options);
}
XMPP.prototype.addListener = function(type, listener) {
this.eventEmitter.on(type, listener);
};
XMPP.prototype.removeListener = function (type, listener) {
this.eventEmitter.removeListener(type, listener);
};
//FIXME: this should work with the room
XMPP.prototype.leaveRoom = function (jid) {
var handler = this.connection.jingle.jid2session[jid];
if (handler && handler.peerconnection) {
// FIXME: probably removing streams is not required and close() should
// be enough
if (RTC.localAudio) {
handler.peerconnection.removeStream(
RTC.localAudio.getOriginalStream(), true);
}
if (RTC.localVideo) {
handler.peerconnection.removeStream(
RTC.localVideo.getOriginalStream(), true);
}
handler.peerconnection.close();
}
this.eventEmitter.emit(XMPPEvents.DISPOSE_CONFERENCE);
this.connection.emuc.doLeave(jid);
};
/**
* Sends 'data' as a log message to the focus. Returns true iff a message
* was sent.
* @param data
* @returns {boolean} true iff a message was sent.
*/
XMPP.prototype.sendLogs = function (data) {
if(!this.connection.emuc.focusMucJid)
return false;
var deflate = true;
var content = JSON.stringify(data);
if (deflate) {
content = String.fromCharCode.apply(null, Pako.deflateRaw(content));
}
content = Base64.encode(content);
// XEP-0337-ish
var message = $msg({to: this.connection.emuc.focusMucJid, type: 'normal'});
message.c('log', { xmlns: 'urn:xmpp:eventlog',
id: 'PeerConnectionStats'});
message.c('message').t(content).up();
if (deflate) {
message.c('tag', {name: "deflated", value: "true"}).up();
}
message.up();
this.connection.send(message);
return true;
};
// Gets the logs from strophe.jingle.
XMPP.prototype.getJingleLog = function () {
return this.connection.jingle ? this.connection.jingle.getLog() : {};
};
// Gets the logs from strophe.
XMPP.prototype.getXmppLog = function () {
return this.connection.logger ? this.connection.logger.log : null;
};
XMPP.prototype.dial = function (to, from, roomName,roomPass) {
this.connection.rayo.dial(to, from, roomName,roomPass);
};
XMPP.prototype.setMute = function (jid, mute) {
this.connection.moderate.setMute(jid, mute);
};
XMPP.prototype.eject = function (jid) {
this.connection.moderate.eject(jid);
};
XMPP.prototype.getSessions = function () {
return this.connection.jingle.sessions;
};
XMPP.prototype.disconnect = function () {
if (this.disconnectInProgress || !this.connection || !this.connection.connected)
{
this.eventEmitter.emit(JitsiConnectionEvents.WRONG_STATE);
return;
}
this.disconnectInProgress = true;
this.connection.disconnect();
};
module.exports = XMPP;