2016-10-09 19:14:02 +00:00
"use strict" ;
2018-01-11 11:33:36 +00:00
const _ = require ( "lodash" ) ;
2018-03-02 18:28:54 +00:00
const colors = require ( "chalk" ) ;
2018-01-11 11:33:36 +00:00
const pkg = require ( "../package.json" ) ;
const Chan = require ( "./models/chan" ) ;
const crypto = require ( "crypto" ) ;
const Msg = require ( "./models/msg" ) ;
const Network = require ( "./models/network" ) ;
const ircFramework = require ( "irc-framework" ) ;
const Helper = require ( "./helper" ) ;
2017-06-21 07:58:29 +00:00
const UAParser = require ( "ua-parser-js" ) ;
2017-11-28 17:56:53 +00:00
const MessageStorage = require ( "./plugins/sqlite" ) ;
2014-09-13 21:29:45 +00:00
module . exports = Client ;
2018-01-11 11:33:36 +00:00
let id = 0 ;
const events = [
2017-07-10 16:01:20 +00:00
"away" ,
2016-03-08 18:50:48 +00:00
"connection" ,
2016-04-24 15:12:54 +00:00
"unhandled" ,
2017-04-22 12:51:21 +00:00
"banlist" ,
2014-09-13 21:29:45 +00:00
"ctcp" ,
2017-09-19 15:22:50 +00:00
"chghost" ,
2014-09-13 21:29:45 +00:00
"error" ,
2016-02-12 11:24:13 +00:00
"invite" ,
2014-09-13 21:29:45 +00:00
"join" ,
"kick" ,
"mode" ,
"motd" ,
"message" ,
"names" ,
"nick" ,
"part" ,
"quit" ,
"topic" ,
"welcome" ,
2016-03-09 20:04:07 +00:00
"list" ,
2017-11-15 06:35:15 +00:00
"whois" ,
2014-09-13 21:29:45 +00:00
] ;
2018-01-11 11:33:36 +00:00
const inputs = [
2017-04-24 10:40:53 +00:00
"ban" ,
2016-03-27 15:57:59 +00:00
"ctcp" ,
2016-03-06 09:24:56 +00:00
"msg" ,
"part" ,
2017-08-23 23:08:44 +00:00
"rejoin" ,
2014-09-13 21:29:45 +00:00
"action" ,
2016-11-19 08:24:39 +00:00
"away" ,
2014-09-13 21:29:45 +00:00
"connect" ,
2016-04-14 08:56:02 +00:00
"disconnect" ,
2014-09-13 21:29:45 +00:00
"invite" ,
"kick" ,
"mode" ,
2016-10-01 17:04:03 +00:00
"nick" ,
2014-09-13 21:29:45 +00:00
"notice" ,
2016-03-24 20:40:36 +00:00
"query" ,
2014-09-13 21:29:45 +00:00
"quit" ,
"raw" ,
"topic" ,
2016-05-29 02:07:34 +00:00
"list" ,
2017-11-15 06:35:15 +00:00
"whois" ,
2016-03-14 04:21:42 +00:00
] . reduce ( function ( plugins , name ) {
2018-01-11 11:33:36 +00:00
const plugin = require ( ` ./plugins/inputs/ ${ name } ` ) ;
2017-04-08 12:34:31 +00:00
plugin . commands . forEach ( ( command ) => plugins [ command ] = plugin ) ;
2016-03-14 04:21:42 +00:00
return plugins ;
} , { } ) ;
2014-09-13 21:29:45 +00:00
2017-11-22 06:39:32 +00:00
function Client ( manager , name , config = { } ) {
2014-09-13 21:29:45 +00:00
_ . merge ( this , {
2017-04-28 15:58:14 +00:00
awayMessage : config . awayMessage || "" ,
2016-09-25 06:41:10 +00:00
lastActiveChannel : - 1 ,
attachedClients : { } ,
2014-09-13 21:29:45 +00:00
config : config ,
id : id ++ ,
2014-09-16 19:47:01 +00:00
name : name ,
2014-09-13 21:29:45 +00:00
networks : [ ] ,
2016-02-17 00:14:43 +00:00
sockets : manager . sockets ,
2017-11-15 06:35:15 +00:00
manager : manager ,
2014-09-13 21:29:45 +00:00
} ) ;
2016-05-31 21:28:31 +00:00
2018-01-11 11:33:36 +00:00
const client = this ;
let delay = 0 ;
2017-11-28 17:56:53 +00:00
if ( ! Helper . config . public ) {
client . messageStorage = new MessageStorage ( ) ;
if ( client . config . log && Helper . config . messageStorage . includes ( "sqlite" ) ) {
client . messageStorage . enable ( client . name ) ;
}
}
2017-04-08 12:34:31 +00:00
( client . config . networks || [ ] ) . forEach ( ( n ) => {
2016-06-30 13:06:07 +00:00
setTimeout ( function ( ) {
client . connect ( n ) ;
} , delay ) ;
delay += 1000 ;
} ) ;
2016-04-16 11:32:38 +00:00
2017-06-21 07:58:29 +00:00
if ( typeof client . config . sessions !== "object" ) {
client . config . sessions = { } ;
}
2017-07-10 19:47:03 +00:00
_ . forOwn ( client . config . sessions , ( session ) => {
if ( session . pushSubscription ) {
this . registerPushSubscription ( session , session . pushSubscription , true ) ;
}
} ) ;
2016-06-30 13:06:07 +00:00
if ( client . name ) {
2016-12-11 08:29:09 +00:00
log . info ( ` User ${ colors . bold ( client . name ) } loaded ` ) ;
2016-06-19 08:01:50 +00:00
}
2014-09-13 21:29:45 +00:00
}
Client . prototype . emit = function ( event , data ) {
if ( this . sockets !== null ) {
this . sockets . in ( this . id ) . emit ( event , data ) ;
}
2014-09-13 17:18:42 +00:00
} ;
2014-09-13 21:29:45 +00:00
2016-10-09 08:54:44 +00:00
Client . prototype . find = function ( channelId ) {
2018-01-11 11:33:36 +00:00
let network = null ;
let chan = null ;
2018-02-20 07:28:04 +00:00
2018-01-11 11:33:36 +00:00
for ( const i in this . networks ) {
const n = this . networks [ i ] ;
2016-10-09 08:54:44 +00:00
chan = _ . find ( n . channels , { id : channelId } ) ;
2018-02-20 07:28:04 +00:00
2014-09-13 21:29:45 +00:00
if ( chan ) {
network = n ;
break ;
}
}
2018-02-20 07:28:04 +00:00
2014-09-13 21:29:45 +00:00
if ( network && chan ) {
2018-03-05 00:59:16 +00:00
return { network , chan } ;
2014-09-13 21:29:45 +00:00
}
2016-10-09 08:54:44 +00:00
return false ;
2014-09-13 21:29:45 +00:00
} ;
Client . prototype . connect = function ( args ) {
2018-01-11 11:33:36 +00:00
const client = this ;
const nick = args . nick || "lounge-user" ;
let webirc = null ;
let channels = [ ] ;
2016-06-17 10:46:15 +00:00
2016-06-19 17:12:42 +00:00
if ( args . channels ) {
2018-01-11 11:33:36 +00:00
let badName = false ;
2016-06-19 17:12:42 +00:00
2017-04-08 12:34:31 +00:00
args . channels . forEach ( ( chan ) => {
2016-06-19 17:12:42 +00:00
if ( ! chan . name ) {
badName = true ;
return ;
}
2016-06-17 10:46:15 +00:00
channels . push ( new Chan ( {
2017-04-01 08:33:17 +00:00
name : chan . name ,
key : chan . key || "" ,
2018-01-30 16:46:34 +00:00
type : chan . type ,
2016-06-17 10:46:15 +00:00
} ) ) ;
} ) ;
2016-06-19 17:12:42 +00:00
if ( badName && client . name ) {
log . warn ( "User '" + client . name + "' on network '" + args . name + "' has an invalid channel which has been ignored" ) ;
}
// `join` is kept for backwards compatibility when updating from versions <2.0
// also used by the "connect" window
} else if ( args . join ) {
channels = args . join
2016-10-09 08:54:44 +00:00
. replace ( /,/g , " " )
2016-06-19 17:12:42 +00:00
. split ( /\s+/g )
. map ( function ( chan ) {
return new Chan ( {
2017-11-15 06:35:15 +00:00
name : chan ,
2016-06-19 17:12:42 +00:00
} ) ;
} ) ;
2016-06-17 10:46:15 +00:00
}
2016-03-07 21:09:42 +00:00
2016-11-19 20:23:51 +00:00
args . ip = args . ip || ( client . config && client . config . ip ) || client . ip ;
args . hostname = args . hostname || ( client . config && client . config . hostname ) || client . hostname ;
2018-01-11 11:33:36 +00:00
const network = new Network ( {
2017-11-28 17:25:15 +00:00
uuid : args . uuid ,
2018-01-11 11:33:36 +00:00
name : args . name || ( Helper . config . displayNetwork ? "" : Helper . config . defaults . name ) || "" ,
2016-03-07 21:09:42 +00:00
host : args . host || "" ,
port : parseInt ( args . port , 10 ) || ( args . tls ? 6697 : 6667 ) ,
tls : ! ! args . tls ,
2018-03-05 18:11:41 +00:00
rejectUnauthorized : ! ! args . rejectUnauthorized ,
2016-03-07 21:09:42 +00:00
password : args . password ,
username : args . username || nick . replace ( /[^a-zA-Z0-9]/g , "" ) ,
realname : args . realname || "The Lounge User" ,
2016-04-03 05:12:49 +00:00
commands : args . commands ,
ip : args . ip ,
hostname : args . hostname ,
2016-06-17 10:46:15 +00:00
channels : channels ,
2016-03-07 21:09:42 +00:00
} ) ;
2016-05-12 11:15:38 +00:00
network . setNick ( nick ) ;
2016-03-07 21:09:42 +00:00
client . networks . push ( network ) ;
client . emit ( "network" , {
2017-11-29 19:54:09 +00:00
networks : [ network . getFilteredClone ( this . lastActiveChannel , - 1 ) ] ,
2016-03-07 21:09:42 +00:00
} ) ;
2018-01-11 11:33:36 +00:00
if ( Helper . config . lockNetwork ) {
2016-02-21 12:02:35 +00:00
// This check is needed to prevent invalid user configurations
2018-01-11 11:33:36 +00:00
if ( ! Helper . config . public && args . host && args . host . length > 0 && args . host !== Helper . config . defaults . host ) {
2016-04-19 10:20:18 +00:00
network . channels [ 0 ] . pushMessage ( client , new Msg ( {
type : Msg . Type . ERROR ,
2017-11-15 06:35:15 +00:00
text : "Hostname you specified is not allowed." ,
2016-09-25 06:41:10 +00:00
} ) , true ) ;
2016-02-21 12:02:35 +00:00
return ;
}
2018-01-11 11:33:36 +00:00
network . host = Helper . config . defaults . host ;
network . port = Helper . config . defaults . port ;
network . tls = Helper . config . defaults . tls ;
2018-02-17 08:22:28 +00:00
network . rejectUnauthorized = Helper . config . defaults . rejectUnauthorized ;
2016-02-21 12:02:35 +00:00
}
2016-03-07 21:09:42 +00:00
if ( network . host . length === 0 ) {
2016-04-19 10:20:18 +00:00
network . channels [ 0 ] . pushMessage ( client , new Msg ( {
type : Msg . Type . ERROR ,
2017-11-15 06:35:15 +00:00
text : "You must specify a hostname to connect." ,
2016-09-25 06:41:10 +00:00
} ) , true ) ;
2016-02-21 12:02:35 +00:00
return ;
}
2018-01-11 11:33:36 +00:00
if ( Helper . config . webirc && network . host in Helper . config . webirc ) {
2016-11-19 20:23:51 +00:00
if ( ! args . hostname ) {
args . hostname = args . ip ;
}
2016-04-03 05:12:49 +00:00
if ( args . ip ) {
2018-01-11 11:33:36 +00:00
if ( Helper . config . webirc [ network . host ] instanceof Function ) {
webirc = Helper . config . webirc [ network . host ] ( client , args ) ;
2016-04-03 05:12:49 +00:00
} else {
webirc = {
2018-01-11 11:33:36 +00:00
password : Helper . config . webirc [ network . host ] ,
2016-10-09 19:15:20 +00:00
username : pkg . name ,
2016-04-03 05:12:49 +00:00
address : args . ip ,
2017-11-15 06:35:15 +00:00
hostname : args . hostname ,
2016-04-03 05:12:49 +00:00
} ;
}
} else {
log . warn ( "Cannot find a valid WEBIRC configuration for " + nick
+ "!" + network . username + "@" + network . host ) ;
}
}
2017-02-02 20:52:37 +00:00
network . irc = new ircFramework . Client ( {
2017-12-31 09:20:20 +00:00
version : false , // We handle it ourselves
2016-03-07 21:09:42 +00:00
host : network . host ,
port : network . port ,
nick : nick ,
2018-01-11 11:33:36 +00:00
username : Helper . config . useHexIp ? Helper . ip2hex ( args . ip ) : network . username ,
2016-03-07 21:09:42 +00:00
gecos : network . realname ,
password : network . password ,
tls : network . tls ,
2018-01-11 11:33:36 +00:00
outgoing _addr : Helper . config . bind ,
2018-02-17 08:22:28 +00:00
rejectUnauthorized : network . rejectUnauthorized ,
2017-09-19 15:22:50 +00:00
enable _chghost : true ,
2017-02-02 20:52:37 +00:00
enable _echomessage : true ,
2016-04-13 07:10:44 +00:00
auto _reconnect : true ,
2016-07-02 18:45:41 +00:00
auto _reconnect _wait : 10000 + Math . floor ( Math . random ( ) * 1000 ) , // If multiple users are connected to the same network, randomize their reconnections a little
auto _reconnect _max _retries : 360 , // At least one hour (plus timeouts) worth of reconnections
2016-04-03 05:12:49 +00:00
webirc : webirc ,
2014-09-13 21:29:45 +00:00
} ) ;
2016-11-19 20:23:51 +00:00
2017-02-02 20:52:37 +00:00
network . irc . requestCap ( [
2018-03-10 11:25:56 +00:00
"znc.in/self-message" , // Legacy echo-message for ZNC
2017-02-02 20:52:37 +00:00
] ) ;
2018-03-10 11:25:56 +00:00
// Request only new messages from ZNC if we have sqlite logging enabled
// See http://wiki.znc.in/Playback
if ( client . config . log && Helper . config . messageStorage . includes ( "sqlite" ) ) {
network . irc . requestCap ( "znc.in/playback" ) ;
}
2017-04-08 12:34:31 +00:00
events . forEach ( ( plugin ) => {
2018-01-11 11:33:36 +00:00
require ( ` ./plugins/irc-events/ ${ plugin } ` ) . apply ( client , [
2017-02-02 20:52:37 +00:00
network . irc ,
2017-11-15 06:35:15 +00:00
network ,
2017-02-02 20:52:37 +00:00
] ) ;
} ) ;
network . irc . connect ( ) ;
2016-11-19 20:23:51 +00:00
client . save ( ) ;
2017-11-28 17:56:53 +00:00
channels . forEach ( ( channel ) => channel . loadMessages ( client , network ) ) ;
2014-09-13 21:29:45 +00:00
} ;
2017-06-21 07:58:29 +00:00
Client . prototype . generateToken = function ( callback ) {
2018-01-05 13:26:12 +00:00
crypto . randomBytes ( 64 , ( err , buf ) => {
2016-10-09 08:54:44 +00:00
if ( err ) {
throw err ;
}
2017-06-21 07:58:29 +00:00
callback ( buf . toString ( "hex" ) ) ;
2016-05-31 21:28:31 +00:00
} ) ;
} ;
2018-01-05 13:26:12 +00:00
Client . prototype . calculateTokenHash = function ( token ) {
return crypto . createHash ( "sha512" ) . update ( token ) . digest ( "hex" ) ;
} ;
2017-06-21 07:58:29 +00:00
Client . prototype . updateSession = function ( token , ip , request ) {
const client = this ;
const agent = UAParser ( request . headers [ "user-agent" ] || "" ) ;
let friendlyAgent = "" ;
2017-08-13 18:37:12 +00:00
if ( agent . browser . name ) {
2017-06-21 07:58:29 +00:00
friendlyAgent = ` ${ agent . browser . name } ${ agent . browser . major } ` ;
} else {
friendlyAgent = "Unknown browser" ;
}
2017-08-13 18:37:12 +00:00
if ( agent . os . name ) {
2017-08-08 19:46:55 +00:00
friendlyAgent += ` on ${ agent . os . name } ${ agent . os . version } ` ;
2017-06-21 07:58:29 +00:00
}
2017-09-17 08:09:19 +00:00
client . config . sessions [ token ] = _ . assign ( client . config . sessions [ token ] , {
2017-06-21 07:58:29 +00:00
lastUse : Date . now ( ) ,
ip : ip ,
agent : friendlyAgent ,
2017-09-17 08:09:19 +00:00
} ) ;
client . manager . updateUser ( client . name , {
2017-11-15 06:35:15 +00:00
sessions : client . config . sessions ,
2017-09-17 08:09:19 +00:00
} ) ;
2017-06-21 07:58:29 +00:00
} ;
2016-05-31 21:28:31 +00:00
Client . prototype . setPassword = function ( hash , callback ) {
2018-01-11 11:33:36 +00:00
const client = this ;
2016-05-31 21:28:31 +00:00
2017-06-21 07:58:29 +00:00
client . manager . updateUser ( client . name , {
2017-11-15 06:35:15 +00:00
password : hash ,
2017-06-21 07:58:29 +00:00
} , function ( err ) {
if ( err ) {
return callback ( false ) ;
}
2016-05-31 21:28:31 +00:00
2017-06-21 07:58:29 +00:00
client . config . password = hash ;
return callback ( true ) ;
2016-05-31 21:28:31 +00:00
} ) ;
2016-02-17 00:14:43 +00:00
} ;
2014-09-13 21:29:45 +00:00
Client . prototype . input = function ( data ) {
2018-01-11 11:33:36 +00:00
const client = this ;
2017-04-08 12:34:31 +00:00
data . text . split ( "\n" ) . forEach ( ( line ) => {
2016-06-05 02:48:41 +00:00
data . text = line ;
client . inputLine ( data ) ;
} ) ;
} ;
Client . prototype . inputLine = function ( data ) {
2018-01-11 11:33:36 +00:00
const client = this ;
const target = client . find ( data . target ) ;
2018-02-20 07:28:04 +00:00
2016-09-25 06:41:10 +00:00
if ( ! target ) {
return ;
}
// Sending a message to a channel is higher priority than merely opening one
// so that reloading the page will open this channel
this . lastActiveChannel = target . chan . id ;
2016-03-06 09:24:56 +00:00
2018-01-11 11:33:36 +00:00
let text = data . text ;
2016-03-06 09:24:56 +00:00
// This is either a normal message or a command escaped with a leading '/'
if ( text . charAt ( 0 ) !== "/" || text . charAt ( 1 ) === "/" ) {
2017-03-11 18:09:37 +00:00
if ( target . chan . type === Chan . Type . LOBBY ) {
target . chan . pushMessage ( this , new Msg ( {
type : Msg . Type . ERROR ,
2017-11-15 06:35:15 +00:00
text : "Messages can not be sent to lobbies." ,
2017-03-11 18:09:37 +00:00
} ) ) ;
return ;
}
2016-03-06 09:24:56 +00:00
text = "say " + text . replace ( /^\// , "" ) ;
} else {
text = text . substr ( 1 ) ;
2014-09-13 21:29:45 +00:00
}
2016-03-06 09:24:56 +00:00
2018-01-11 11:33:36 +00:00
const args = text . split ( " " ) ;
const cmd = args . shift ( ) . toLowerCase ( ) ;
2016-03-06 09:24:56 +00:00
2018-01-11 11:33:36 +00:00
const irc = target . network . irc ;
let connected = irc && irc . connection && irc . connection . connected ;
2016-04-03 09:58:59 +00:00
2016-03-14 04:21:42 +00:00
if ( cmd in inputs ) {
2018-01-11 11:33:36 +00:00
const plugin = inputs [ cmd ] ;
2018-02-20 07:28:04 +00:00
2016-04-03 09:58:59 +00:00
if ( connected || plugin . allowDisconnected ) {
connected = true ;
plugin . input . apply ( client , [ target . network , target . chan , cmd , args ] ) ;
}
} else if ( connected ) {
irc . raw ( text ) ;
}
if ( ! connected ) {
2016-04-19 10:20:18 +00:00
target . chan . pushMessage ( this , new Msg ( {
type : Msg . Type . ERROR ,
2017-11-15 06:35:15 +00:00
text : "You are not connected to the IRC network, unable to send your command." ,
2016-04-19 10:20:18 +00:00
} ) ) ;
2016-03-06 09:24:56 +00:00
}
2014-09-13 21:29:45 +00:00
} ;
Client . prototype . more = function ( data ) {
2017-08-23 09:32:59 +00:00
const client = this ;
const target = client . find ( data . target ) ;
2014-09-13 21:29:45 +00:00
if ( ! target ) {
2018-01-07 13:04:37 +00:00
return null ;
2014-09-13 21:29:45 +00:00
}
2017-08-23 09:32:59 +00:00
const chan = target . chan ;
2017-09-12 13:05:40 +00:00
let messages = [ ] ;
let index = 0 ;
2017-08-23 09:32:59 +00:00
2017-09-12 13:05:40 +00:00
// If client requests -1, send last 100 messages
if ( data . lastId < 0 ) {
index = chan . messages . length ;
} else {
index = chan . messages . findIndex ( ( val ) => val . id === data . lastId ) ;
}
// If requested id is not found, an empty array will be sent
if ( index > 0 ) {
messages = chan . messages . slice ( Math . max ( 0 , index - 100 ) , index ) ;
}
2017-08-23 09:32:59 +00:00
2018-01-07 13:04:37 +00:00
return {
2014-09-13 21:29:45 +00:00
chan : chan . id ,
2017-11-15 06:35:15 +00:00
messages : messages ,
2018-01-07 13:04:37 +00:00
} ;
2014-09-13 21:29:45 +00:00
} ;
2016-12-21 10:38:50 +00:00
Client . prototype . open = function ( socketId , target ) {
// Opening a window like settings
if ( target === null ) {
2017-07-10 19:47:03 +00:00
this . attachedClients [ socketId ] . openChannel = - 1 ;
2016-12-21 10:38:50 +00:00
return ;
}
target = this . find ( target ) ;
2018-02-20 07:28:04 +00:00
2016-09-25 06:41:10 +00:00
if ( ! target ) {
return ;
2014-09-21 16:46:43 +00:00
}
2016-09-25 06:41:10 +00:00
target . chan . firstUnread = 0 ;
target . chan . unread = 0 ;
2017-09-03 15:57:07 +00:00
target . chan . highlight = 0 ;
2016-09-25 06:41:10 +00:00
2017-07-10 19:47:03 +00:00
this . attachedClients [ socketId ] . openChannel = target . chan . id ;
2016-09-25 06:41:10 +00:00
this . lastActiveChannel = target . chan . id ;
this . emit ( "open" , target . chan . id ) ;
2014-09-21 16:46:43 +00:00
} ;
2014-09-24 19:42:36 +00:00
Client . prototype . sort = function ( data ) {
2017-04-22 10:25:36 +00:00
const order = data . order ;
2014-09-24 19:42:36 +00:00
2017-04-22 10:25:36 +00:00
if ( ! _ . isArray ( order ) ) {
return ;
}
2014-09-24 19:42:36 +00:00
2017-04-22 10:25:36 +00:00
switch ( data . type ) {
2014-09-24 19:42:36 +00:00
case "networks" :
2017-04-08 12:34:31 +00:00
this . networks . sort ( ( a , b ) => order . indexOf ( a . id ) - order . indexOf ( b . id ) ) ;
2017-04-22 10:25:36 +00:00
// Sync order to connected clients
2017-04-08 12:34:31 +00:00
this . emit ( "sync_sort" , { order : this . networks . map ( ( obj ) => obj . id ) , type : data . type , target : data . target } ) ;
2017-04-22 10:25:36 +00:00
2014-09-24 19:42:36 +00:00
break ;
2018-01-11 11:33:36 +00:00
case "channels" : {
const network = _ . find ( this . networks , { id : data . target } ) ;
2018-02-20 07:28:04 +00:00
2014-09-24 19:42:36 +00:00
if ( ! network ) {
return ;
}
2017-04-22 10:25:36 +00:00
2017-04-08 12:34:31 +00:00
network . channels . sort ( ( a , b ) => order . indexOf ( a . id ) - order . indexOf ( b . id ) ) ;
2017-04-22 10:25:36 +00:00
// Sync order to connected clients
2017-04-08 12:34:31 +00:00
this . emit ( "sync_sort" , { order : network . channels . map ( ( obj ) => obj . id ) , type : data . type , target : data . target } ) ;
2017-04-22 10:25:36 +00:00
2014-09-24 19:42:36 +00:00
break ;
}
2018-01-11 11:33:36 +00:00
}
2016-06-12 10:02:37 +00:00
2017-04-22 10:25:36 +00:00
this . save ( ) ;
2014-09-24 19:42:36 +00:00
} ;
2016-02-17 02:29:44 +00:00
Client . prototype . names = function ( data ) {
2018-01-11 11:33:36 +00:00
const client = this ;
const target = client . find ( data . target ) ;
2018-02-20 07:28:04 +00:00
2016-02-17 02:29:44 +00:00
if ( ! target ) {
return ;
}
client . emit ( "names" , {
2016-03-23 10:15:44 +00:00
id : target . chan . id ,
2017-11-16 20:32:03 +00:00
users : target . chan . getSortedUsers ( target . network . irc ) ,
2016-02-17 02:29:44 +00:00
} ) ;
} ;
2017-08-30 17:26:45 +00:00
Client . prototype . quit = function ( signOut ) {
2017-08-29 12:43:52 +00:00
const sockets = this . sockets . sockets ;
const room = sockets . adapter . rooms [ this . id ] ;
if ( room && room . sockets ) {
for ( const user in room . sockets ) {
const socket = sockets . connected [ user ] ;
if ( socket ) {
2017-08-30 17:26:45 +00:00
if ( signOut ) {
socket . emit ( "sign-out" ) ;
}
2017-08-29 12:43:52 +00:00
socket . disconnect ( ) ;
}
2014-09-29 15:49:38 +00:00
}
}
2017-08-29 12:43:52 +00:00
2017-04-08 12:34:31 +00:00
this . networks . forEach ( ( network ) => {
2016-04-03 09:03:09 +00:00
if ( network . irc ) {
2017-08-18 19:04:16 +00:00
network . irc . quit ( Helper . config . leaveMessage ) ;
2016-04-03 09:03:09 +00:00
}
2017-08-11 12:02:58 +00:00
network . destroy ( ) ;
2014-09-13 21:29:45 +00:00
} ) ;
2018-03-10 16:49:16 +00:00
if ( this . messageStorage ) {
this . messageStorage . close ( ) ;
}
2014-09-13 17:18:42 +00:00
} ;
2014-10-11 20:44:56 +00:00
2017-07-10 19:47:03 +00:00
Client . prototype . clientAttach = function ( socketId , token ) {
2018-01-11 11:33:36 +00:00
const client = this ;
let save = false ;
2016-11-19 20:34:05 +00:00
2016-12-18 09:24:50 +00:00
if ( client . awayMessage && _ . size ( client . attachedClients ) === 0 ) {
client . networks . forEach ( function ( network ) {
// Only remove away on client attachment if
// there is no away message on this network
2018-03-09 10:33:24 +00:00
if ( network . irc && ! network . awayMessage ) {
2016-12-18 09:24:50 +00:00
network . irc . raw ( "AWAY" ) ;
}
} ) ;
}
2018-03-05 00:59:16 +00:00
const openChannel = client . lastActiveChannel ;
client . attachedClients [ socketId ] = { token , openChannel } ;
2017-04-28 15:58:14 +00:00
2016-11-19 20:34:05 +00:00
// Update old networks to store ip and hostmask
2017-04-08 12:34:31 +00:00
client . networks . forEach ( ( network ) => {
2016-11-19 20:34:05 +00:00
if ( ! network . ip ) {
save = true ;
network . ip = ( client . config && client . config . ip ) || client . ip ;
}
if ( ! network . hostname ) {
2018-01-11 11:33:36 +00:00
const hostmask = ( client . config && client . config . hostname ) || client . hostname ;
2016-11-19 20:34:05 +00:00
if ( hostmask ) {
save = true ;
network . hostmask = hostmask ;
}
}
} ) ;
if ( save ) {
client . save ( ) ;
}
2016-09-25 06:41:10 +00:00
} ;
Client . prototype . clientDetach = function ( socketId ) {
2016-12-18 09:24:50 +00:00
const client = this ;
2016-09-25 06:41:10 +00:00
delete this . attachedClients [ socketId ] ;
2016-12-18 09:24:50 +00:00
if ( client . awayMessage && _ . size ( client . attachedClients ) === 0 ) {
client . networks . forEach ( function ( network ) {
// Only set away on client deattachment if
// there is no away message on this network
2018-03-09 10:33:24 +00:00
if ( network . irc && ! network . awayMessage ) {
2016-12-18 09:24:50 +00:00
network . irc . raw ( "AWAY" , client . awayMessage ) ;
}
} ) ;
}
2016-09-25 06:41:10 +00:00
} ;
2017-07-10 19:47:03 +00:00
Client . prototype . registerPushSubscription = function ( session , subscription , noSave ) {
if ( ! _ . isPlainObject ( subscription ) || ! _ . isPlainObject ( subscription . keys )
|| typeof subscription . endpoint !== "string" || ! /^https?:\/\// . test ( subscription . endpoint )
|| typeof subscription . keys . p256dh !== "string" || typeof subscription . keys . auth !== "string" ) {
session . pushSubscription = null ;
return ;
}
const data = {
endpoint : subscription . endpoint ,
keys : {
p256dh : subscription . keys . p256dh ,
2017-11-15 06:35:15 +00:00
auth : subscription . keys . auth ,
} ,
2017-07-10 19:47:03 +00:00
} ;
session . pushSubscription = data ;
if ( ! noSave ) {
this . manager . updateUser ( this . name , {
2017-11-15 06:35:15 +00:00
sessions : this . config . sessions ,
2017-07-10 19:47:03 +00:00
} ) ;
}
return data ;
} ;
Client . prototype . unregisterPushSubscription = function ( token ) {
this . config . sessions [ token ] . pushSubscription = null ;
this . manager . updateUser ( this . name , {
2017-11-15 06:35:15 +00:00
sessions : this . config . sessions ,
2017-07-10 19:47:03 +00:00
} ) ;
} ;
2016-11-19 21:00:54 +00:00
Client . prototype . save = _ . debounce ( function SaveClient ( ) {
2016-06-08 09:26:24 +00:00
if ( Helper . config . public ) {
2014-10-12 05:15:03 +00:00
return ;
}
2014-11-09 16:01:22 +00:00
2016-11-19 21:00:54 +00:00
const client = this ;
2017-04-08 12:34:31 +00:00
const json = { } ;
json . networks = this . networks . map ( ( n ) => n . export ( ) ) ;
2016-02-17 00:14:43 +00:00
client . manager . updateUser ( client . name , json ) ;
2016-11-19 21:00:54 +00:00
} , 1000 , { maxWait : 10000 } ) ;