You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

client.go 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  4. // released under the MIT license
  5. package irc
  6. import (
  7. "fmt"
  8. "net"
  9. "runtime/debug"
  10. "strconv"
  11. "strings"
  12. "sync"
  13. "sync/atomic"
  14. "time"
  15. "github.com/goshuirc/irc-go/ircfmt"
  16. "github.com/goshuirc/irc-go/ircmsg"
  17. ident "github.com/oragono/go-ident"
  18. "github.com/oragono/oragono/irc/caps"
  19. "github.com/oragono/oragono/irc/history"
  20. "github.com/oragono/oragono/irc/modes"
  21. "github.com/oragono/oragono/irc/sno"
  22. "github.com/oragono/oragono/irc/utils"
  23. )
  24. const (
  25. // IdentTimeoutSeconds is how many seconds before our ident (username) check times out.
  26. IdentTimeoutSeconds = 1.5
  27. IRCv3TimestampFormat = "2006-01-02T15:04:05.999Z"
  28. )
  29. var (
  30. LoopbackIP = net.ParseIP("127.0.0.1")
  31. )
  32. // ResumeDetails is a place to stash data at various stages of
  33. // the resume process: when handling the RESUME command itself,
  34. // when completing the registration, and when rejoining channels.
  35. type ResumeDetails struct {
  36. OldClient *Client
  37. OldNick string
  38. OldNickMask string
  39. PresentedToken string
  40. Timestamp time.Time
  41. ResumedAt time.Time
  42. Channels []string
  43. HistoryIncomplete bool
  44. }
  45. // Client is an IRC client.
  46. type Client struct {
  47. account string
  48. accountName string
  49. atime time.Time
  50. authorized bool
  51. awayMessage string
  52. capabilities *caps.Set
  53. capState caps.State
  54. capVersion caps.Version
  55. certfp string
  56. channels ChannelSet
  57. ctime time.Time
  58. exitedSnomaskSent bool
  59. fakelag *Fakelag
  60. flags *modes.ModeSet
  61. hasQuit bool
  62. hops int
  63. hostname string
  64. idletimer *IdleTimer
  65. isDestroyed bool
  66. isQuitting bool
  67. languages []string
  68. maxlenTags uint32
  69. maxlenRest uint32
  70. nick string
  71. nickCasefolded string
  72. nickMaskCasefolded string
  73. nickMaskString string // cache for nickmask string since it's used with lots of replies
  74. nickTimer *NickTimer
  75. oper *Oper
  76. preregNick string
  77. proxiedIP net.IP // actual remote IP if using the PROXY protocol
  78. quitMessage string
  79. rawHostname string
  80. realname string
  81. registered bool
  82. resumeDetails *ResumeDetails
  83. resumeToken string
  84. saslInProgress bool
  85. saslMechanism string
  86. saslValue string
  87. server *Server
  88. socket *Socket
  89. stateMutex sync.RWMutex // tier 1
  90. username string
  91. vhost string
  92. history *history.Buffer
  93. }
  94. // NewClient sets up a new client and starts its goroutine.
  95. func NewClient(server *Server, conn net.Conn, isTLS bool) {
  96. now := time.Now()
  97. config := server.Config()
  98. fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
  99. socket := NewSocket(conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
  100. client := &Client{
  101. atime: now,
  102. authorized: server.Password() == nil,
  103. capabilities: caps.NewSet(),
  104. capState: caps.NoneState,
  105. capVersion: caps.Cap301,
  106. channels: make(ChannelSet),
  107. ctime: now,
  108. flags: modes.NewModeSet(),
  109. server: server,
  110. socket: socket,
  111. nick: "*", // * is used until actual nick is given
  112. nickCasefolded: "*",
  113. nickMaskString: "*", // * is used until actual nick is given
  114. history: history.NewHistoryBuffer(config.History.ClientLength),
  115. }
  116. client.languages = server.languages.Default()
  117. client.recomputeMaxlens()
  118. if isTLS {
  119. client.SetMode(modes.TLS, true)
  120. // error is not useful to us here anyways so we can ignore it
  121. client.certfp, _ = client.socket.CertFP()
  122. }
  123. if config.Server.CheckIdent && !utils.AddrIsUnix(conn.RemoteAddr()) {
  124. _, serverPortString, err := net.SplitHostPort(conn.LocalAddr().String())
  125. if err != nil {
  126. server.logger.Error("internal", "bad server address", err.Error())
  127. return
  128. }
  129. serverPort, _ := strconv.Atoi(serverPortString)
  130. clientHost, clientPortString, err := net.SplitHostPort(conn.RemoteAddr().String())
  131. if err != nil {
  132. server.logger.Error("internal", "bad client address", err.Error())
  133. return
  134. }
  135. clientPort, _ := strconv.Atoi(clientPortString)
  136. client.Notice(client.t("*** Looking up your username"))
  137. resp, err := ident.Query(clientHost, serverPort, clientPort, IdentTimeoutSeconds)
  138. if err == nil {
  139. username := resp.Identifier
  140. _, err := CasefoldName(username) // ensure it's a valid username
  141. if err == nil {
  142. client.Notice(client.t("*** Found your username"))
  143. client.username = username
  144. // we don't need to updateNickMask here since nickMask is not used for anything yet
  145. } else {
  146. client.Notice(client.t("*** Got a malformed username, ignoring"))
  147. }
  148. } else {
  149. client.Notice(client.t("*** Could not find your username"))
  150. }
  151. }
  152. go client.run()
  153. }
  154. func (client *Client) resetFakelag() {
  155. fakelag := func() *Fakelag {
  156. if client.HasRoleCapabs("nofakelag") {
  157. return nil
  158. }
  159. flc := client.server.FakelagConfig()
  160. if !flc.Enabled {
  161. return nil
  162. }
  163. return NewFakelag(flc.Window, flc.BurstLimit, flc.MessagesPerWindow, flc.Cooldown)
  164. }()
  165. client.stateMutex.Lock()
  166. defer client.stateMutex.Unlock()
  167. client.fakelag = fakelag
  168. }
  169. // IP returns the IP address of this client.
  170. func (client *Client) IP() net.IP {
  171. if client.proxiedIP != nil {
  172. return client.proxiedIP
  173. }
  174. if ip := utils.AddrToIP(client.socket.conn.RemoteAddr()); ip != nil {
  175. return ip
  176. }
  177. // unix domain socket that hasn't issued PROXY/WEBIRC yet. YOLO
  178. return LoopbackIP
  179. }
  180. // IPString returns the IP address of this client as a string.
  181. func (client *Client) IPString() string {
  182. ip := client.IP().String()
  183. if 0 < len(ip) && ip[0] == ':' {
  184. ip = "0" + ip
  185. }
  186. return ip
  187. }
  188. //
  189. // command goroutine
  190. //
  191. func (client *Client) recomputeMaxlens() (int, int) {
  192. maxlenTags := 512
  193. maxlenRest := 512
  194. if client.capabilities.Has(caps.MessageTags) {
  195. maxlenTags = 4096
  196. }
  197. if client.capabilities.Has(caps.MaxLine) {
  198. limits := client.server.Limits()
  199. if limits.LineLen.Tags > maxlenTags {
  200. maxlenTags = limits.LineLen.Tags
  201. }
  202. maxlenRest = limits.LineLen.Rest
  203. }
  204. atomic.StoreUint32(&client.maxlenTags, uint32(maxlenTags))
  205. atomic.StoreUint32(&client.maxlenRest, uint32(maxlenRest))
  206. return maxlenTags, maxlenRest
  207. }
  208. // allow these negotiated length limits to be read without locks; this is a convenience
  209. // so that Client.Send doesn't have to acquire any Client locks
  210. func (client *Client) maxlens() (int, int) {
  211. return int(atomic.LoadUint32(&client.maxlenTags)), int(atomic.LoadUint32(&client.maxlenRest))
  212. }
  213. func (client *Client) run() {
  214. var err error
  215. var isExiting bool
  216. var line string
  217. var msg ircmsg.IrcMessage
  218. defer func() {
  219. if r := recover(); r != nil {
  220. client.server.logger.Error("internal",
  221. fmt.Sprintf("Client caused panic: %v\n%s", r, debug.Stack()))
  222. if client.server.RecoverFromErrors() {
  223. client.server.logger.Error("internal", "Disconnecting client and attempting to recover")
  224. } else {
  225. panic(r)
  226. }
  227. }
  228. // ensure client connection gets closed
  229. client.destroy(false)
  230. }()
  231. client.idletimer = NewIdleTimer(client)
  232. client.idletimer.Start()
  233. client.nickTimer = NewNickTimer(client)
  234. client.resetFakelag()
  235. // Set the hostname for this client
  236. // (may be overridden by a later PROXY command from stunnel)
  237. client.rawHostname = utils.AddrLookupHostname(client.socket.conn.RemoteAddr())
  238. firstLine := true
  239. for {
  240. maxlenTags, maxlenRest := client.recomputeMaxlens()
  241. line, err = client.socket.Read()
  242. if err != nil {
  243. quitMessage := "connection closed"
  244. if err == errReadQ {
  245. quitMessage = "readQ exceeded"
  246. }
  247. client.Quit(quitMessage)
  248. break
  249. }
  250. client.server.logger.Debug("userinput", client.nick, "<- ", line)
  251. // special-cased handling of PROXY protocol, see `handleProxyCommand` for details:
  252. if firstLine {
  253. firstLine = false
  254. if strings.HasPrefix(line, "PROXY") {
  255. err = handleProxyCommand(client.server, client, line)
  256. if err != nil {
  257. break
  258. } else {
  259. continue
  260. }
  261. }
  262. }
  263. msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
  264. if err == ircmsg.ErrorLineIsEmpty {
  265. continue
  266. } else if err != nil {
  267. client.Quit(client.t("Received malformed line"))
  268. break
  269. }
  270. cmd, exists := Commands[msg.Command]
  271. if !exists {
  272. if len(msg.Command) > 0 {
  273. client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.nick, msg.Command, client.t("Unknown command"))
  274. } else {
  275. client.Send(nil, client.server.name, ERR_UNKNOWNCOMMAND, client.nick, "lastcmd", client.t("No command given"))
  276. }
  277. continue
  278. }
  279. isExiting = cmd.Run(client.server, client, msg)
  280. if isExiting || client.isQuitting {
  281. break
  282. }
  283. }
  284. }
  285. //
  286. // idle, quit, timers and timeouts
  287. //
  288. // Active updates when the client was last 'active' (i.e. the user should be sitting in front of their client).
  289. func (client *Client) Active() {
  290. client.stateMutex.Lock()
  291. defer client.stateMutex.Unlock()
  292. client.atime = time.Now()
  293. }
  294. // Touch marks the client as alive (as it it has a connection to us and we
  295. // can receive messages from it).
  296. func (client *Client) Touch() {
  297. client.idletimer.Touch()
  298. }
  299. // Ping sends the client a PING message.
  300. func (client *Client) Ping() {
  301. client.Send(nil, "", "PING", client.nick)
  302. }
  303. // Register sets the client details as appropriate when entering the network.
  304. func (client *Client) Register() {
  305. client.stateMutex.Lock()
  306. alreadyRegistered := client.registered
  307. client.registered = true
  308. client.stateMutex.Unlock()
  309. if alreadyRegistered {
  310. return
  311. }
  312. // apply resume details if we're able to.
  313. client.TryResume()
  314. // finish registration
  315. client.updateNickMask("")
  316. client.server.monitorManager.AlertAbout(client, true)
  317. }
  318. // TryResume tries to resume if the client asked us to.
  319. func (client *Client) TryResume() {
  320. if client.resumeDetails == nil {
  321. return
  322. }
  323. server := client.server
  324. config := server.Config()
  325. oldnick := client.resumeDetails.OldNick
  326. timestamp := client.resumeDetails.Timestamp
  327. var timestampString string
  328. if !timestamp.IsZero() {
  329. timestampString = timestamp.UTC().Format(IRCv3TimestampFormat)
  330. }
  331. oldClient := server.clients.Get(oldnick)
  332. if oldClient == nil {
  333. client.Send(nil, server.name, "RESUME", "ERR", oldnick, client.t("Cannot resume connection, old client not found"))
  334. client.resumeDetails = nil
  335. return
  336. }
  337. oldNick := oldClient.Nick()
  338. oldNickmask := oldClient.NickMaskString()
  339. resumeAllowed := config.Server.AllowPlaintextResume || (oldClient.HasMode(modes.TLS) && client.HasMode(modes.TLS))
  340. if !resumeAllowed {
  341. client.Send(nil, server.name, "RESUME", "ERR", oldnick, client.t("Cannot resume connection, old and new clients must have TLS"))
  342. client.resumeDetails = nil
  343. return
  344. }
  345. oldResumeToken := oldClient.ResumeToken()
  346. if oldResumeToken == "" || !utils.SecretTokensMatch(oldResumeToken, client.resumeDetails.PresentedToken) {
  347. client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection, invalid resume token"))
  348. client.resumeDetails = nil
  349. return
  350. }
  351. err := server.clients.Resume(client, oldClient)
  352. if err != nil {
  353. client.resumeDetails = nil
  354. client.Send(nil, server.name, "RESUME", "ERR", client.t("Cannot resume connection"))
  355. return
  356. }
  357. // this is a bit racey
  358. client.resumeDetails.ResumedAt = time.Now()
  359. client.nickTimer.Touch()
  360. // resume successful, proceed to copy client state (nickname, flags, etc.)
  361. // after this, the server thinks that `newClient` owns the nickname
  362. client.resumeDetails.OldClient = oldClient
  363. // transfer monitor stuff
  364. server.monitorManager.Resume(client, oldClient)
  365. // record the names, not the pointers, of the channels,
  366. // to avoid dumb annoying race conditions
  367. channels := oldClient.Channels()
  368. client.resumeDetails.Channels = make([]string, len(channels))
  369. for i, channel := range channels {
  370. client.resumeDetails.Channels[i] = channel.Name()
  371. }
  372. username := client.Username()
  373. hostname := client.Hostname()
  374. friends := make(ClientSet)
  375. oldestLostMessage := time.Now()
  376. // work out how much time, if any, is not covered by history buffers
  377. for _, channel := range channels {
  378. for _, member := range channel.Members() {
  379. friends.Add(member)
  380. lastDiscarded := channel.history.LastDiscarded()
  381. if lastDiscarded.Before(oldestLostMessage) {
  382. oldestLostMessage = lastDiscarded
  383. }
  384. }
  385. }
  386. personalHistory := oldClient.history.All()
  387. lastDiscarded := oldClient.history.LastDiscarded()
  388. if lastDiscarded.Before(oldestLostMessage) {
  389. oldestLostMessage = lastDiscarded
  390. }
  391. for _, item := range personalHistory {
  392. if item.Type == history.Privmsg || item.Type == history.Notice {
  393. sender := server.clients.Get(item.Nick)
  394. if sender != nil {
  395. friends.Add(sender)
  396. }
  397. }
  398. }
  399. gap := lastDiscarded.Sub(timestamp)
  400. client.resumeDetails.HistoryIncomplete = gap > 0
  401. gapSeconds := int(gap.Seconds()) + 1 // round up to avoid confusion
  402. // send quit/resume messages to friends
  403. for friend := range friends {
  404. if friend.capabilities.Has(caps.Resume) {
  405. if timestamp.IsZero() {
  406. friend.Send(nil, oldNickmask, "RESUMED", username, hostname)
  407. } else {
  408. friend.Send(nil, oldNickmask, "RESUMED", username, hostname, timestampString)
  409. }
  410. } else {
  411. if client.resumeDetails.HistoryIncomplete {
  412. friend.Send(nil, oldNickmask, "QUIT", fmt.Sprintf(friend.t("Client reconnected (up to %d seconds of history lost)"), gapSeconds))
  413. } else {
  414. friend.Send(nil, oldNickmask, "QUIT", fmt.Sprintf(friend.t("Client reconnected")))
  415. }
  416. }
  417. }
  418. if client.resumeDetails.HistoryIncomplete {
  419. client.Send(nil, "RESUME", "WARN", fmt.Sprintf(client.t("Resume may have lost up to %d seconds of history"), gapSeconds))
  420. }
  421. client.Send(nil, "RESUME", "SUCCESS", oldNick)
  422. // after we send the rest of the registration burst, we'll try rejoining channels
  423. }
  424. func (client *Client) tryResumeChannels() {
  425. details := client.resumeDetails
  426. if details == nil {
  427. return
  428. }
  429. channels := make([]*Channel, len(details.Channels))
  430. for _, name := range details.Channels {
  431. channel := client.server.channels.Get(name)
  432. if channel == nil {
  433. continue
  434. }
  435. channel.Resume(client, details.OldClient, details.Timestamp)
  436. channels = append(channels, channel)
  437. }
  438. // replay direct PRIVSMG history
  439. if !details.Timestamp.IsZero() {
  440. now := time.Now()
  441. nick := client.Nick()
  442. items, complete := client.history.Between(details.Timestamp, now)
  443. for _, item := range items {
  444. var command string
  445. switch item.Type {
  446. case history.Privmsg:
  447. command = "PRIVMSG"
  448. case history.Notice:
  449. command = "NOTICE"
  450. default:
  451. continue
  452. }
  453. client.sendSplitMsgFromClientInternal(true, item.Time, item.Msgid, item.Nick, item.AccountName, nil, command, nick, item.Message)
  454. }
  455. if !complete {
  456. client.Send(nil, "HistServ", "NOTICE", nick, client.t("Some additional message history may have been lost"))
  457. }
  458. }
  459. details.OldClient.destroy(true)
  460. }
  461. // copy applicable state from oldClient to client as part of a resume
  462. func (client *Client) copyResumeData(oldClient *Client) {
  463. oldClient.stateMutex.RLock()
  464. flags := oldClient.flags
  465. history := oldClient.history
  466. nick := oldClient.nick
  467. nickCasefolded := oldClient.nickCasefolded
  468. vhost := oldClient.vhost
  469. account := oldClient.account
  470. accountName := oldClient.accountName
  471. oldClient.stateMutex.RUnlock()
  472. // copy all flags, *except* TLS (in the case that the admins enabled
  473. // resume over plaintext)
  474. hasTLS := client.flags.HasMode(modes.TLS)
  475. temp := modes.NewModeSet()
  476. temp.Copy(flags)
  477. temp.SetMode(modes.TLS, hasTLS)
  478. client.flags.Copy(temp)
  479. client.stateMutex.Lock()
  480. defer client.stateMutex.Unlock()
  481. // reuse the old client's history buffer
  482. client.history = history
  483. // copy other data
  484. client.nick = nick
  485. client.nickCasefolded = nickCasefolded
  486. client.vhost = vhost
  487. client.account = account
  488. client.accountName = accountName
  489. client.updateNickMaskNoMutex()
  490. }
  491. // IdleTime returns how long this client's been idle.
  492. func (client *Client) IdleTime() time.Duration {
  493. client.stateMutex.RLock()
  494. defer client.stateMutex.RUnlock()
  495. return time.Since(client.atime)
  496. }
  497. // SignonTime returns this client's signon time as a unix timestamp.
  498. func (client *Client) SignonTime() int64 {
  499. return client.ctime.Unix()
  500. }
  501. // IdleSeconds returns the number of seconds this client's been idle.
  502. func (client *Client) IdleSeconds() uint64 {
  503. return uint64(client.IdleTime().Seconds())
  504. }
  505. // HasNick returns true if the client's nickname is set (used in registration).
  506. func (client *Client) HasNick() bool {
  507. client.stateMutex.RLock()
  508. defer client.stateMutex.RUnlock()
  509. return client.nick != "" && client.nick != "*"
  510. }
  511. // HasUsername returns true if the client's username is set (used in registration).
  512. func (client *Client) HasUsername() bool {
  513. client.stateMutex.RLock()
  514. defer client.stateMutex.RUnlock()
  515. return client.username != "" && client.username != "*"
  516. }
  517. func (client *Client) SetNames(username, realname string) error {
  518. _, err := CasefoldName(username)
  519. if err != nil {
  520. return errInvalidUsername
  521. }
  522. client.stateMutex.Lock()
  523. defer client.stateMutex.Unlock()
  524. if client.username == "" {
  525. client.username = "~" + username
  526. }
  527. if client.realname == "" {
  528. client.realname = realname
  529. }
  530. return nil
  531. }
  532. // HasRoleCapabs returns true if client has the given (role) capabilities.
  533. func (client *Client) HasRoleCapabs(capabs ...string) bool {
  534. oper := client.Oper()
  535. if oper == nil {
  536. return false
  537. }
  538. for _, capab := range capabs {
  539. if !oper.Class.Capabilities[capab] {
  540. return false
  541. }
  542. }
  543. return true
  544. }
  545. // ModeString returns the mode string for this client.
  546. func (client *Client) ModeString() (str string) {
  547. return "+" + client.flags.String()
  548. }
  549. // Friends refers to clients that share a channel with this client.
  550. func (client *Client) Friends(capabs ...caps.Capability) ClientSet {
  551. friends := make(ClientSet)
  552. // make sure that I have the right caps
  553. hasCaps := true
  554. for _, capab := range capabs {
  555. if !client.capabilities.Has(capab) {
  556. hasCaps = false
  557. break
  558. }
  559. }
  560. if hasCaps {
  561. friends.Add(client)
  562. }
  563. for _, channel := range client.Channels() {
  564. for _, member := range channel.Members() {
  565. // make sure they have all the required caps
  566. hasCaps = true
  567. for _, capab := range capabs {
  568. if !member.capabilities.Has(capab) {
  569. hasCaps = false
  570. break
  571. }
  572. }
  573. if hasCaps {
  574. friends.Add(member)
  575. }
  576. }
  577. }
  578. return friends
  579. }
  580. // XXX: CHGHOST requires prefix nickmask to have original hostname,
  581. // this is annoying to do correctly
  582. func (client *Client) sendChghost(oldNickMask string, vhost string) {
  583. username := client.Username()
  584. for fClient := range client.Friends(caps.ChgHost) {
  585. fClient.sendFromClientInternal(false, time.Time{}, "", oldNickMask, client.AccountName(), nil, "CHGHOST", username, vhost)
  586. }
  587. }
  588. // choose the correct vhost to display
  589. func (client *Client) getVHostNoMutex() string {
  590. // hostserv vhost OR operclass vhost OR nothing (i.e., normal rdns hostmask)
  591. if client.vhost != "" {
  592. return client.vhost
  593. } else if client.oper != nil {
  594. return client.oper.Vhost
  595. } else {
  596. return ""
  597. }
  598. }
  599. // SetVHost updates the client's hostserv-based vhost
  600. func (client *Client) SetVHost(vhost string) (updated bool) {
  601. client.stateMutex.Lock()
  602. defer client.stateMutex.Unlock()
  603. updated = (client.vhost != vhost)
  604. client.vhost = vhost
  605. if updated {
  606. client.updateNickMaskNoMutex()
  607. }
  608. return
  609. }
  610. // updateNick updates `nick` and `nickCasefolded`.
  611. func (client *Client) updateNick(nick string) {
  612. casefoldedName, err := CasefoldName(nick)
  613. if err != nil {
  614. client.server.logger.Error("internal", "nick couldn't be casefolded", nick, err.Error())
  615. return
  616. }
  617. client.stateMutex.Lock()
  618. client.nick = nick
  619. client.nickCasefolded = casefoldedName
  620. client.stateMutex.Unlock()
  621. }
  622. // updateNickMask updates the casefolded nickname and nickmask.
  623. func (client *Client) updateNickMask(nick string) {
  624. // on "", just regenerate the nickmask etc.
  625. // otherwise, update the actual nick
  626. if nick != "" {
  627. client.updateNick(nick)
  628. }
  629. client.stateMutex.Lock()
  630. defer client.stateMutex.Unlock()
  631. client.updateNickMaskNoMutex()
  632. }
  633. // updateNickMask updates the casefolded nickname and nickmask, not acquiring any mutexes.
  634. func (client *Client) updateNickMaskNoMutex() {
  635. client.hostname = client.getVHostNoMutex()
  636. if client.hostname == "" {
  637. client.hostname = client.rawHostname
  638. }
  639. nickMaskString := fmt.Sprintf("%s!%s@%s", client.nick, client.username, client.hostname)
  640. nickMaskCasefolded, err := Casefold(nickMaskString)
  641. if err != nil {
  642. client.server.logger.Error("internal", "nickmask couldn't be casefolded", nickMaskString, err.Error())
  643. return
  644. }
  645. client.nickMaskString = nickMaskString
  646. client.nickMaskCasefolded = nickMaskCasefolded
  647. }
  648. // AllNickmasks returns all the possible nickmasks for the client.
  649. func (client *Client) AllNickmasks() []string {
  650. var masks []string
  651. var mask string
  652. var err error
  653. client.stateMutex.RLock()
  654. nick := client.nick
  655. username := client.username
  656. rawHostname := client.rawHostname
  657. vhost := client.getVHostNoMutex()
  658. client.stateMutex.RUnlock()
  659. if len(vhost) > 0 {
  660. mask, err = Casefold(fmt.Sprintf("%s!%s@%s", nick, username, vhost))
  661. if err == nil {
  662. masks = append(masks, mask)
  663. }
  664. }
  665. mask, err = Casefold(fmt.Sprintf("%s!%s@%s", nick, username, rawHostname))
  666. if err == nil {
  667. masks = append(masks, mask)
  668. }
  669. mask2, err := Casefold(fmt.Sprintf("%s!%s@%s", nick, username, client.IPString()))
  670. if err == nil && mask2 != mask {
  671. masks = append(masks, mask2)
  672. }
  673. return masks
  674. }
  675. // LoggedIntoAccount returns true if this client is logged into an account.
  676. func (client *Client) LoggedIntoAccount() bool {
  677. return client.Account() != ""
  678. }
  679. // RplISupport outputs our ISUPPORT lines to the client. This is used on connection and in VERSION responses.
  680. func (client *Client) RplISupport(rb *ResponseBuffer) {
  681. translatedISupport := client.t("are supported by this server")
  682. nick := client.Nick()
  683. for _, cachedTokenLine := range client.server.ISupport().CachedReply {
  684. length := len(cachedTokenLine) + 2
  685. tokenline := make([]string, length)
  686. tokenline[0] = nick
  687. copy(tokenline[1:], cachedTokenLine)
  688. tokenline[length-1] = translatedISupport
  689. rb.Add(nil, client.server.name, RPL_ISUPPORT, tokenline...)
  690. }
  691. }
  692. // Quit sets the given quit message for the client and tells the client to quit out.
  693. func (client *Client) Quit(message string) {
  694. client.stateMutex.Lock()
  695. alreadyQuit := client.isQuitting
  696. if !alreadyQuit {
  697. client.isQuitting = true
  698. client.quitMessage = message
  699. }
  700. client.stateMutex.Unlock()
  701. if alreadyQuit {
  702. return
  703. }
  704. quitMsg := ircmsg.MakeMessage(nil, client.nickMaskString, "QUIT", message)
  705. quitLine, _ := quitMsg.Line()
  706. errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message)
  707. errorLine, _ := errorMsg.Line()
  708. client.socket.SetFinalData(quitLine + errorLine)
  709. }
  710. // destroy gets rid of a client, removes them from server lists etc.
  711. func (client *Client) destroy(beingResumed bool) {
  712. // allow destroy() to execute at most once
  713. client.stateMutex.Lock()
  714. isDestroyed := client.isDestroyed
  715. client.isDestroyed = true
  716. quitMessage := client.quitMessage
  717. nickMaskString := client.nickMaskString
  718. accountName := client.accountName
  719. client.stateMutex.Unlock()
  720. if isDestroyed {
  721. return
  722. }
  723. // see #235: deduplicating the list of PART recipients uses (comparatively speaking)
  724. // a lot of RAM, so limit concurrency to avoid thrashing
  725. client.server.semaphores.ClientDestroy.Acquire()
  726. defer client.server.semaphores.ClientDestroy.Release()
  727. if beingResumed {
  728. client.server.logger.Debug("quit", fmt.Sprintf("%s is being resumed", client.nick))
  729. } else {
  730. client.server.logger.Debug("quit", fmt.Sprintf("%s is no longer on the server", client.nick))
  731. }
  732. // send quit/error message to client if they haven't been sent already
  733. client.Quit("Connection closed")
  734. if !beingResumed {
  735. client.server.whoWas.Append(client.WhoWas())
  736. }
  737. // remove from connection limits
  738. ipaddr := client.IP()
  739. // this check shouldn't be required but eh
  740. if ipaddr != nil {
  741. client.server.connectionLimiter.RemoveClient(ipaddr)
  742. }
  743. // alert monitors
  744. client.server.monitorManager.AlertAbout(client, false)
  745. // clean up monitor state
  746. client.server.monitorManager.RemoveAll(client)
  747. // clean up channels
  748. friends := make(ClientSet)
  749. for _, channel := range client.Channels() {
  750. if !beingResumed {
  751. channel.Quit(client)
  752. channel.history.Add(history.Item{
  753. Type: history.Quit,
  754. Nick: nickMaskString,
  755. AccountName: accountName,
  756. Message: utils.MakeSplitMessage(quitMessage, true),
  757. })
  758. }
  759. for _, member := range channel.Members() {
  760. friends.Add(member)
  761. }
  762. }
  763. friends.Remove(client)
  764. // clean up server
  765. if !beingResumed {
  766. client.server.clients.Remove(client)
  767. }
  768. // clean up self
  769. client.idletimer.Stop()
  770. client.nickTimer.Stop()
  771. client.server.accounts.Logout(client)
  772. client.socket.Close()
  773. // send quit messages to friends
  774. if !beingResumed {
  775. if client.Registered() {
  776. client.server.stats.ChangeTotal(-1)
  777. }
  778. if client.HasMode(modes.Invisible) {
  779. client.server.stats.ChangeInvisible(-1)
  780. }
  781. if client.HasMode(modes.Operator) || client.HasMode(modes.LocalOperator) {
  782. client.server.stats.ChangeOperators(-1)
  783. }
  784. for friend := range friends {
  785. if quitMessage == "" {
  786. quitMessage = "Exited"
  787. }
  788. friend.Send(nil, client.nickMaskString, "QUIT", quitMessage)
  789. }
  790. }
  791. if !client.exitedSnomaskSent {
  792. if beingResumed {
  793. client.server.snomasks.Send(sno.LocalQuits, fmt.Sprintf(ircfmt.Unescape("%s$r is resuming their connection, old client has been destroyed"), client.nick))
  794. } else {
  795. client.server.snomasks.Send(sno.LocalQuits, fmt.Sprintf(ircfmt.Unescape("%s$r exited the network"), client.nick))
  796. }
  797. }
  798. }
  799. // SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
  800. // Adds account-tag to the line as well.
  801. func (client *Client) SendSplitMsgFromClient(msgid string, from *Client, tags Tags, command, target string, message utils.SplitMessage) {
  802. client.sendSplitMsgFromClientInternal(false, time.Time{}, msgid, from.NickMaskString(), from.AccountName(), tags, command, target, message)
  803. }
  804. func (client *Client) sendSplitMsgFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags Tags, command, target string, message utils.SplitMessage) {
  805. if client.capabilities.Has(caps.MaxLine) || message.Wrapped == nil {
  806. client.sendFromClientInternal(blocking, serverTime, msgid, nickmask, accountName, tags, command, target, message.Original)
  807. } else {
  808. for _, str := range message.Wrapped {
  809. client.sendFromClientInternal(blocking, serverTime, msgid, nickmask, accountName, tags, command, target, str)
  810. }
  811. }
  812. }
  813. // SendFromClient sends an IRC line coming from a specific client.
  814. // Adds account-tag to the line as well.
  815. func (client *Client) SendFromClient(msgid string, from *Client, tags Tags, command string, params ...string) error {
  816. return client.sendFromClientInternal(false, time.Time{}, msgid, from.NickMaskString(), from.AccountName(), tags, command, params...)
  817. }
  818. // helper to add a tag to `tags` (or create a new tag set if the current one is nil)
  819. func ensureTag(tags Tags, tagName, tagValue string) (result Tags) {
  820. if tags == nil {
  821. result = ircmsg.MakeTags(tagName, tagValue)
  822. } else {
  823. result = tags
  824. (*tags)[tagName] = ircmsg.MakeTagValue(tagValue)
  825. }
  826. return
  827. }
  828. // XXX this is a hack where we allow overriding the client's nickmask
  829. // this is to support CHGHOST, which requires that we send the *original* nickmask with the response
  830. func (client *Client) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags Tags, command string, params ...string) error {
  831. // attach account-tag
  832. if client.capabilities.Has(caps.AccountTag) && accountName != "*" {
  833. tags = ensureTag(tags, "account", accountName)
  834. }
  835. // attach message-id
  836. if len(msgid) > 0 && client.capabilities.Has(caps.MessageTags) {
  837. tags = ensureTag(tags, "draft/msgid", msgid)
  838. }
  839. return client.sendInternal(blocking, serverTime, tags, nickmask, command, params...)
  840. }
  841. var (
  842. // these are all the output commands that MUST have their last param be a trailing.
  843. // this is needed because dumb clients like to treat trailing params separately from the
  844. // other params in messages.
  845. commandsThatMustUseTrailing = map[string]bool{
  846. "PRIVMSG": true,
  847. "NOTICE": true,
  848. RPL_WHOISCHANNELS: true,
  849. RPL_USERHOST: true,
  850. }
  851. )
  852. // SendRawMessage sends a raw message to the client.
  853. func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) error {
  854. // use dumb hack to force the last param to be a trailing param if required
  855. var usedTrailingHack bool
  856. if commandsThatMustUseTrailing[strings.ToUpper(message.Command)] && len(message.Params) > 0 {
  857. lastParam := message.Params[len(message.Params)-1]
  858. // to force trailing, we ensure the final param contains a space
  859. if !strings.Contains(lastParam, " ") {
  860. message.Params[len(message.Params)-1] = lastParam + " "
  861. usedTrailingHack = true
  862. }
  863. }
  864. // assemble message
  865. maxlenTags, maxlenRest := client.maxlens()
  866. line, err := message.LineMaxLenBytes(maxlenTags, maxlenRest)
  867. if err != nil {
  868. logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack())
  869. client.server.logger.Error("internal", logline)
  870. message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
  871. line, _ := message.LineBytes()
  872. if blocking {
  873. client.socket.BlockingWrite(line)
  874. } else {
  875. client.socket.Write(line)
  876. }
  877. return err
  878. }
  879. // if we used the trailing hack, we need to strip the final space we appended earlier on
  880. if usedTrailingHack {
  881. copy(line[len(line)-3:], []byte{'\r', '\n'})
  882. line = line[:len(line)-1]
  883. }
  884. if client.server.logger.IsLoggingRawIO() {
  885. logline := string(line[:len(line)-2]) // strip "\r\n"
  886. client.server.logger.Debug("useroutput", client.nick, " ->", logline)
  887. }
  888. if blocking {
  889. return client.socket.BlockingWrite(line)
  890. } else {
  891. return client.socket.Write(line)
  892. }
  893. }
  894. func (client *Client) sendInternal(blocking bool, serverTime time.Time, tags Tags, prefix string, command string, params ...string) error {
  895. // attach server time
  896. if client.capabilities.Has(caps.ServerTime) {
  897. if serverTime.IsZero() {
  898. serverTime = time.Now()
  899. }
  900. tags = ensureTag(tags, "time", serverTime.UTC().Format(IRCv3TimestampFormat))
  901. }
  902. // send out the message
  903. message := ircmsg.MakeMessage(tags, prefix, command, params...)
  904. client.SendRawMessage(message, blocking)
  905. return nil
  906. }
  907. // Send sends an IRC line to the client.
  908. func (client *Client) Send(tags Tags, prefix string, command string, params ...string) error {
  909. return client.sendInternal(false, time.Time{}, tags, prefix, command, params...)
  910. }
  911. // Notice sends the client a notice from the server.
  912. func (client *Client) Notice(text string) {
  913. limit := 400
  914. if client.capabilities.Has(caps.MaxLine) {
  915. limit = client.server.Limits().LineLen.Rest - 110
  916. }
  917. lines := utils.WordWrap(text, limit)
  918. // force blank lines to be sent if we receive them
  919. if len(lines) == 0 {
  920. lines = []string{""}
  921. }
  922. for _, line := range lines {
  923. client.Send(nil, client.server.name, "NOTICE", client.nick, line)
  924. }
  925. }
  926. func (client *Client) addChannel(channel *Channel) {
  927. client.stateMutex.Lock()
  928. client.channels[channel] = true
  929. client.stateMutex.Unlock()
  930. }
  931. func (client *Client) removeChannel(channel *Channel) {
  932. client.stateMutex.Lock()
  933. delete(client.channels, channel)
  934. client.stateMutex.Unlock()
  935. }
  936. // Ensures the client has a cryptographically secure resume token, and returns
  937. // its value. An error is returned if a token was previously assigned.
  938. func (client *Client) generateResumeToken() (token string, err error) {
  939. newToken := utils.GenerateSecretToken()
  940. client.stateMutex.Lock()
  941. defer client.stateMutex.Unlock()
  942. if client.resumeToken == "" {
  943. client.resumeToken = newToken
  944. } else {
  945. err = errResumeTokenAlreadySet
  946. }
  947. return client.resumeToken, err
  948. }