2016-06-15 21:50:56 +10:00
// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
2017-03-27 22:15:02 +10:00
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
2016-06-15 21:50:56 +10:00
// released under the MIT license
2012-04-07 11:44:59 -07:00
package irc
import (
2016-11-16 03:05:33 +10:00
"errors"
2012-04-17 22:11:35 -07:00
"fmt"
2016-06-30 19:28:34 +10:00
"log"
2012-04-07 11:44:59 -07:00
"net"
2016-10-16 20:35:50 +10:00
"runtime/debug"
2016-06-30 15:35:34 +10:00
"strconv"
2017-01-20 23:51:36 +10:00
"strings"
2017-04-18 22:26:01 +10:00
"sync"
2017-10-22 19:50:16 -04:00
"sync/atomic"
2012-12-11 23:12:35 -08:00
"time"
2016-06-17 22:17:42 +10:00
2017-06-15 10:14:19 -06:00
"github.com/goshuirc/irc-go/ircfmt"
"github.com/goshuirc/irc-go/ircmsg"
2017-06-14 12:00:53 -06:00
ident "github.com/oragono/go-ident"
2017-09-29 12:07:52 +10:00
"github.com/oragono/oragono/irc/caps"
2017-06-14 12:00:53 -06:00
"github.com/oragono/oragono/irc/sno"
2017-10-05 23:47:43 +10:00
"github.com/oragono/oragono/irc/utils"
2012-04-07 11:44:59 -07:00
)
2014-03-12 17:52:25 -07:00
const (
2017-06-19 14:53:16 -06:00
// IdentTimeoutSeconds is how many seconds before our ident (username) check times out.
2017-10-01 23:59:02 -04:00
IdentTimeoutSeconds = 1.5
2016-06-30 15:35:34 +10:00
)
var (
2017-06-19 14:53:16 -06:00
// ErrNickAlreadySet is a weird error that's sent when the server's consistency has been compromised.
ErrNickAlreadySet = errors . New ( "Nickname is already set" )
2014-03-12 17:52:25 -07:00
)
2016-10-23 11:48:57 +10:00
// Client is an IRC client.
2012-04-07 11:44:59 -07:00
type Client struct {
2016-10-11 23:51:46 +10:00
account * ClientAccount
atime time . Time
authorized bool
awayMessage string
2017-09-29 17:25:58 +10:00
capabilities * caps . Set
2016-10-11 23:51:46 +10:00
capState CapState
2017-09-29 17:25:58 +10:00
capVersion caps . Version
2016-10-11 23:51:46 +10:00
certfp string
channels ChannelSet
2016-10-23 10:47:11 +10:00
class * OperClass
2016-10-11 23:51:46 +10:00
ctime time . Time
2017-06-11 10:01:39 -06:00
exitedSnomaskSent bool
2017-03-24 12:23:21 +10:00
flags map [ Mode ] bool
2016-10-11 23:51:46 +10:00
hasQuit bool
2016-10-16 21:28:59 +10:00
hops int
2016-10-11 23:51:46 +10:00
hostname string
2017-10-15 12:24:28 -04:00
idletimer * IdleTimer
2017-04-18 22:26:01 +10:00
isDestroyed bool
isQuitting bool
2018-01-21 16:49:17 +10:00
languages [ ] string
2017-10-22 19:50:16 -04:00
maxlenTags uint32
maxlenRest uint32
2016-10-11 23:51:46 +10:00
nick string
nickCasefolded string
nickMaskCasefolded string
2017-04-18 22:26:01 +10:00
nickMaskString string // cache for nickmask string since it's used with lots of replies
2016-10-23 10:47:11 +10:00
operName string
2017-09-11 01:04:08 -04:00
proxiedIP string // actual remote IP if using the PROXY protocol
2017-09-25 22:47:03 -04:00
quitMessage string
2017-04-18 22:26:01 +10:00
rawHostname string
2016-10-11 23:51:46 +10:00
realname string
registered bool
2018-01-21 11:23:33 +10:00
resumeDetails * ResumeDetails
2016-10-11 23:51:46 +10:00
saslInProgress bool
saslMechanism string
saslValue string
server * Server
socket * Socket
2017-11-22 04:41:11 -05:00
stateMutex sync . RWMutex // tier 1
2016-10-11 23:51:46 +10:00
username string
2017-04-18 22:26:01 +10:00
vhost string
2016-10-23 11:01:05 +10:00
whoisLine string
2012-12-16 19:13:53 -08:00
}
2016-10-16 20:14:56 +10:00
// NewClient returns a client with all the appropriate info setup.
2016-06-29 01:09:07 +10:00
func NewClient ( server * Server , conn net . Conn , isTLS bool ) * Client {
2014-02-13 18:59:45 -08:00
now := time . Now ( )
2017-03-14 08:12:39 +10:00
socket := NewSocket ( conn , server . MaxSendQBytes )
2017-03-13 23:53:21 +10:00
go socket . RunSocketWriter ( )
2012-12-09 12:51:50 -08:00
client := & Client {
2016-09-19 22:30:29 +10:00
atime : now ,
2017-11-03 02:36:55 -04:00
authorized : server . Password ( ) == nil ,
2017-09-29 17:25:58 +10:00
capabilities : caps . NewSet ( ) ,
2016-10-16 13:54:09 +10:00
capState : CapNone ,
2017-09-29 17:25:58 +10:00
capVersion : caps . Cap301 ,
2016-09-19 22:30:29 +10:00
channels : make ( ChannelSet ) ,
ctime : now ,
2017-03-24 12:23:21 +10:00
flags : make ( map [ Mode ] bool ) ,
2016-09-19 22:30:29 +10:00
server : server ,
socket : & socket ,
account : & NoAccount ,
2016-10-11 23:51:46 +10:00
nick : "*" , // * is used until actual nick is given
nickCasefolded : "*" ,
2016-09-19 22:30:29 +10:00
nickMaskString : "*" , // * is used until actual nick is given
2012-12-09 12:51:50 -08:00
}
2018-01-22 21:30:28 +10:00
client . languages = server . languages . Default ( )
2018-01-22 21:26:01 +10:00
2017-10-29 05:04:32 +00:00
client . recomputeMaxlens ( )
2016-06-29 01:09:07 +10:00
if isTLS {
client . flags [ TLS ] = true
2016-09-07 21:32:58 +10:00
// error is not useful to us here anyways so we can ignore it
client . certfp , _ = client . socket . CertFP ( )
2016-06-29 01:09:07 +10:00
}
2016-06-30 19:28:34 +10:00
if server . checkIdent {
_ , serverPortString , err := net . SplitHostPort ( conn . LocalAddr ( ) . String ( ) )
serverPort , _ := strconv . Atoi ( serverPortString )
if err != nil {
log . Fatal ( err )
}
clientHost , clientPortString , err := net . SplitHostPort ( conn . RemoteAddr ( ) . String ( ) )
clientPort , _ := strconv . Atoi ( clientPortString )
if err != nil {
log . Fatal ( err )
}
2018-01-22 21:26:01 +10:00
client . Notice ( client . t ( "*** Looking up your username" ) )
2016-07-02 19:12:00 +10:00
resp , err := ident . Query ( clientHost , serverPort , clientPort , IdentTimeoutSeconds )
2016-06-30 19:28:34 +10:00
if err == nil {
username := resp . Identifier
2016-10-11 23:51:46 +10:00
_ , err := CasefoldName ( username ) // ensure it's a valid username
if err == nil {
2018-01-22 21:26:01 +10:00
client . Notice ( client . t ( "*** Found your username" ) )
2016-10-11 23:51:46 +10:00
client . username = username
2016-06-30 19:28:34 +10:00
// we don't need to updateNickMask here since nickMask is not used for anything yet
} else {
2018-01-22 21:26:01 +10:00
client . Notice ( client . t ( "*** Got a malformed username, ignoring" ) )
2016-06-30 19:28:34 +10:00
}
} else {
2018-01-22 21:26:01 +10:00
client . Notice ( client . t ( "*** Could not find your username" ) )
2016-06-30 19:28:34 +10:00
}
}
2014-02-23 22:21:39 -08:00
go client . run ( )
2012-12-12 23:27:17 -08:00
2012-04-07 23:32:08 -07:00
return client
2012-04-07 11:44:59 -07:00
}
2017-05-24 00:58:36 -06:00
// IP returns the IP address of this client.
func ( client * Client ) IP ( ) net . IP {
2017-09-11 01:04:08 -04:00
if client . proxiedIP != "" {
return net . ParseIP ( client . proxiedIP )
}
2017-10-05 23:47:43 +10:00
return net . ParseIP ( utils . IPString ( client . socket . conn . RemoteAddr ( ) ) )
2017-05-24 00:58:36 -06:00
}
2017-06-23 05:15:10 +10:00
// IPString returns the IP address of this client as a string.
func ( client * Client ) IPString ( ) string {
2017-09-11 01:04:08 -04:00
if client . proxiedIP != "" {
return client . proxiedIP
}
2017-06-23 05:15:10 +10:00
ip := client . IP ( ) . String ( )
if 0 < len ( ip ) && ip [ 0 ] == ':' {
ip = "0" + ip
}
return ip
}
2014-02-23 22:21:39 -08:00
//
// command goroutine
//
2017-10-22 19:50:16 -04:00
func ( client * Client ) recomputeMaxlens ( ) ( int , int ) {
2017-01-14 00:22:42 +10:00
maxlenTags := 512
maxlenRest := 512
2017-09-29 17:25:58 +10:00
if client . capabilities . Has ( caps . MessageTags ) {
2017-01-14 00:22:42 +10:00
maxlenTags = 4096
}
2017-09-29 17:25:58 +10:00
if client . capabilities . Has ( caps . MaxLine ) {
2017-11-03 02:36:55 -04:00
limits := client . server . Limits ( )
2017-10-02 04:42:50 -04:00
if limits . LineLen . Tags > maxlenTags {
maxlenTags = limits . LineLen . Tags
2017-01-14 00:22:42 +10:00
}
2017-10-02 04:42:50 -04:00
maxlenRest = limits . LineLen . Rest
2017-01-14 00:22:42 +10:00
}
2017-10-22 19:50:16 -04:00
atomic . StoreUint32 ( & client . maxlenTags , uint32 ( maxlenTags ) )
atomic . StoreUint32 ( & client . maxlenRest , uint32 ( maxlenRest ) )
2017-01-14 00:22:42 +10:00
return maxlenTags , maxlenRest
}
2017-10-22 19:50:16 -04:00
// allow these negotiated length limits to be read without locks; this is a convenience
// so that Client.Send doesn't have to acquire any Client locks
func ( client * Client ) maxlens ( ) ( int , int ) {
return int ( atomic . LoadUint32 ( & client . maxlenTags ) ) , int ( atomic . LoadUint32 ( & client . maxlenRest ) )
}
2014-02-23 22:21:39 -08:00
func ( client * Client ) run ( ) {
2014-04-15 08:49:52 -07:00
var err error
2016-06-17 22:17:42 +10:00
var isExiting bool
2014-04-15 08:49:52 -07:00
var line string
2016-06-17 22:17:42 +10:00
var msg ircmsg . IrcMessage
2014-04-15 08:49:52 -07:00
2017-10-23 18:38:32 -04:00
defer func ( ) {
2017-10-26 05:15:55 -04:00
if r := recover ( ) ; r != nil {
client . server . logger . Error ( "internal" ,
fmt . Sprintf ( "Client caused panic: %v\n%s" , r , debug . Stack ( ) ) )
if client . server . RecoverFromErrors ( ) {
client . server . logger . Error ( "internal" , "Disconnecting client and attempting to recover" )
} else {
panic ( r )
2017-10-26 04:19:01 -04:00
}
2017-10-23 18:38:32 -04:00
}
// ensure client connection gets closed
2018-01-21 11:59:52 +10:00
client . destroy ( false )
2017-10-23 18:38:32 -04:00
} ( )
2017-10-15 12:24:28 -04:00
client . idletimer = NewIdleTimer ( client )
client . idletimer . Start ( )
2016-10-15 16:29:34 +10:00
// Set the hostname for this client
2017-09-11 01:04:08 -04:00
// (may be overridden by a later PROXY command from stunnel)
2017-10-05 23:47:43 +10:00
client . rawHostname = utils . AddrLookupHostname ( client . socket . conn . RemoteAddr ( ) )
2014-04-15 08:49:52 -07:00
2016-06-17 22:17:42 +10:00
for {
2017-10-22 19:50:16 -04:00
maxlenTags , maxlenRest := client . recomputeMaxlens ( )
2016-06-17 22:17:42 +10:00
line , err = client . socket . Read ( )
if err != nil {
client . Quit ( "connection closed" )
break
}
2017-03-10 22:02:08 +10:00
client . server . logger . Debug ( "userinput " , client . nick , "<- " , line )
2017-03-06 22:11:10 +10:00
2017-01-14 00:22:42 +10:00
msg , err = ircmsg . ParseLineMaxLen ( line , maxlenTags , maxlenRest )
2017-01-19 07:56:33 +10:00
if err == ircmsg . ErrorLineIsEmpty {
continue
} else if err != nil {
2018-01-22 21:26:01 +10:00
client . Quit ( client . t ( "Received malformed line" ) )
2016-06-17 22:17:42 +10:00
break
2014-02-23 22:21:39 -08:00
}
2014-04-15 08:49:52 -07:00
2016-06-19 10:01:30 +10:00
cmd , exists := Commands [ msg . Command ]
if ! exists {
2016-11-04 21:38:47 +10:00
if len ( msg . Command ) > 0 {
2018-01-22 21:26:01 +10:00
client . Send ( nil , client . server . name , ERR_UNKNOWNCOMMAND , client . nick , msg . Command , client . t ( "Unknown command" ) )
2016-11-04 21:38:47 +10:00
} else {
2018-01-22 21:26:01 +10:00
client . Send ( nil , client . server . name , ERR_UNKNOWNCOMMAND , client . nick , "lastcmd" , client . t ( "No command given" ) )
2016-11-04 21:38:47 +10:00
}
2016-06-20 22:53:45 +10:00
continue
2016-06-19 10:01:30 +10:00
}
isExiting = cmd . Run ( client . server , client , msg )
2016-06-22 22:04:13 +10:00
if isExiting || client . isQuitting {
2016-06-17 22:17:42 +10:00
break
}
2014-02-23 22:21:39 -08:00
}
2014-04-15 08:49:52 -07:00
}
2016-06-17 22:17:42 +10:00
//
2017-05-09 20:37:48 +10:00
// idle, quit, timers and timeouts
2016-06-17 22:17:42 +10:00
//
2014-04-15 08:49:52 -07:00
2017-05-09 20:37:48 +10:00
// Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client).
2014-02-18 13:25:21 -08:00
func ( client * Client ) Active ( ) {
2017-12-02 20:05:06 -05:00
client . stateMutex . Lock ( )
defer client . stateMutex . Unlock ( )
2014-02-13 18:39:33 -08:00
client . atime = time . Now ( )
2014-02-18 13:25:21 -08:00
}
2014-02-13 18:39:33 -08:00
2017-05-09 20:37:48 +10:00
// Touch marks the client as alive (as it it has a connection to us and we
2017-10-15 12:24:28 -04:00
// can receive messages from it).
2014-02-18 13:25:21 -08:00
func ( client * Client ) Touch ( ) {
2017-10-15 12:24:28 -04:00
client . idletimer . Touch ( )
2014-02-09 12:13:09 -08:00
}
2017-10-15 12:24:28 -04:00
// Ping sends the client a PING message.
func ( client * Client ) Ping ( ) {
2016-10-11 23:51:46 +10:00
client . Send ( nil , "" , "PING" , client . nick )
2014-02-18 11:22:56 -08:00
2017-05-09 20:37:48 +10:00
}
//
// server goroutine
//
2016-10-16 20:35:50 +10:00
// Register sets the client details as appropriate when entering the network.
2014-02-18 13:25:21 -08:00
func ( client * Client ) Register ( ) {
2017-10-15 12:24:28 -04:00
client . stateMutex . Lock ( )
alreadyRegistered := client . registered
client . registered = true
client . stateMutex . Unlock ( )
if alreadyRegistered {
2014-03-12 17:52:25 -07:00
return
}
2016-10-16 20:14:56 +10:00
2018-01-21 11:23:33 +10:00
// apply resume details if we're able to.
client . TryResume ( )
// finish registration
2017-10-15 12:24:28 -04:00
client . Touch ( )
2017-10-04 00:57:03 -04:00
client . updateNickMask ( "" )
2017-10-04 02:59:59 -04:00
client . server . monitorManager . AlertAbout ( client , true )
2012-12-12 23:27:17 -08:00
}
2018-01-21 11:23:33 +10:00
// TryResume tries to resume if the client asked us to.
func ( client * Client ) TryResume ( ) {
if client . resumeDetails == nil {
return
}
server := client . server
// just grab these mutexes for safety. later we can work out whether we can grab+release them earlier
server . clients . Lock ( )
defer server . clients . Unlock ( )
server . channels . Lock ( )
defer server . channels . Unlock ( )
oldnick := client . resumeDetails . OldNick
timestamp := client . resumeDetails . Timestamp
var timestampString string
if timestamp != nil {
2018-01-21 12:23:47 +10:00
timestampString = timestamp . UTC ( ) . Format ( "2006-01-02T15:04:05.999Z" )
2018-01-21 11:23:33 +10:00
}
2018-01-21 12:23:47 +10:00
// can't use server.clients.Get since we hold server.clients' tier 1 mutex
casefoldedName , err := CasefoldName ( oldnick )
if err != nil {
2018-01-22 21:26:01 +10:00
client . Send ( nil , server . name , ERR_CANNOT_RESUME , oldnick , client . t ( "Cannot resume connection, old client not found" ) )
2018-01-21 12:23:47 +10:00
return
}
oldClient := server . clients . byNick [ casefoldedName ]
2018-01-21 11:23:33 +10:00
if oldClient == nil {
2018-01-22 21:26:01 +10:00
client . Send ( nil , server . name , ERR_CANNOT_RESUME , oldnick , client . t ( "Cannot resume connection, old client not found" ) )
2018-01-21 11:23:33 +10:00
return
}
oldAccountName := oldClient . AccountName ( )
newAccountName := client . AccountName ( )
if oldAccountName == "" || newAccountName == "" || oldAccountName != newAccountName {
2018-01-22 21:26:01 +10:00
client . Send ( nil , server . name , ERR_CANNOT_RESUME , oldnick , client . t ( "Cannot resume connection, old and new clients must be logged into the same account" ) )
2018-01-21 11:23:33 +10:00
return
}
if ! oldClient . HasMode ( TLS ) || ! client . HasMode ( TLS ) {
2018-01-22 21:26:01 +10:00
client . Send ( nil , server . name , ERR_CANNOT_RESUME , oldnick , client . t ( "Cannot resume connection, old and new clients must have TLS" ) )
2018-01-21 11:23:33 +10:00
return
}
2018-01-21 12:39:09 +10:00
// unmark the new client's nick as being occupied
server . clients . removeInternal ( client )
2018-01-21 11:23:33 +10:00
// send RESUMED to the reconnecting client
if timestamp == nil {
client . Send ( nil , oldClient . NickMaskString ( ) , "RESUMED" , oldClient . nick , client . username , client . Hostname ( ) )
} else {
client . Send ( nil , oldClient . NickMaskString ( ) , "RESUMED" , oldClient . nick , client . username , client . Hostname ( ) , timestampString )
}
// send QUIT/RESUMED to friends
for friend := range oldClient . Friends ( ) {
if friend . capabilities . Has ( caps . Resume ) {
if timestamp == nil {
friend . Send ( nil , oldClient . NickMaskString ( ) , "RESUMED" , oldClient . nick , client . username , client . Hostname ( ) )
} else {
friend . Send ( nil , oldClient . NickMaskString ( ) , "RESUMED" , oldClient . nick , client . username , client . Hostname ( ) , timestampString )
}
} else {
2018-01-22 21:26:01 +10:00
friend . Send ( nil , oldClient . NickMaskString ( ) , "QUIT" , friend . t ( "Client reconnected" ) )
2018-01-21 11:23:33 +10:00
}
}
// apply old client's details to new client
client . nick = oldClient . nick
2018-01-22 20:55:20 +10:00
client . updateNickMaskNoMutex ( )
2018-01-21 11:23:33 +10:00
for channel := range oldClient . channels {
channel . stateMutex . Lock ( )
2018-01-22 20:55:20 +10:00
client . channels [ channel ] = true
2018-01-21 13:13:20 +10:00
client . resumeDetails . SendFakeJoinsFor = append ( client . resumeDetails . SendFakeJoinsFor , channel . name )
2018-01-21 11:23:33 +10:00
oldModeSet := channel . members [ oldClient ]
channel . members . Remove ( oldClient )
channel . members [ client ] = oldModeSet
2018-01-21 13:13:20 +10:00
channel . regenerateMembersCache ( true )
2018-01-21 11:23:33 +10:00
2018-01-22 20:55:20 +10:00
// construct fake modestring if necessary
oldModes := oldModeSet . String ( )
var params [ ] string
if 0 < len ( oldModes ) {
params = [ ] string { channel . name , "+" + oldModes }
for _ = range oldModes {
params = append ( params , client . nick )
}
}
2018-01-21 11:23:33 +10:00
// send join for old clients
for member := range channel . members {
if member . capabilities . Has ( caps . Resume ) {
continue
}
if member . capabilities . Has ( caps . ExtendedJoin ) {
member . Send ( nil , client . nickMaskString , "JOIN" , channel . name , client . account . Name , client . realname )
} else {
member . Send ( nil , client . nickMaskString , "JOIN" , channel . name )
}
2018-01-22 20:55:20 +10:00
// send fake modestring if necessary
if 0 < len ( oldModes ) {
member . Send ( nil , server . name , "MODE" , params ... )
}
2018-01-21 11:23:33 +10:00
}
channel . stateMutex . Unlock ( )
}
server . clients . byNick [ oldnick ] = client
2018-01-21 12:39:09 +10:00
oldClient . destroy ( true )
2018-01-21 11:23:33 +10:00
}
2016-10-23 11:48:57 +10:00
// IdleTime returns how long this client's been idle.
2014-02-17 15:25:32 -08:00
func ( client * Client ) IdleTime ( ) time . Duration {
2017-12-02 20:05:06 -05:00
client . stateMutex . RLock ( )
defer client . stateMutex . RUnlock ( )
2014-02-17 15:25:32 -08:00
return time . Since ( client . atime )
}
2016-10-23 11:48:57 +10:00
// SignonTime returns this client's signon time as a unix timestamp.
2014-02-17 19:56:06 -08:00
func ( client * Client ) SignonTime ( ) int64 {
return client . ctime . Unix ( )
}
2016-10-23 11:48:57 +10:00
// IdleSeconds returns the number of seconds this client's been idle.
2014-02-17 19:08:57 -08:00
func ( client * Client ) IdleSeconds ( ) uint64 {
return uint64 ( client . IdleTime ( ) . Seconds ( ) )
}
2016-10-23 11:48:57 +10:00
// HasNick returns true if the client's nickname is set (used in registration).
2014-02-08 17:10:04 -08:00
func ( client * Client ) HasNick ( ) bool {
2017-11-22 04:41:11 -05:00
client . stateMutex . RLock ( )
defer client . stateMutex . RUnlock ( )
2016-10-11 23:51:46 +10:00
return client . nick != "" && client . nick != "*"
2012-12-16 19:13:53 -08:00
}
2017-04-16 11:31:33 +10:00
// HasUsername returns true if the client's username is set (used in registration).
2014-02-08 17:10:04 -08:00
func ( client * Client ) HasUsername ( ) bool {
2017-11-22 04:41:11 -05:00
client . stateMutex . RLock ( )
defer client . stateMutex . RUnlock ( )
2016-10-11 23:51:46 +10:00
return client . username != "" && client . username != "*"
2014-02-08 17:10:04 -08:00
}
2017-09-29 12:11:06 +10:00
// HasRoleCapabs returns true if client has the given (role) capabilities.
func ( client * Client ) HasRoleCapabs ( capabs ... string ) bool {
2016-10-23 11:13:08 +10:00
if client . class == nil {
return false
}
for _ , capab := range capabs {
if ! client . class . Capabilities [ capab ] {
return false
}
}
return true
}
2017-04-16 11:31:33 +10:00
// ModeString returns the mode string for this client.
func ( client * Client ) ModeString ( ) ( str string ) {
2016-09-07 21:50:42 +10:00
str = "+"
2017-04-16 11:31:33 +10:00
for flag := range client . flags {
2014-02-17 13:22:35 -08:00
str += flag . String ( )
2014-02-09 10:07:40 -08:00
}
2014-02-09 08:53:06 -08:00
return
2012-04-17 20:24:26 -07:00
}
2012-04-17 21:13:12 -07:00
2016-06-17 22:17:42 +10:00
// Friends refers to clients that share a channel with this client.
2017-09-29 17:25:58 +10:00
func ( client * Client ) Friends ( capabs ... caps . Capability ) ClientSet {
2014-02-18 15:28:20 -08:00
friends := make ( ClientSet )
2016-10-27 00:44:36 +10:00
// make sure that I have the right caps
hasCaps := true
2017-09-29 17:25:58 +10:00
for _ , capab := range capabs {
if ! client . capabilities . Has ( capab ) {
2016-10-27 00:44:36 +10:00
hasCaps = false
break
}
}
if hasCaps {
friends . Add ( client )
}
2017-10-22 19:50:16 -04:00
for _ , channel := range client . Channels ( ) {
for _ , member := range channel . Members ( ) {
2016-10-13 18:08:08 +10:00
// make sure they have all the required caps
2017-09-26 08:36:34 +10:00
hasCaps = true
2017-09-29 17:25:58 +10:00
for _ , capab := range capabs {
if ! member . capabilities . Has ( capab ) {
2017-09-26 08:36:34 +10:00
hasCaps = false
break
2016-10-13 18:08:08 +10:00
}
}
2017-09-26 08:36:34 +10:00
if hasCaps {
friends . Add ( member )
}
2014-02-18 15:28:20 -08:00
}
2014-02-16 17:23:47 -08:00
}
2014-02-18 15:28:20 -08:00
return friends
2014-02-16 17:23:47 -08:00
}
2017-10-04 00:57:03 -04:00
// updateNick updates `nick` and `nickCasefolded`.
func ( client * Client ) updateNick ( nick string ) {
casefoldedName , err := CasefoldName ( nick )
2016-10-11 23:51:46 +10:00
if err != nil {
2016-10-16 20:35:50 +10:00
log . Println ( fmt . Sprintf ( "ERROR: Nick [%s] couldn't be casefolded... this should never happen. Printing stacktrace." , client . nick ) )
debug . PrintStack ( )
2016-10-11 23:51:46 +10:00
}
2017-10-04 00:57:03 -04:00
client . stateMutex . Lock ( )
client . nick = nick
2016-10-11 23:51:46 +10:00
client . nickCasefolded = casefoldedName
2017-10-04 00:57:03 -04:00
client . stateMutex . Unlock ( )
2016-10-16 20:35:50 +10:00
}
// updateNickMask updates the casefolded nickname and nickmask.
2017-10-04 00:57:03 -04:00
func ( client * Client ) updateNickMask ( nick string ) {
// on "", just regenerate the nickmask etc.
// otherwise, update the actual nick
if nick != "" {
client . updateNick ( nick )
}
client . stateMutex . Lock ( )
2017-11-22 04:41:11 -05:00
defer client . stateMutex . Unlock ( )
2018-01-22 20:55:20 +10:00
client . updateNickMaskNoMutex ( )
}
2016-10-11 23:51:46 +10:00
2018-01-22 20:55:20 +10:00
// updateNickMask updates the casefolded nickname and nickmask, not holding any mutexes.
func ( client * Client ) updateNickMaskNoMutex ( ) {
2016-10-23 11:28:31 +10:00
if len ( client . vhost ) > 0 {
client . hostname = client . vhost
} else {
client . hostname = client . rawHostname
}
2017-10-04 00:57:03 -04:00
nickMaskString := fmt . Sprintf ( "%s!%s@%s" , client . nick , client . username , client . hostname )
nickMaskCasefolded , err := Casefold ( nickMaskString )
2016-10-11 23:51:46 +10:00
if err != nil {
2016-10-16 20:35:50 +10:00
log . Println ( fmt . Sprintf ( "ERROR: Nickmask [%s] couldn't be casefolded... this should never happen. Printing stacktrace." , client . nickMaskString ) )
debug . PrintStack ( )
2016-10-11 23:51:46 +10:00
}
2017-10-04 00:57:03 -04:00
client . nickMaskString = nickMaskString
2016-10-11 23:51:46 +10:00
client . nickMaskCasefolded = nickMaskCasefolded
2016-06-19 15:37:29 +10:00
}
2017-01-11 22:38:16 +10:00
// AllNickmasks returns all the possible nickmasks for the client.
func ( client * Client ) AllNickmasks ( ) [ ] string {
var masks [ ] string
var mask string
var err error
if len ( client . vhost ) > 0 {
mask , err = Casefold ( fmt . Sprintf ( "%s!%s@%s" , client . nick , client . username , client . vhost ) )
if err == nil {
masks = append ( masks , mask )
}
}
mask , err = Casefold ( fmt . Sprintf ( "%s!%s@%s" , client . nick , client . username , client . rawHostname ) )
if err == nil {
masks = append ( masks , mask )
}
2017-10-05 23:47:43 +10:00
mask2 , err := Casefold ( fmt . Sprintf ( "%s!%s@%s" , client . nick , client . username , utils . IPString ( client . socket . conn . RemoteAddr ( ) ) ) )
2017-01-11 22:38:16 +10:00
if err == nil && mask2 != mask {
masks = append ( masks , mask2 )
}
return masks
}
2017-09-28 15:49:01 +10:00
// LoggedIntoAccount returns true if this client is logged into an account.
func ( client * Client ) LoggedIntoAccount ( ) bool {
return client . account != nil && client . account != & NoAccount
}
2017-10-05 23:39:57 +10:00
// RplISupport outputs our ISUPPORT lines to the client. This is used on connection and in VERSION responses.
func ( client * Client ) RplISupport ( ) {
2018-01-23 23:31:29 +10:00
translatedISupport := client . t ( "are supported by this server" )
2017-11-03 02:36:55 -04:00
for _ , tokenline := range client . server . ISupport ( ) . CachedReply {
2017-10-05 23:39:57 +10:00
// ugly trickery ahead
2018-01-23 23:31:29 +10:00
tokenline = append ( tokenline , translatedISupport )
2017-10-05 23:39:57 +10:00
client . Send ( nil , client . server . name , RPL_ISUPPORT , append ( [ ] string { client . nick } , tokenline ... ) ... )
}
}
2017-10-16 10:48:05 +10:00
// Quit sets the given quit message for the client and tells the client to quit out.
2016-06-19 10:01:30 +10:00
func ( client * Client ) Quit ( message string ) {
2017-10-10 20:49:29 -04:00
client . stateMutex . Lock ( )
2017-10-15 12:24:28 -04:00
alreadyQuit := client . isQuitting
2017-10-10 20:49:29 -04:00
if ! alreadyQuit {
2017-10-15 12:24:28 -04:00
client . isQuitting = true
2017-09-25 22:47:03 -04:00
client . quitMessage = message
2016-11-29 21:06:01 +10:00
}
2017-10-10 20:49:29 -04:00
client . stateMutex . Unlock ( )
if alreadyQuit {
return
}
quitMsg := ircmsg . MakeMessage ( nil , client . nickMaskString , "QUIT" , message )
quitLine , _ := quitMsg . Line ( )
errorMsg := ircmsg . MakeMessage ( nil , "" , "ERROR" , message )
errorLine , _ := errorMsg . Line ( )
client . socket . SetFinalData ( quitLine + errorLine )
2016-06-17 22:17:42 +10:00
}
2016-10-23 11:48:57 +10:00
// destroy gets rid of a client, removes them from server lists etc.
2018-01-21 11:23:33 +10:00
func ( client * Client ) destroy ( beingResumed bool ) {
2017-10-11 12:48:06 -04:00
// allow destroy() to execute at most once
2018-01-21 12:39:09 +10:00
if ! beingResumed {
client . stateMutex . Lock ( )
2018-01-22 20:55:20 +10:00
}
isDestroyed := client . isDestroyed
client . isDestroyed = true
if ! beingResumed {
2018-01-21 12:39:09 +10:00
client . stateMutex . Unlock ( )
2018-01-22 20:55:20 +10:00
}
if isDestroyed {
return
2014-02-18 13:25:21 -08:00
}
2014-02-19 18:46:46 -08:00
2018-01-21 11:23:33 +10:00
if beingResumed {
client . server . logger . Debug ( "quit" , fmt . Sprintf ( "%s is being resumed" , client . nick ) )
} else {
client . server . logger . Debug ( "quit" , fmt . Sprintf ( "%s is no longer on the server" , client . nick ) )
}
2017-03-06 22:11:10 +10:00
2016-11-29 21:06:01 +10:00
// send quit/error message to client if they haven't been sent already
client . Quit ( "Connection closed" )
2014-02-18 15:28:20 -08:00
friends := client . Friends ( )
friends . Remove ( client )
2018-01-21 11:23:33 +10:00
if ! beingResumed {
client . server . whoWas . Append ( client )
}
2016-06-17 22:17:42 +10:00
2016-10-23 23:05:00 +10:00
// remove from connection limits
2017-05-24 00:58:36 -06:00
ipaddr := client . IP ( )
2016-10-23 23:05:00 +10:00
// this check shouldn't be required but eh
if ipaddr != nil {
2017-10-09 17:37:13 -04:00
client . server . connectionLimiter . RemoveClient ( ipaddr )
2016-10-23 23:05:00 +10:00
}
2016-10-16 20:14:56 +10:00
// alert monitors
2017-10-04 02:59:59 -04:00
client . server . monitorManager . AlertAbout ( client , false )
2017-10-04 00:57:03 -04:00
// clean up monitor state
2017-10-04 02:59:59 -04:00
client . server . monitorManager . RemoveAll ( client )
2016-10-16 20:14:56 +10:00
2016-06-17 22:17:42 +10:00
// clean up channels
2017-10-30 05:21:47 -04:00
for _ , channel := range client . Channels ( ) {
2018-01-21 11:23:33 +10:00
if ! beingResumed {
channel . Quit ( client )
}
2017-10-22 19:50:16 -04:00
for _ , member := range channel . Members ( ) {
friends . Add ( member )
}
2016-06-17 22:17:42 +10:00
}
// clean up server
2018-01-21 11:23:33 +10:00
if ! beingResumed {
client . server . clients . Remove ( client )
}
2016-06-17 22:17:42 +10:00
// clean up self
2017-10-15 12:24:28 -04:00
if client . idletimer != nil {
client . idletimer . Stop ( )
2016-06-17 22:17:42 +10:00
}
client . socket . Close ( )
2016-11-29 21:06:01 +10:00
// send quit messages to friends
2018-01-21 11:23:33 +10:00
if ! beingResumed {
for friend := range friends {
if client . quitMessage == "" {
client . quitMessage = "Exited"
}
friend . Send ( nil , client . nickMaskString , "QUIT" , client . quitMessage )
2017-09-25 22:47:03 -04:00
}
2016-11-29 21:06:01 +10:00
}
2017-06-11 10:01:39 -06:00
if ! client . exitedSnomaskSent {
2018-01-21 11:23:33 +10:00
if beingResumed {
client . server . snomasks . Send ( sno . LocalQuits , fmt . Sprintf ( ircfmt . Unescape ( "%s$r is resuming their connection, old client has been destroyed" ) , client . nick ) )
} else {
client . server . snomasks . Send ( sno . LocalQuits , fmt . Sprintf ( ircfmt . Unescape ( "%s$r exited the network" ) , client . nick ) )
}
2017-06-11 10:01:39 -06:00
}
2016-06-19 10:01:30 +10:00
}
2014-02-18 13:25:21 -08:00
2017-01-14 15:28:50 +10:00
// SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
// Adds account-tag to the line as well.
2017-01-14 19:52:47 +10:00
func ( client * Client ) SendSplitMsgFromClient ( msgid string , from * Client , tags * map [ string ] ircmsg . TagValue , command , target string , message SplitMessage ) {
2017-09-29 17:25:58 +10:00
if client . capabilities . Has ( caps . MaxLine ) {
2017-01-18 08:05:31 +10:00
client . SendFromClient ( msgid , from , tags , command , target , message . ForMaxLine )
2017-01-14 15:28:50 +10:00
} else {
for _ , str := range message . For512 {
2017-01-18 08:05:31 +10:00
client . SendFromClient ( msgid , from , tags , command , target , str )
2017-01-14 15:28:50 +10:00
}
}
}
2016-09-12 11:25:31 +10:00
// SendFromClient sends an IRC line coming from a specific client.
// Adds account-tag to the line as well.
2017-01-14 21:48:57 +10:00
func ( client * Client ) SendFromClient ( msgid string , from * Client , tags * map [ string ] ircmsg . TagValue , command string , params ... string ) error {
2016-09-12 11:25:31 +10:00
// attach account-tag
2017-09-29 17:25:58 +10:00
if client . capabilities . Has ( caps . AccountTag ) && from . account != & NoAccount {
2016-09-12 11:25:31 +10:00
if tags == nil {
tags = ircmsg . MakeTags ( "account" , from . account . Name )
} else {
( * tags ) [ "account" ] = ircmsg . MakeTagValue ( from . account . Name )
}
}
2017-01-14 19:52:47 +10:00
// attach message-id
2017-09-29 17:25:58 +10:00
if len ( msgid ) > 0 && client . capabilities . Has ( caps . MessageTags ) {
2017-01-14 19:52:47 +10:00
if tags == nil {
tags = ircmsg . MakeTags ( "draft/msgid" , msgid )
} else {
( * tags ) [ "draft/msgid" ] = ircmsg . MakeTagValue ( msgid )
}
}
2016-09-12 11:25:31 +10:00
2017-01-14 21:48:57 +10:00
return client . Send ( tags , from . nickMaskString , command , params ... )
2016-09-12 11:25:31 +10:00
}
2017-01-21 00:07:10 +10:00
var (
2017-01-23 09:03:49 +10:00
// these are all the output commands that MUST have their last param be a trailing.
// this is needed because silly clients like to treat trailing as separate from the
// other params in messages.
2017-01-21 00:07:10 +10:00
commandsThatMustUseTrailing = map [ string ] bool {
"PRIVMSG" : true ,
"NOTICE" : true ,
2017-01-23 09:03:49 +10:00
RPL_WHOISCHANNELS : true ,
2017-03-06 15:50:23 +10:00
RPL_USERHOST : true ,
2017-01-21 00:07:10 +10:00
}
)
2016-06-19 10:01:30 +10:00
// Send sends an IRC line to the client.
func ( client * Client ) Send ( tags * map [ string ] ircmsg . TagValue , prefix string , command string , params ... string ) error {
2016-08-13 22:04:21 +10:00
// attach server-time
2017-09-29 17:25:58 +10:00
if client . capabilities . Has ( caps . ServerTime ) {
2017-02-07 01:37:32 +01:00
t := time . Now ( ) . UTC ( ) . Format ( "2006-01-02T15:04:05.999Z" )
2016-08-13 22:04:21 +10:00
if tags == nil {
2017-02-07 01:37:32 +01:00
tags = ircmsg . MakeTags ( "time" , t )
2016-08-13 22:04:21 +10:00
} else {
2017-02-07 01:37:32 +01:00
( * tags ) [ "time" ] = ircmsg . MakeTagValue ( t )
2016-08-13 22:04:21 +10:00
}
}
2017-05-09 20:37:48 +10:00
// force trailing, if message requires it
var usedTrailingHack bool
2017-01-21 00:07:10 +10:00
if commandsThatMustUseTrailing [ strings . ToUpper ( command ) ] && len ( params ) > 0 {
2017-01-20 23:51:36 +10:00
lastParam := params [ len ( params ) - 1 ]
2017-05-09 20:37:48 +10:00
// to force trailing, we ensure the final param contains a space
2017-01-20 23:51:36 +10:00
if ! strings . Contains ( lastParam , " " ) {
params [ len ( params ) - 1 ] = lastParam + " "
2017-05-09 20:37:48 +10:00
usedTrailingHack = true
2017-01-20 23:51:36 +10:00
}
}
2016-08-13 22:04:21 +10:00
// send out the message
2016-09-19 23:00:19 +10:00
message := ircmsg . MakeMessage ( tags , prefix , command , params ... )
2017-01-14 00:22:42 +10:00
maxlenTags , maxlenRest := client . maxlens ( )
line , err := message . LineMaxLen ( maxlenTags , maxlenRest )
2016-06-19 10:01:30 +10:00
if err != nil {
2016-09-19 23:00:19 +10:00
// try not to fail quietly - especially useful when running tests, as a note to dig deeper
2016-11-01 23:56:21 +10:00
// log.Println("Error assembling message:")
// spew.Dump(message)
2016-11-04 21:38:47 +10:00
// debug.PrintStack()
2016-10-11 23:51:46 +10:00
message = ircmsg . MakeMessage ( nil , client . server . name , ERR_UNKNOWNERROR , "*" , "Error assembling message for sending" )
2016-09-19 23:00:19 +10:00
line , _ := message . Line ( )
client . socket . Write ( line )
2016-06-19 10:01:30 +10:00
return err
2014-02-16 17:23:47 -08:00
}
2017-01-20 23:51:36 +10:00
2017-05-09 20:37:48 +10:00
// is we used the trailing hack, we need to strip the final space we appended earlier
if usedTrailingHack {
2017-01-20 23:51:36 +10:00
line = line [ : len ( line ) - 3 ] + "\r\n"
}
2017-03-10 22:02:08 +10:00
client . server . logger . Debug ( "useroutput" , client . nick , " ->" , strings . TrimRight ( line , "\r\n" ) )
2017-03-06 22:11:10 +10:00
2016-06-19 10:01:30 +10:00
client . socket . Write ( line )
return nil
}
// Notice sends the client a notice from the server.
func ( client * Client ) Notice ( text string ) {
2017-05-09 21:09:44 +10:00
limit := 400
2017-09-29 17:25:58 +10:00
if client . capabilities . Has ( caps . MaxLine ) {
2017-11-03 02:36:55 -04:00
limit = client . server . Limits ( ) . LineLen . Rest - 110
2017-05-09 21:09:44 +10:00
}
lines := wordWrap ( text , limit )
for _ , line := range lines {
client . Send ( nil , client . server . name , "NOTICE" , client . nick , line )
}
2014-02-16 17:23:47 -08:00
}
2017-10-22 19:50:16 -04:00
func ( client * Client ) addChannel ( channel * Channel ) {
client . stateMutex . Lock ( )
client . channels [ channel ] = true
client . stateMutex . Unlock ( )
}
func ( client * Client ) removeChannel ( channel * Channel ) {
client . stateMutex . Lock ( )
delete ( client . channels , channel )
client . stateMutex . Unlock ( )
}