2016-07-18 21:35:02 -04:00
"use strict" ;
2014-08-14 09:35:37 -07:00
var _ = require ( "lodash" ) ;
2016-06-12 02:44:28 +01:00
var pkg = require ( "../package.json" ) ;
2014-10-03 02:57:35 -07:00
var bcrypt = require ( "bcrypt-nodejs" ) ;
2014-08-14 09:35:37 -07:00
var Client = require ( "./client" ) ;
var ClientManager = require ( "./clientManager" ) ;
2014-09-26 15:12:53 -07:00
var express = require ( "express" ) ;
2014-08-14 09:35:37 -07:00
var fs = require ( "fs" ) ;
var io = require ( "socket.io" ) ;
2016-04-03 01:12:49 -04:00
var dns = require ( "dns" ) ;
2014-09-13 14:23:17 +02:00
var Helper = require ( "./helper" ) ;
2016-07-29 21:20:38 -04:00
var ldap = require ( "ldapjs" ) ;
2014-08-14 09:35:37 -07:00
2016-04-26 16:41:08 -04:00
var manager = null ;
2016-07-29 21:20:38 -04:00
var ldapclient = null ;
var authFunction = localAuth ;
2014-08-14 09:35:37 -07:00
2016-06-08 12:26:24 +03:00
module . exports = function ( ) {
2016-04-26 16:41:08 -04:00
manager = new ClientManager ( ) ;
2014-08-14 09:35:37 -07:00
2014-09-26 15:12:53 -07:00
var app = express ( )
2016-05-01 20:27:10 +03:00
. use ( allRequests )
2014-08-14 09:35:37 -07:00
. use ( index )
2014-10-03 16:33:44 -07:00
. use ( express . static ( "client" ) ) ;
2014-11-01 22:06:01 +02:00
2016-06-08 12:26:24 +03:00
var config = Helper . config ;
2014-09-26 16:26:21 -07:00
var server = null ;
2016-07-29 21:20:38 -04:00
if ( config . public && ( config . ldap || { } ) . enable ) {
2016-08-10 02:14:09 -04:00
log . warn ( "Server is public and set to use LDAP. Set to private mode if trying to use LDAP authentication." ) ;
2016-07-29 21:20:38 -04:00
}
2016-06-08 12:26:24 +03:00
if ( ! config . https . enable ) {
2014-09-26 16:26:21 -07:00
server = require ( "http" ) ;
2016-06-08 12:26:24 +03:00
server = server . createServer ( app ) . listen ( config . port , config . host ) ;
2014-09-26 16:26:21 -07:00
} else {
2016-03-09 14:04:05 +02:00
server = require ( "spdy" ) ;
2014-09-26 16:26:21 -07:00
server = server . createServer ( {
2016-06-08 12:26:24 +03:00
key : fs . readFileSync ( Helper . expandHome ( config . https . key ) ) ,
cert : fs . readFileSync ( Helper . expandHome ( config . https . certificate ) )
} , app ) . listen ( config . port , config . host ) ;
2014-09-26 16:26:21 -07:00
}
2016-06-08 12:26:24 +03:00
if ( config . identd . enable ) {
2016-04-26 16:53:29 -04:00
if ( manager . identHandler ) {
log . warn ( "Using both identd and oidentd at the same time!" ) ;
}
2014-10-11 19:33:28 +02:00
require ( "./identd" ) . start ( config . identd . port ) ;
2014-10-11 11:09:27 +01:00
}
2016-08-10 02:14:09 -04:00
if ( ! config . public && ( config . ldap || { } ) . enable ) {
2016-07-29 21:20:38 -04:00
ldapclient = ldap . createClient ( {
url : config . ldap . url
} ) ;
authFunction = ldapAuth ;
}
2016-02-29 01:19:11 +00:00
var sockets = io ( server , {
2016-06-08 12:26:24 +03:00
transports : config . transports
2014-11-01 22:06:01 +02:00
} ) ;
2014-08-14 09:35:37 -07:00
sockets . on ( "connect" , function ( socket ) {
if ( config . public ) {
auth . call ( socket ) ;
} else {
init ( socket ) ;
}
} ) ;
2014-09-24 15:23:54 -07:00
manager . sockets = sockets ;
2016-06-08 12:26:24 +03:00
var protocol = config . https . enable ? "https" : "http" ;
log . info ( "The Lounge v" + pkg . version + " is now running on" , protocol + "://" + ( config . host || "*" ) + ":" + config . port + "/" , ( config . public ? "in public mode" : "in private mode" ) ) ;
2016-04-26 13:51:11 +03:00
log . info ( "Press ctrl-c to stop\n" ) ;
2014-08-14 09:35:37 -07:00
if ( ! config . public ) {
2014-09-24 15:23:54 -07:00
manager . loadUsers ( ) ;
if ( config . autoload ) {
manager . autoload ( ) ;
}
2014-08-14 09:35:37 -07:00
}
} ;
2016-04-03 01:12:49 -04:00
function getClientIp ( req ) {
2016-07-30 20:54:09 -04:00
var ip ;
2016-06-08 12:26:24 +03:00
if ( ! Helper . config . reverseProxy ) {
2016-07-30 20:54:09 -04:00
ip = req . connection . remoteAddress ;
2016-04-03 01:12:49 -04:00
} else {
2016-07-30 20:54:09 -04:00
ip = req . headers [ "x-forwarded-for" ] || req . connection . remoteAddress ;
2016-04-03 01:12:49 -04:00
}
2016-07-30 20:54:09 -04:00
return ip . replace ( /^::ffff:/ , "" ) ;
2016-04-03 01:12:49 -04:00
}
2016-05-01 20:27:10 +03:00
function allRequests ( req , res , next ) {
res . setHeader ( "X-Content-Type-Options" , "nosniff" ) ;
return next ( ) ;
}
2016-07-13 03:17:55 -04:00
// Information to populate the About section in UI, either from npm or from git
2016-07-18 21:35:02 -04:00
var gitCommit = null ;
2016-07-13 03:17:55 -04:00
try {
2016-07-18 21:35:02 -04:00
gitCommit = require ( "child_process" )
. execSync ( "git rev-parse --short HEAD 2> /dev/null" ) // Returns hash of current commit
2016-07-13 03:17:55 -04:00
. toString ( )
. trim ( ) ;
} catch ( e ) {
// Not a git repository or git is not installed: treat it as npm release
}
2014-08-14 09:35:37 -07:00
function index ( req , res , next ) {
2016-05-01 12:41:17 +03:00
if ( req . url . split ( "?" ) [ 0 ] !== "/" ) {
return next ( ) ;
}
2014-08-14 09:35:37 -07:00
return fs . readFile ( "client/index.html" , "utf-8" , function ( err , file ) {
var data = _ . merge (
2016-06-12 02:44:28 +01:00
pkg ,
2016-06-08 12:26:24 +03:00
Helper . config
2014-08-14 09:35:37 -07:00
) ;
2016-07-13 03:17:55 -04:00
data . gitCommit = gitCommit ;
2016-08-17 01:52:29 -04:00
data . themes = fs . readdirSync ( "client/themes/" ) . filter ( function ( file ) {
return file . endsWith ( ".css" ) ;
} ) . map ( function ( css ) {
return css . slice ( 0 , - 4 ) ;
} ) ;
2016-02-14 19:09:51 +02:00
var template = _ . template ( file ) ;
2016-09-09 01:17:31 -04:00
res . setHeader ( "Content-Security-Policy" , "default-src *; connect-src 'self' ws: wss:; style-src * 'unsafe-inline'; script-src 'self'; child-src 'none'; object-src 'none'; form-action 'none'; referrer no-referrer;" ) ;
2014-09-13 05:54:17 +01:00
res . setHeader ( "Content-Type" , "text/html" ) ;
res . writeHead ( 200 ) ;
2016-02-14 19:09:51 +02:00
res . end ( template ( data ) ) ;
2014-08-14 09:35:37 -07:00
} ) ;
}
2016-06-01 00:28:31 +03:00
function init ( socket , client ) {
2014-08-14 09:35:37 -07:00
if ( ! client ) {
2016-06-01 00:02:10 +03:00
socket . emit ( "auth" , { success : true } ) ;
2014-08-14 09:35:37 -07:00
socket . on ( "auth" , auth ) ;
} else {
socket . on (
"input" ,
function ( data ) {
2014-09-09 12:31:23 -07:00
client . input ( data ) ;
2014-08-14 09:35:37 -07:00
}
) ;
socket . on (
2014-09-10 12:23:56 -07:00
"more" ,
2014-08-14 09:35:37 -07:00
function ( data ) {
2014-09-10 12:23:56 -07:00
client . more ( data ) ;
2014-08-14 09:35:37 -07:00
}
) ;
socket . on (
"conn" ,
function ( data ) {
2016-04-03 01:12:49 -04:00
// prevent people from overriding webirc settings
data . ip = null ;
data . hostname = null ;
2014-08-14 09:35:37 -07:00
client . connect ( data ) ;
}
) ;
2016-07-29 21:20:38 -04:00
if ( ! Helper . config . public && ! Helper . config . ldap . enable ) {
2016-02-17 00:14:43 +00:00
socket . on (
"change-password" ,
function ( data ) {
var old = data . old _password ;
var p1 = data . new _password ;
var p2 = data . verify _password ;
if ( typeof p1 === "undefined" || p1 === "" ) {
socket . emit ( "change-password" , {
error : "Please enter a new password"
} ) ;
return ;
}
if ( p1 !== p2 ) {
socket . emit ( "change-password" , {
error : "Both new password fields must match"
} ) ;
return ;
}
if ( ! bcrypt . compareSync ( old || "" , client . config . password ) ) {
socket . emit ( "change-password" , {
error : "The current password field does not match your account password"
} ) ;
return ;
}
2016-06-01 00:28:31 +03:00
2016-02-17 00:14:43 +00:00
var salt = bcrypt . genSaltSync ( 8 ) ;
var hash = bcrypt . hashSync ( p1 , salt ) ;
2016-06-01 00:28:31 +03:00
client . setPassword ( hash , function ( success ) {
var obj = { } ;
if ( success ) {
obj . success = "Successfully updated your password, all your other sessions were logged out" ;
obj . token = client . config . token ;
} else {
obj . error = "Failed to update your password" ;
}
socket . emit ( "change-password" , obj ) ;
2016-02-17 00:14:43 +00:00
} ) ;
}
) ;
}
2014-09-21 09:46:43 -07:00
socket . on (
"open" ,
function ( data ) {
client . open ( data ) ;
}
2014-09-24 12:42:36 -07:00
) ;
socket . on (
"sort" ,
function ( data ) {
client . sort ( data ) ;
}
) ;
2016-02-16 21:29:44 -05:00
socket . on (
"names" ,
function ( data ) {
client . names ( data ) ;
}
) ;
2014-08-14 09:35:37 -07:00
socket . join ( client . id ) ;
socket . emit ( "init" , {
2014-09-21 09:46:43 -07:00
active : client . activeChannel ,
2014-09-15 14:13:03 -07:00
networks : client . networks ,
2016-06-30 15:06:07 +02:00
token : client . config . token || null
2014-08-14 09:35:37 -07:00
} ) ;
}
}
2016-06-01 00:28:31 +03:00
function reverseDnsLookup ( socket , client ) {
2016-04-03 01:12:49 -04:00
client . ip = getClientIp ( socket . request ) ;
dns . reverse ( client . ip , function ( err , host ) {
if ( ! err && host . length ) {
client . hostname = host [ 0 ] ;
} else {
client . hostname = client . ip ;
}
2016-06-01 00:28:31 +03:00
init ( socket , client ) ;
2016-04-03 01:12:49 -04:00
} ) ;
}
2016-07-29 21:20:38 -04:00
function localAuth ( client , user , password , callback ) {
var result = false ;
try {
result = bcrypt . compareSync ( password || "" , client . config . password ) ;
} catch ( error ) {
if ( error === "Not a valid BCrypt hash." ) {
2016-08-10 02:14:09 -04:00
log . error ( "User (" + user + ") with no local password set tried to sign in. (Probably a LDAP user)" ) ;
2016-07-29 21:20:38 -04:00
}
result = false ;
} finally {
callback ( result ) ;
}
}
function ldapAuth ( client , user , password , callback ) {
var userDN = user . replace ( /([,\\\/#+<>;"= ])/g , "\\$1" ) ;
var bindDN = Helper . config . ldap . primaryKey + "=" + userDN + "," + Helper . config . ldap . baseDN ;
ldapclient . bind ( bindDN , password , function ( err ) {
if ( ! err && ! client ) {
if ( ! manager . addUser ( user , null ) ) {
2016-08-10 02:14:09 -04:00
log . error ( "Unable to create new user" , user ) ;
2016-07-29 21:20:38 -04:00
}
}
callback ( ! err ) ;
} ) ;
}
2014-08-14 09:35:37 -07:00
function auth ( data ) {
var socket = this ;
2016-07-29 21:20:38 -04:00
var client ;
2016-06-08 12:26:24 +03:00
if ( Helper . config . public ) {
2016-07-29 21:20:38 -04:00
client = new Client ( manager ) ;
2014-08-14 09:35:37 -07:00
manager . clients . push ( client ) ;
socket . on ( "disconnect" , function ( ) {
manager . clients = _ . without ( manager . clients , client ) ;
client . quit ( ) ;
} ) ;
2016-06-08 12:26:24 +03:00
if ( Helper . config . webirc ) {
2016-04-03 01:12:49 -04:00
reverseDnsLookup ( socket , client ) ;
} else {
init ( socket , client ) ;
}
2014-08-14 09:35:37 -07:00
} else {
2016-07-29 21:20:38 -04:00
client = manager . findClient ( data . user , data . token ) ;
2016-08-18 00:02:40 -04:00
var signedIn = data . token && client && data . token === client . config . token ;
2016-07-29 21:20:38 -04:00
var token ;
2016-08-18 00:02:40 -04:00
if ( client && ( data . remember || data . token ) ) {
2016-08-10 02:26:47 -04:00
token = client . config . token ;
2016-07-29 21:20:38 -04:00
}
var authCallback = function ( success ) {
2014-09-15 14:13:03 -07:00
if ( success ) {
2016-07-29 21:20:38 -04:00
if ( ! client ) {
// LDAP just created a user
manager . loadUser ( data . user ) ;
client = manager . findClient ( data . user ) ;
}
2016-06-08 12:26:24 +03:00
if ( Helper . config . webirc !== null && ! client . config [ "ip" ] ) {
2016-06-01 00:28:31 +03:00
reverseDnsLookup ( socket , client ) ;
2016-04-03 01:12:49 -04:00
} else {
2016-07-29 21:20:38 -04:00
init ( socket , client , token ) ;
2016-04-03 01:12:49 -04:00
}
2016-07-29 21:20:38 -04:00
} else {
socket . emit ( "auth" , { success : success } ) ;
2014-09-15 14:13:03 -07:00
}
2016-07-29 21:20:38 -04:00
} ;
if ( signedIn ) {
authCallback ( true ) ;
} else {
authFunction ( client , data . user , data . password , authCallback ) ;
2014-08-14 09:35:37 -07:00
}
}
}