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 32KB

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