Dockerfile
This commit is contained in:
parent
d67440a7cc
commit
9c501c7a54
|
@ -0,0 +1,44 @@
|
|||
FROM ubuntu:14.04
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV HOME /root
|
||||
|
||||
ENV XMPP_NAME Otalk
|
||||
ENV XMPP_DOMAIN example.com
|
||||
ENV XMPP_WSS wss://example.com:5281/xmpp-websocket/
|
||||
ENV XMPP_MUC chat.example.com
|
||||
ENV XMPP_STARTUP groupchat/room%40chat.example.com
|
||||
ENV XMPP_ADMIN admin
|
||||
|
||||
ENV LDAP_BASE dc=example.com
|
||||
ENV LDAP_DN cn=admin,dc=example.com
|
||||
ENV LDAP_PWD password
|
||||
ENV LDAP_GROUP mygroup
|
||||
|
||||
RUN sed -i 's/^#\s*\(deb.*universe\)$/\1/g' /etc/apt/sources.list && \
|
||||
sed -i 's/^#\s*\(deb.*multiverse\)$/\1/g' /etc/apt/sources.list && \
|
||||
apt-get -y update && \
|
||||
dpkg-divert --local --rename --add /sbin/initctl && \
|
||||
ln -sf /bin/true /sbin/initctl && \
|
||||
dpkg-divert --local --rename --add /usr/bin/ischroot && \
|
||||
ln -sf /bin/true /usr/bin/ischroot && \
|
||||
apt-get -y upgrade && \
|
||||
apt-get install -y vim wget sudo net-tools pwgen unzip openssh-server \
|
||||
logrotate supervisor language-pack-en software-properties-common \
|
||||
python-software-properties apt-transport-https ca-certificates curl && \
|
||||
apt-get clean
|
||||
|
||||
RUN locale-gen en_US && locale-gen en_US.UTF-8 && echo 'LANG="en_US.UTF-8"' > /etc/default/locale
|
||||
|
||||
RUN apt-get update && apt-get install -y --force-yes nodejs git-core libldap2-dev uuid-dev
|
||||
|
||||
RUN apt-get remove -y --force-yes nodejs && apt-get install -y --force-yes nodejs-legacy npm
|
||||
|
||||
RUN git clone git://github.com/digicoop/otalk.git
|
||||
|
||||
RUN cd otalk && npm install
|
||||
|
||||
ADD app /app
|
||||
|
||||
RUN chmod +x /app/start.sh
|
||||
CMD "/app/start.sh"
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"isDev": true,
|
||||
"http": {
|
||||
"baseUrl": "http://localhost:8000",
|
||||
"port": 8000,
|
||||
"key": "./fakekeys/privatekey.pem",
|
||||
"cert": "./fakekeys/certificate.pem"
|
||||
},
|
||||
"session": {
|
||||
"secret": "shhhhhh don't tell anyone ok?"
|
||||
},
|
||||
"server": {
|
||||
"name": "{{XMPP_NAME}}",
|
||||
"domain": "{{XMPP_DOMAIN}}",
|
||||
"wss": "{{XMPP_WSS}}",
|
||||
"muc": "{{XMPP_MUC}}",
|
||||
"startup": "{{XMPP_STARTUP}}",
|
||||
"admin": "{{XMPP_ADMIN}}"
|
||||
},
|
||||
"ldap": {
|
||||
"address": "{{LDAP_HOST}}",
|
||||
"user": "{{LDAP_DN}}",
|
||||
"password": "{{LDAP_PWD}}",
|
||||
"base": "ou=users,{{LDAP_BASE}}",
|
||||
"filter": "objectClass=person",
|
||||
"group": "cn={{LDAP_GROUP}},ou=groups,{{LDAP_BASE}}"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function (client) {
|
||||
// We always need this one first
|
||||
client.use(require('./disco'));
|
||||
|
||||
client.use(require('./attention'));
|
||||
client.use(require('./avatar'));
|
||||
client.use(require('./blocking'));
|
||||
client.use(require('./bob'));
|
||||
client.use(require('./bookmarks'));
|
||||
client.use(require('./carbons'));
|
||||
client.use(require('./chatstates'));
|
||||
client.use(require('./command'));
|
||||
client.use(require('./correction'));
|
||||
client.use(require('./csi'));
|
||||
client.use(require('./dataforms'));
|
||||
client.use(require('./delayed'));
|
||||
client.use(require('./escaping'));
|
||||
client.use(require('./extdisco'));
|
||||
client.use(require('./forwarding'));
|
||||
client.use(require('./geoloc'));
|
||||
client.use(require('./hashes'));
|
||||
client.use(require('./idle'));
|
||||
client.use(require('./invisible'));
|
||||
client.use(require('./jidprep'));
|
||||
//client.use(require('./jingle'));
|
||||
client.use(require('./json'));
|
||||
client.use(require('./keepalive'));
|
||||
client.use(require('./logging'));
|
||||
client.use(require('./mam'));
|
||||
client.use(require('./muc'));
|
||||
client.use(require('./mood'));
|
||||
client.use(require('./nick'));
|
||||
client.use(require('./oob'));
|
||||
client.use(require('./ping'));
|
||||
client.use(require('./private'));
|
||||
client.use(require('./psa'));
|
||||
client.use(require('./pubsub'));
|
||||
client.use(require('./reach'));
|
||||
client.use(require('./receipts'));
|
||||
client.use(require('./register'));
|
||||
client.use(require('./roster'));
|
||||
client.use(require('./rtt'));
|
||||
client.use(require('./shim'));
|
||||
client.use(require('./time'));
|
||||
client.use(require('./vcard'));
|
||||
client.use(require('./version'));
|
||||
};
|
|
@ -0,0 +1,265 @@
|
|||
'use strict';
|
||||
|
||||
var stanza = require('jxt');
|
||||
var Message = require('./message');
|
||||
var Presence = require('./presence');
|
||||
var Iq = require('./iq');
|
||||
var DataForm = require('./dataforms').DataForm;
|
||||
var jxtutil = require('jxt-xmpp-types');
|
||||
|
||||
var NS = 'http://jabber.org/protocol/muc';
|
||||
var USER_NS = NS + '#user';
|
||||
var ADMIN_NS = NS + '#admin';
|
||||
var OWNER_NS = NS + '#owner';
|
||||
var UNIQ_NS = NS + '#unique';
|
||||
|
||||
|
||||
var proxy = function (child, field) {
|
||||
return {
|
||||
get: function () {
|
||||
if (this._extensions[child]) {
|
||||
return this[child][field];
|
||||
}
|
||||
},
|
||||
set: function (value) {
|
||||
this[child][field] = value;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var UserItem = stanza.define({
|
||||
name: '_mucUserItem',
|
||||
namespace: USER_NS,
|
||||
element: 'item',
|
||||
fields: {
|
||||
affiliation: stanza.attribute('affiliation'),
|
||||
nick: stanza.attribute('nick'),
|
||||
jid: jxtutil.jidAttribute('jid'),
|
||||
role: stanza.attribute('role'),
|
||||
reason: stanza.subText(USER_NS, 'reason')
|
||||
}
|
||||
});
|
||||
|
||||
var UserActor = stanza.define({
|
||||
name: '_mucUserActor',
|
||||
namespace: USER_NS,
|
||||
element: 'actor',
|
||||
fields: {
|
||||
nick: stanza.attribute('nick'),
|
||||
jid: jxtutil.jidAttribute('jid')
|
||||
}
|
||||
});
|
||||
|
||||
var Destroyed = stanza.define({
|
||||
name: 'destroyed',
|
||||
namespace: USER_NS,
|
||||
element: 'destroy',
|
||||
fields: {
|
||||
jid: jxtutil.jidAttribute('jid'),
|
||||
reason: stanza.subText(USER_NS, 'reason')
|
||||
}
|
||||
});
|
||||
|
||||
var Invite = stanza.define({
|
||||
name: 'invite',
|
||||
namespace: USER_NS,
|
||||
element: 'invite',
|
||||
fields: {
|
||||
to: jxtutil.jidAttribute('to'),
|
||||
from: jxtutil.jidAttribute('from'),
|
||||
reason: stanza.subText(USER_NS, 'reason'),
|
||||
thread: stanza.subAttribute(USER_NS, 'continue', 'thread'),
|
||||
'continue': stanza.boolSub(USER_NS, 'continue')
|
||||
}
|
||||
});
|
||||
|
||||
var Decline = stanza.define({
|
||||
name: 'decline',
|
||||
namespace: USER_NS,
|
||||
element: 'decline',
|
||||
fields: {
|
||||
to: jxtutil.jidAttribute('to'),
|
||||
from: jxtutil.jidAttribute('from'),
|
||||
reason: stanza.subText(USER_NS, 'reason')
|
||||
}
|
||||
});
|
||||
|
||||
var AdminItem = stanza.define({
|
||||
name: '_mucAdminItem',
|
||||
namespace: ADMIN_NS,
|
||||
element: 'item',
|
||||
fields: {
|
||||
affiliation: stanza.attribute('affiliation'),
|
||||
nick: stanza.attribute('nick'),
|
||||
jid: jxtutil.jidAttribute('jid'),
|
||||
role: stanza.attribute('role'),
|
||||
reason: stanza.subText(ADMIN_NS, 'reason')
|
||||
}
|
||||
});
|
||||
|
||||
var AdminActor = stanza.define({
|
||||
name: 'actor',
|
||||
namespace: USER_NS,
|
||||
element: 'actor',
|
||||
fields: {
|
||||
nick: stanza.attribute('nick'),
|
||||
jid: jxtutil.jidAttribute('jid')
|
||||
}
|
||||
});
|
||||
|
||||
var Destroy = stanza.define({
|
||||
name: 'destroy',
|
||||
namespace: OWNER_NS,
|
||||
element: 'destroy',
|
||||
fields: {
|
||||
jid: jxtutil.jidAttribute('jid'),
|
||||
password: stanza.subText(OWNER_NS, 'password'),
|
||||
reason: stanza.subText(OWNER_NS, 'reason')
|
||||
}
|
||||
});
|
||||
|
||||
exports.MUC = stanza.define({
|
||||
name: 'muc',
|
||||
namespace: USER_NS,
|
||||
element: 'x',
|
||||
fields: {
|
||||
affiliation: proxy('_mucUserItem', 'affiliation'),
|
||||
nick: proxy('_mucUserItem', 'nick'),
|
||||
jid: proxy('_mucUserItem', 'jid'),
|
||||
role: proxy('_mucUserItem', 'role'),
|
||||
actor: proxy('_mucUserItem', '_mucUserActor'),
|
||||
reason: proxy('_mucUserItem', 'reason'),
|
||||
password: stanza.subText(USER_NS, 'password'),
|
||||
codes: {
|
||||
get: function () {
|
||||
return stanza.getMultiSubText(this.xml, USER_NS, 'status', function (sub) {
|
||||
return stanza.getAttribute(sub, 'code');
|
||||
});
|
||||
},
|
||||
set: function (value) {
|
||||
var self = this;
|
||||
stanza.setMultiSubText(this.xml, USER_NS, 'status', value, function (val) {
|
||||
var child = stanza.createElement(USER_NS, 'status', USER_NS);
|
||||
stanza.setAttribute(child, 'code', val);
|
||||
self.xml.appendChild(child);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exports.MUCAdmin = stanza.define({
|
||||
name: 'mucAdmin',
|
||||
namespace: ADMIN_NS,
|
||||
element: 'query',
|
||||
fields: {
|
||||
affiliation: proxy('_mucAdminItem', 'affiliation'),
|
||||
nick: proxy('_mucAdminItem', 'nick'),
|
||||
jid: proxy('_mucAdminItem', 'jid'),
|
||||
role: proxy('_mucAdminItem', 'role'),
|
||||
actor: proxy('_mucAdminItem', '_mucAdminActor'),
|
||||
reason: proxy('_mucAdminItem', 'reason')
|
||||
}
|
||||
});
|
||||
|
||||
exports.MUCOwner = stanza.define({
|
||||
name: 'mucOwner',
|
||||
namespace: OWNER_NS,
|
||||
element: 'query'
|
||||
});
|
||||
|
||||
exports.MUCJoin = stanza.define({
|
||||
name: 'joinMuc',
|
||||
namespace: NS,
|
||||
element: 'x',
|
||||
fields: {
|
||||
password: stanza.subText(NS, 'password'),
|
||||
history: {
|
||||
get: function () {
|
||||
var result = {};
|
||||
var hist = stanza.find(this.xml, this._NS, 'history');
|
||||
|
||||
if (!hist.length) {
|
||||
return {};
|
||||
}
|
||||
hist = hist[0];
|
||||
|
||||
var maxchars = hist.getAttribute('maxchars') || '';
|
||||
var maxstanzas = hist.getAttribute('maxstanas') || '';
|
||||
var seconds = hist.getAttribute('seconds') || '';
|
||||
var since = hist.getAttribute('since') || '';
|
||||
|
||||
|
||||
if (maxchars) {
|
||||
result.maxchars = parseInt(maxchars, 10);
|
||||
}
|
||||
if (maxstanzas) {
|
||||
result.maxstanzas = parseInt(maxstanzas, 10);
|
||||
}
|
||||
if (seconds) {
|
||||
result.seconds = parseInt(seconds, 10);
|
||||
}
|
||||
if (since) {
|
||||
result.since = new Date(since);
|
||||
}
|
||||
},
|
||||
set: function (opts) {
|
||||
var existing = stanza.find(this.xml, this._NS, 'history');
|
||||
if (existing.length) {
|
||||
for (var i = 0; i < existing.length; i++) {
|
||||
this.xml.removeChild(existing[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var hist = stanza.createElement(this._NS, 'history', this._NS);
|
||||
this.xml.appendChild(hist);
|
||||
|
||||
if (opts.maxchars) {
|
||||
hist.setAttribute('maxchars' + opts.maxchars);
|
||||
}
|
||||
if (opts.maxstanzas) {
|
||||
hist.setAttribute('maxstanzas', opts.maxstanzas);
|
||||
}
|
||||
if (opts.seconds) {
|
||||
hist.setAttribute('seconds' + opts.seconds);
|
||||
}
|
||||
if (opts.since) {
|
||||
hist.setAttribute('since', opts.since.toISOString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exports.DirectInvite = stanza.define({
|
||||
name: 'mucInvite',
|
||||
namespace: 'jabber:x:conference',
|
||||
element: 'x',
|
||||
fields: {
|
||||
jid: jxtutil.jidAttribute('jid'),
|
||||
password: stanza.attribute('password'),
|
||||
reason: stanza.attribute('reason'),
|
||||
thread: stanza.attribute('thread'),
|
||||
'continue': stanza.boolAttribute('continue')
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
stanza.add(Iq, 'mucUnique', stanza.subText(UNIQ_NS, 'unique'));
|
||||
|
||||
|
||||
stanza.extend(UserItem, UserActor);
|
||||
stanza.extend(exports.MUC, UserItem);
|
||||
stanza.extend(exports.MUC, Invite, 'invites');
|
||||
stanza.extend(exports.MUC, Decline);
|
||||
stanza.extend(exports.MUC, Destroyed);
|
||||
stanza.extend(AdminItem, AdminActor);
|
||||
stanza.extend(exports.MUCAdmin, AdminItem, 'items');
|
||||
stanza.extend(exports.MUCOwner, Destroy);
|
||||
stanza.extend(exports.MUCOwner, DataForm);
|
||||
stanza.extend(Presence, exports.MUC);
|
||||
stanza.extend(Message, exports.MUC);
|
||||
stanza.extend(Presence, exports.MUCJoin);
|
||||
stanza.extend(Message, exports.DirectInvite);
|
||||
stanza.extend(Iq, exports.MUCAdmin);
|
||||
stanza.extend(Iq, exports.MUCOwner);
|
|
@ -0,0 +1,158 @@
|
|||
'use strict';
|
||||
|
||||
var util = require('util');
|
||||
var stanza = require('jxt');
|
||||
var WildEmitter = require('wildemitter');
|
||||
var async = require('async');
|
||||
var framing = require('../stanza/framing');
|
||||
var StreamError = require('../stanza/streamError');
|
||||
|
||||
var WS = (require('faye-websocket') && require('faye-websocket').Client) ?
|
||||
require('faye-websocket').Client :
|
||||
window.WebSocket;
|
||||
|
||||
var WS_OPEN = 1;
|
||||
|
||||
|
||||
|
||||
function WSConnection(sm) {
|
||||
var self = this;
|
||||
|
||||
WildEmitter.call(this);
|
||||
|
||||
self.sm = sm;
|
||||
self.closing = false;
|
||||
|
||||
self.sendQueue = async.queue(function (data, cb) {
|
||||
if (self.conn) {
|
||||
if (typeof data !== 'string') {
|
||||
data = data.toString();
|
||||
}
|
||||
|
||||
data = new Buffer(data, 'utf8').toString();
|
||||
|
||||
self.emit('raw:outgoing', data);
|
||||
if (self.conn.readyState === WS_OPEN) {
|
||||
self.conn.send(data);
|
||||
}
|
||||
}
|
||||
cb();
|
||||
}, 1);
|
||||
|
||||
self.on('connected', function () {
|
||||
self.send(self.startHeader());
|
||||
});
|
||||
|
||||
self.on('raw:incoming', function (data) {
|
||||
var stanzaObj, err;
|
||||
|
||||
data = data.trim();
|
||||
if (data === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.indexOf("<stream:stream") > 0 && data.indexOf("</stream:stream") == -1) {
|
||||
data += "</stream:stream>";
|
||||
}
|
||||
|
||||
if (data.indexOf("<subject/></message>") > 0) {
|
||||
data = data.replace("<subject/></message>", "<subject>true</subject></message>");
|
||||
}
|
||||
|
||||
try {
|
||||
stanzaObj = stanza.parse(data);
|
||||
} catch (e) {
|
||||
err = new StreamError({
|
||||
condition: 'invalid-xml'
|
||||
});
|
||||
self.emit('stream:error', err, e);
|
||||
self.send(err);
|
||||
return self.disconnect();
|
||||
}
|
||||
|
||||
if (stanzaObj._name === 'openStream') {
|
||||
self.hasStream = true;
|
||||
self.stream = stanzaObj;
|
||||
return self.emit('stream:start', stanzaObj.toJSON());
|
||||
}
|
||||
if (stanzaObj._name === 'closeStream') {
|
||||
self.emit('stream:end');
|
||||
return self.disconnect();
|
||||
}
|
||||
|
||||
if (!stanzaObj.lang) {
|
||||
stanzaObj.lang = self.stream ? self.stream.lang : "en";
|
||||
}
|
||||
|
||||
self.emit('stream:data', stanzaObj);
|
||||
});
|
||||
}
|
||||
|
||||
util.inherits(WSConnection, WildEmitter);
|
||||
|
||||
WSConnection.prototype.connect = function (opts) {
|
||||
var self = this;
|
||||
|
||||
self.config = opts;
|
||||
|
||||
self.hasStream = false;
|
||||
self.closing = false;
|
||||
|
||||
self.conn = new WS(opts.wsURL, 'xmpp');
|
||||
self.conn.onerror = function (e) {
|
||||
e.preventDefault();
|
||||
self.emit('disconnected', self);
|
||||
};
|
||||
|
||||
self.conn.onclose = function () {
|
||||
self.emit('disconnected', self);
|
||||
};
|
||||
|
||||
self.conn.onopen = function () {
|
||||
self.sm.started = false;
|
||||
self.emit('connected', self);
|
||||
};
|
||||
|
||||
self.conn.onmessage = function (wsMsg) {
|
||||
self.emit('raw:incoming', new Buffer(wsMsg.data, 'utf8').toString());
|
||||
};
|
||||
};
|
||||
|
||||
WSConnection.prototype.startHeader = function () {
|
||||
return new framing.Open({
|
||||
version: this.config.version || '1.0',
|
||||
lang: this.config.lang || 'en',
|
||||
to: this.config.server
|
||||
});
|
||||
};
|
||||
|
||||
WSConnection.prototype.closeHeader = function () {
|
||||
return new framing.Close();
|
||||
};
|
||||
|
||||
WSConnection.prototype.disconnect = function () {
|
||||
if (this.conn && !this.closing) {
|
||||
this.closing = true;
|
||||
this.send(this.closeHeader());
|
||||
} else {
|
||||
this.hasStream = false;
|
||||
this.stream = undefined;
|
||||
if (this.conn.readyState === WS_OPEN) {
|
||||
this.conn.close();
|
||||
}
|
||||
this.conn = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
WSConnection.prototype.restart = function () {
|
||||
var self = this;
|
||||
self.hasStream = false;
|
||||
self.send(this.startHeader());
|
||||
};
|
||||
|
||||
WSConnection.prototype.send = function (data) {
|
||||
this.sendQueue.push(data);
|
||||
};
|
||||
|
||||
|
||||
module.exports = WSConnection;
|
|
@ -0,0 +1,35 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "Configuring dev_config.json..."
|
||||
|
||||
export XMPP_NAME="$(echo ${XMPP_NAME} | sed 's/\//\\\//g')"
|
||||
export XMPP_DOMAIN="$(echo ${XMPP_DOMAIN} | sed 's/\//\\\//g')"
|
||||
export XMPP_WSS="$(echo ${XMPP_WSS} | sed 's/\//\\\//g')"
|
||||
export XMPP_MUC="$(echo ${XMPP_MUC} | sed 's/\//\\\//g')"
|
||||
export XMPP_STARTUP="$(echo ${XMPP_STARTUP} | sed 's/\//\\\//g')"
|
||||
export XMPP_ADMIN="$(echo ${XMPP_ADMIN} | sed 's/\//\\\//g')"
|
||||
|
||||
sed 's/{{XMPP_NAME}}/'"${XMPP_NAME}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{XMPP_DOMAIN}}/'"${XMPP_DOMAIN}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{XMPP_WSS}}/'"${XMPP_WSS}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{XMPP_MUC}}/'"${XMPP_MUC}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{XMPP_STARTUP}}/'"${XMPP_STARTUP}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{XMPP_ADMIN}}/'"${XMPP_ADMIN}"'/' -i /app/config/dev_config.json
|
||||
|
||||
sed 's/{{LDAP_HOST}}/'"${LDAP_PORT_389_TCP_ADDR}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{LDAP_BASE}}/'"${LDAP_BASE}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{LDAP_DN}}/'"${LDAP_DN}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{LDAP_PWD}}/'"${LDAP_PWD}"'/' -i /app/config/dev_config.json
|
||||
sed 's/{{LDAP_GROUP}}/'"${LDAP_GROUP}"'/' -i /app/config/dev_config.json
|
||||
|
||||
cp /app/config/dev_config.json /otalk
|
||||
|
||||
echo "Configuring otalk..."
|
||||
|
||||
cd otalk
|
||||
|
||||
cp /app/stanza.io/websocket.js node_modules/stanza.io/lib/transports
|
||||
cp /app/stanza.io/index-browser.js node_modules/stanza.io/lib/plugins
|
||||
cp /app/stanza.io/muc.js node_modules/stanza.io/lib/stanza
|
||||
|
||||
node server
|
Loading…
Reference in New Issue