Mirror of github.com/gissleh/irc
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.

1563 lines
36 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. package irc
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "crypto/rand"
  7. "crypto/tls"
  8. "encoding/base64"
  9. "encoding/binary"
  10. "encoding/hex"
  11. "errors"
  12. "fmt"
  13. mathRand "math/rand"
  14. "net"
  15. "sort"
  16. "strconv"
  17. "strings"
  18. "sync"
  19. "time"
  20. "github.com/gissleh/irc/ircutil"
  21. "github.com/gissleh/irc/isupport"
  22. "github.com/gissleh/irc/list"
  23. )
  24. var supportedCaps = []string{
  25. "server-time",
  26. "cap-notify",
  27. "multi-prefix",
  28. "userhost-in-names",
  29. "account-notify",
  30. "away-notify",
  31. "invite-notify",
  32. "extended-join",
  33. "chghost",
  34. "account-tag",
  35. "echo-message",
  36. "draft/languages",
  37. "sasl",
  38. }
  39. // ErrNoConnection is returned if you try to do something requiring a connection,
  40. // but there is none.
  41. var ErrNoConnection = errors.New("irc: no connection")
  42. // ErrTargetAlreadyAdded is returned by Client.AddTarget if that target has already been
  43. // added to the client.
  44. var ErrTargetAlreadyAdded = errors.New("irc: target already added")
  45. // ErrTargetConflict is returned by Client.AddTarget if there already exists a target
  46. // matching the name and kind.
  47. var ErrTargetConflict = errors.New("irc: target name and kind match existing target")
  48. // ErrTargetNotFound is returned by Client.RemoveTarget if the target is not part of
  49. // the client's target list
  50. var ErrTargetNotFound = errors.New("irc: target not found")
  51. // ErrTargetIsStatus is returned by Client.RemoveTarget if the target is the client's
  52. // status target
  53. var ErrTargetIsStatus = errors.New("irc: cannot remove status target")
  54. // ErrDestroyed is returned by Client.Connect if you try to connect a destroyed client.
  55. var ErrDestroyed = errors.New("irc: client destroyed")
  56. // A Client is an IRC client. You need to use New to construct it
  57. type Client struct {
  58. id string
  59. config Config
  60. mutex sync.RWMutex
  61. conn net.Conn
  62. ctx context.Context
  63. cancel context.CancelFunc
  64. events chan *Event
  65. sends chan string
  66. lastSend time.Time
  67. capEnabled map[string]bool
  68. capData map[string]string
  69. capsRequested []string
  70. nick string
  71. user string
  72. host string
  73. quit bool
  74. ready bool
  75. isupport isupport.ISupport
  76. values map[string]interface{}
  77. status *Status
  78. targets []Target
  79. handlers []Handler
  80. }
  81. // New creates a new client. The context can be context.Background if you want manually to
  82. // tear down clients upon quitting.
  83. func New(ctx context.Context, config Config) *Client {
  84. client := &Client{
  85. id: generateClientID("C"),
  86. values: make(map[string]interface{}),
  87. events: make(chan *Event, 64),
  88. sends: make(chan string, 64),
  89. capEnabled: make(map[string]bool),
  90. capData: make(map[string]string),
  91. config: config.WithDefaults(),
  92. status: &Status{id: generateClientID("T")},
  93. }
  94. client.ctx, client.cancel = context.WithCancel(ctx)
  95. _ = client.AddTarget(client.status)
  96. go client.handleEventLoop()
  97. go client.handleSendLoop()
  98. client.EmitNonBlocking(NewEvent("client", "create"))
  99. return client
  100. }
  101. // Context gets the client's context. It's cancelled if the parent context used
  102. // in New is, or Destroy is called.
  103. func (client *Client) Context() context.Context {
  104. return client.ctx
  105. }
  106. // ID gets the unique identifier for the client, which could be used in data structures
  107. func (client *Client) ID() string {
  108. client.mutex.RLock()
  109. defer client.mutex.RUnlock()
  110. return client.id
  111. }
  112. // Nick gets the nick of the client
  113. func (client *Client) Nick() string {
  114. client.mutex.RLock()
  115. defer client.mutex.RUnlock()
  116. return client.nick
  117. }
  118. // User gets the user/ident of the client
  119. func (client *Client) User() string {
  120. client.mutex.RLock()
  121. defer client.mutex.RUnlock()
  122. return client.user
  123. }
  124. // Host gets the hostname of the client
  125. func (client *Client) Host() string {
  126. client.mutex.RLock()
  127. defer client.mutex.RUnlock()
  128. return client.host
  129. }
  130. // ISupport gets the client's ISupport. This is mutable, and changes to it
  131. // *will* affect the client.
  132. func (client *Client) ISupport() *isupport.ISupport {
  133. return &client.isupport
  134. }
  135. // CapData returns if there was any additional CAP data for the given capability.
  136. func (client *Client) CapData(cap string) string {
  137. client.mutex.RLock()
  138. defer client.mutex.RUnlock()
  139. return client.capData[cap]
  140. }
  141. // CapEnabled returns whether an IRCv3 capability is enabled.
  142. func (client *Client) CapEnabled(cap string) bool {
  143. client.mutex.RLock()
  144. defer client.mutex.RUnlock()
  145. return client.capEnabled[cap]
  146. }
  147. // Ready returns true if the client is marked as ready, which means that it has received the MOTD.
  148. func (client *Client) Ready() bool {
  149. client.mutex.RLock()
  150. defer client.mutex.RUnlock()
  151. return client.ready
  152. }
  153. // HasQuit returns true if the client had manually quit. It should be checked before
  154. // performing any reconnection logic.
  155. func (client *Client) HasQuit() bool {
  156. client.mutex.RLock()
  157. defer client.mutex.RUnlock()
  158. return client.quit
  159. }
  160. func (client *Client) State() ClientState {
  161. client.mutex.RLock()
  162. state := ClientState{
  163. Nick: client.nick,
  164. User: client.user,
  165. Host: client.host,
  166. Connected: client.conn != nil,
  167. Ready: client.ready,
  168. Quit: client.quit,
  169. ISupport: client.isupport.State(),
  170. Caps: make([]string, 0, len(client.capEnabled)),
  171. Targets: make([]ClientStateTarget, 0, len(client.targets)),
  172. }
  173. for key, enabled := range client.capEnabled {
  174. if enabled {
  175. state.Caps = append(state.Caps, key)
  176. }
  177. }
  178. sort.Strings(state.Caps)
  179. for _, target := range client.targets {
  180. tstate := target.State()
  181. tstate.ID = target.ID()
  182. state.Targets = append(state.Targets, tstate)
  183. }
  184. client.mutex.RUnlock()
  185. return state
  186. }
  187. // Connect connects to the server by addr.
  188. func (client *Client) Connect(addr string, ssl bool) (err error) {
  189. var conn net.Conn
  190. if client.Connected() {
  191. _ = client.Disconnect(false)
  192. }
  193. client.isupport.Reset()
  194. client.mutex.Lock()
  195. client.quit = false
  196. client.mutex.Unlock()
  197. client.EmitNonBlocking(NewEvent("client", "connecting"))
  198. if ssl {
  199. conn, err = tls.Dial("tcp", addr, &tls.Config{
  200. InsecureSkipVerify: client.config.SkipSSLVerification,
  201. })
  202. if err != nil {
  203. if !client.Destroyed() {
  204. client.EmitNonBlocking(NewErrorEvent("connect", "TLS connect failed: "+err.Error(), "connect_failed_tls", err))
  205. }
  206. return err
  207. }
  208. } else {
  209. conn, err = net.Dial("tcp", addr)
  210. if err != nil {
  211. if !client.Destroyed() {
  212. client.EmitNonBlocking(NewErrorEvent("connect", "Connect failed: "+err.Error(), "connect_failed", err))
  213. }
  214. return err
  215. }
  216. }
  217. if client.Destroyed() {
  218. _ = conn.Close()
  219. return ErrDestroyed
  220. }
  221. client.EmitNonBlocking(NewEvent("client", "connect"))
  222. go func() {
  223. reader := bufio.NewReader(conn)
  224. replacer := strings.NewReplacer("\r", "", "\n", "")
  225. for {
  226. line, err := reader.ReadString('\n')
  227. if err != nil {
  228. client.EmitNonBlocking(NewErrorEvent("read", "Read failed: "+err.Error(), "read_failed", err))
  229. break
  230. }
  231. line = replacer.Replace(line)
  232. event, err := ParsePacket(line)
  233. if err != nil {
  234. client.mutex.RLock()
  235. hasQuit := client.quit
  236. client.mutex.RUnlock()
  237. if !hasQuit {
  238. client.EmitNonBlocking(NewErrorEvent("parse", "Parse failed: "+err.Error(), "parse_failed", err))
  239. }
  240. continue
  241. }
  242. client.EmitNonBlocking(event)
  243. }
  244. _ = client.conn.Close()
  245. client.mutex.Lock()
  246. client.conn = nil
  247. client.ready = false
  248. client.mutex.Unlock()
  249. client.EmitNonBlocking(NewEvent("client", "disconnect"))
  250. }()
  251. client.mutex.Lock()
  252. client.conn = conn
  253. client.mutex.Unlock()
  254. return nil
  255. }
  256. // Disconnect disconnects from the server. It will either return the
  257. // close error, or ErrNoConnection if there is no connection. If
  258. // markAsQuit is specified, HasQuit will return true until the next
  259. // connections.
  260. func (client *Client) Disconnect(markAsQuit bool) error {
  261. client.mutex.Lock()
  262. defer client.mutex.Unlock()
  263. if markAsQuit {
  264. client.quit = true
  265. }
  266. if client.conn == nil {
  267. return ErrNoConnection
  268. }
  269. return client.conn.Close()
  270. }
  271. // Connected returns true if the client has a connection
  272. func (client *Client) Connected() bool {
  273. client.mutex.RLock()
  274. defer client.mutex.RUnlock()
  275. return client.conn != nil
  276. }
  277. // Send sends a line to the server. A line-feed will be automatically added if one
  278. // is not provided. If this isn't part of early registration, SendQueued might save
  279. // you from a potential flood kick.
  280. func (client *Client) Send(line string) error {
  281. client.mutex.RLock()
  282. conn := client.conn
  283. client.mutex.RUnlock()
  284. if conn == nil {
  285. return ErrNoConnection
  286. }
  287. if !strings.HasSuffix(line, "\n") {
  288. line += "\r\n"
  289. }
  290. _ = conn.SetWriteDeadline(time.Now().Add(time.Second * 30))
  291. _, err := conn.Write([]byte(line))
  292. if err != nil {
  293. client.EmitNonBlocking(NewErrorEvent("write", "Write failed:"+err.Error(), "connect_failed", err))
  294. _ = client.Disconnect(false)
  295. }
  296. return err
  297. }
  298. // Sendf is Send with a fmt.Sprintf. If this isn't part of early registration,
  299. // SendQueuedf might save you from a potential flood kick.
  300. func (client *Client) Sendf(format string, a ...interface{}) error {
  301. return client.Send(fmt.Sprintf(format, a...))
  302. }
  303. // SendQueued appends a message to a queue that will only send 2 messages
  304. // per second to avoid flooding. If the queue is ull, a goroutine will be
  305. // spawned to queue it, so this function will always return immediately.
  306. // Order may not be guaranteed, however, but if you're sending 64 messages
  307. // at once that may not be your greatest concern.
  308. //
  309. // Failed sends will be discarded quietly to avoid a backup from being
  310. // thrown on a new connection.
  311. func (client *Client) SendQueued(line string) {
  312. select {
  313. case client.sends <- line:
  314. default:
  315. go func() { client.sends <- line }()
  316. }
  317. }
  318. // SendQueuedf is SendQueued with a fmt.Sprintf
  319. func (client *Client) SendQueuedf(format string, a ...interface{}) {
  320. client.SendQueued(fmt.Sprintf(format, a...))
  321. }
  322. // SendCTCP sends a queued message with the following CTCP verb and text. If reply is true,
  323. // it will use a NOTICE instead of PRIVMSG.
  324. func (client *Client) SendCTCP(verb, targetName string, reply bool, text string) {
  325. ircVerb := "PRIVMSG"
  326. if reply {
  327. ircVerb = "NOTICE"
  328. }
  329. client.SendQueuedf("%s %s :\x01%s %s\x01", ircVerb, targetName, verb, text)
  330. }
  331. // SendCTCPf is SendCTCP with a fmt.Sprintf
  332. func (client *Client) SendCTCPf(verb, targetName string, reply bool, format string, a ...interface{}) {
  333. client.SendCTCP(verb, targetName, reply, fmt.Sprintf(format, a...))
  334. }
  335. // Say sends a PRIVMSG with the target name and text, cutting the message if it gets too long.
  336. func (client *Client) Say(targetName string, text string) {
  337. overhead := client.PrivmsgOverhead(targetName, false)
  338. cuts := ircutil.CutMessage(text, overhead)
  339. for _, cut := range cuts {
  340. client.SendQueuedf("PRIVMSG %s :%s", targetName, cut)
  341. }
  342. }
  343. // Sayf is Say with a fmt.Sprintf.
  344. func (client *Client) Sayf(targetName string, format string, a ...interface{}) {
  345. client.Say(targetName, fmt.Sprintf(format, a...))
  346. }
  347. // Describe sends a CTCP ACTION with the target name and text, cutting the message if it gets too long.
  348. func (client *Client) Describe(targetName string, text string) {
  349. overhead := client.PrivmsgOverhead(targetName, true)
  350. cuts := ircutil.CutMessage(text, overhead)
  351. for _, cut := range cuts {
  352. client.SendQueuedf("PRIVMSG %s :\x01ACTION %s\x01", targetName, cut)
  353. }
  354. }
  355. // Describef is Describe with a fmt.Sprintf.
  356. func (client *Client) Describef(targetName string, format string, a ...interface{}) {
  357. client.Describe(targetName, fmt.Sprintf(format, a...))
  358. }
  359. // Emit sends an event through the client's event, and it will return immediately
  360. // unless the internal channel is filled up. The returned context can be used to
  361. // wait for the event, or the client's destruction.
  362. func (client *Client) Emit(event Event) context.Context {
  363. event.ctx, event.cancel = context.WithCancel(client.ctx)
  364. if client.Destroyed() {
  365. event.cancel()
  366. return event.ctx
  367. }
  368. client.events <- &event
  369. return event.ctx
  370. }
  371. // EmitNonBlocking is just like emitInGlobalHandlers, but it will spin off a goroutine if the channel is full.
  372. // This lets it be called from other handlers without ever blocking. See Emit for what the
  373. // returned context is for.
  374. func (client *Client) EmitNonBlocking(event Event) context.Context {
  375. event.ctx, event.cancel = context.WithCancel(client.ctx)
  376. if client.Destroyed() {
  377. event.cancel()
  378. return event.ctx
  379. }
  380. select {
  381. case client.events <- &event:
  382. default:
  383. go func() { client.events <- &event }()
  384. }
  385. return event.ctx
  386. }
  387. // EmitSync emits an event and waits for either its context to complete or the one
  388. // passed to it (e.g. a request's context). It's a shorthand for Emit with its
  389. // return value used in a `select` along with a passed context.
  390. func (client *Client) EmitSync(ctx context.Context, event Event) (err error) {
  391. eventCtx := client.Emit(event)
  392. select {
  393. case <-eventCtx.Done():
  394. {
  395. if err := eventCtx.Err(); err != context.Canceled {
  396. return err
  397. }
  398. return nil
  399. }
  400. case <-ctx.Done():
  401. {
  402. return ctx.Err()
  403. }
  404. }
  405. }
  406. // EmitInput emits an input event parsed from the line.
  407. func (client *Client) EmitInput(line string, target Target) context.Context {
  408. event := ParseInput(line)
  409. client.mutex.RLock()
  410. if target != nil && client.TargetByID(target.ID()) == nil {
  411. client.EmitNonBlocking(NewErrorEvent("target", "Target does not exist.", "target_do_not_exist", nil))
  412. ctx, cancel := context.WithCancel(context.Background())
  413. cancel()
  414. return ctx
  415. }
  416. client.mutex.RUnlock()
  417. if target != nil {
  418. client.mutex.RLock()
  419. event.targets = append(event.targets, target)
  420. client.mutex.RUnlock()
  421. } else {
  422. client.mutex.RLock()
  423. event.targets = append(event.targets, client.status)
  424. client.mutex.RUnlock()
  425. }
  426. return client.Emit(event)
  427. }
  428. // Value gets a client value.
  429. func (client *Client) Value(key string) interface{} {
  430. client.mutex.RLock()
  431. defer client.mutex.RUnlock()
  432. return client.values[key]
  433. }
  434. // SetValue sets a client value.
  435. func (client *Client) SetValue(key string, value interface{}) {
  436. client.mutex.Lock()
  437. client.values[key] = value
  438. client.mutex.Unlock()
  439. }
  440. // Destroy destroys the client, which will lead to a disconnect. Cancelling the
  441. // parent context will do the same.
  442. func (client *Client) Destroy() {
  443. _ = client.Disconnect(false)
  444. client.cancel()
  445. close(client.sends)
  446. client.Emit(NewEvent("client", "destroy"))
  447. close(client.events)
  448. }
  449. // Destroyed returns true if the client has been destroyed, either by
  450. // Destroy or the parent context.
  451. func (client *Client) Destroyed() bool {
  452. select {
  453. case <-client.ctx.Done():
  454. return true
  455. default:
  456. return false
  457. }
  458. }
  459. // PrivmsgOverhead returns the overhead on a privmsg to the target. If `action` is true,
  460. // it will also count the extra overhead of a CTCP ACTION.
  461. func (client *Client) PrivmsgOverhead(targetName string, action bool) int {
  462. client.mutex.RLock()
  463. defer client.mutex.RUnlock()
  464. // Return a really safe estimate if user or host is missing.
  465. if client.user == "" || client.host == "" {
  466. return 200
  467. }
  468. return ircutil.MessageOverhead(client.nick, client.user, client.host, targetName, action)
  469. }
  470. // Join joins one or more channels without a key.
  471. func (client *Client) Join(channels ...string) {
  472. client.SendQueuedf("JOIN %s", strings.Join(channels, ","))
  473. }
  474. // Part parts one or more channels.
  475. func (client *Client) Part(channels ...string) {
  476. client.SendQueuedf("PART %s", strings.Join(channels, ","))
  477. }
  478. // Quit sends a quit message and marks the client as having quit, which
  479. // means HasQuit() will return true.
  480. func (client *Client) Quit(reason string) {
  481. client.mutex.Lock()
  482. client.quit = true
  483. client.mutex.Unlock()
  484. client.SendQueuedf("QUIT :%s", reason)
  485. }
  486. // Target gets a target by kind and name
  487. func (client *Client) Target(kind string, name string) Target {
  488. client.mutex.RLock()
  489. defer client.mutex.RUnlock()
  490. for _, target := range client.targets {
  491. if target.Kind() == kind && strings.EqualFold(name, target.Name()) {
  492. return target
  493. }
  494. }
  495. return nil
  496. }
  497. // TargetByID gets a target by kind and name
  498. func (client *Client) TargetByID(id string) Target {
  499. client.mutex.RLock()
  500. defer client.mutex.RUnlock()
  501. for _, target := range client.targets {
  502. if target.ID() == id {
  503. return target
  504. }
  505. }
  506. return nil
  507. }
  508. // Targets gets all targets of the given kinds.
  509. func (client *Client) Targets(kinds ...string) []Target {
  510. if len(kinds) == 0 {
  511. client.mutex.Lock()
  512. targets := make([]Target, len(client.targets))
  513. copy(targets, client.targets)
  514. client.mutex.Unlock()
  515. return targets
  516. }
  517. client.mutex.Lock()
  518. targets := make([]Target, 0, len(client.targets))
  519. for _, target := range client.targets {
  520. for _, kind := range kinds {
  521. if target.Kind() == kind {
  522. targets = append(targets, target)
  523. break
  524. }
  525. }
  526. }
  527. client.mutex.Unlock()
  528. return targets
  529. }
  530. // Status gets the client's status target.
  531. func (client *Client) Status() *Status {
  532. return client.status
  533. }
  534. // Channel is a shorthand for getting a channel target and type asserting it.
  535. func (client *Client) Channel(name string) *Channel {
  536. target := client.Target("channel", name)
  537. if target == nil {
  538. return nil
  539. }
  540. return target.(*Channel)
  541. }
  542. // Channels gets all channel targets the client has.
  543. func (client *Client) Channels() []*Channel {
  544. targets := client.Targets("channel")
  545. channels := make([]*Channel, len(targets))
  546. for i := range targets {
  547. channels[i] = targets[i].(*Channel)
  548. }
  549. return channels
  550. }
  551. // Query is a shorthand for getting a query target and type asserting it.
  552. func (client *Client) Query(name string) *Query {
  553. target := client.Target("query", name)
  554. if target == nil {
  555. return nil
  556. }
  557. return target.(*Query)
  558. }
  559. // AddTarget adds a target to the client, generating a unique ID for it.
  560. func (client *Client) AddTarget(target Target) (err error) {
  561. client.mutex.Lock()
  562. defer client.mutex.Unlock()
  563. for i := range client.targets {
  564. if target == client.targets[i] {
  565. err = ErrTargetAlreadyAdded
  566. return
  567. } else if target.Kind() == client.targets[i].Kind() && target.Name() == client.targets[i].Name() {
  568. err = ErrTargetConflict
  569. return
  570. }
  571. }
  572. client.targets = append(client.targets, target)
  573. event := NewEvent("hook", "add_target")
  574. event.Args = []string{target.ID(), target.Kind(), target.Name()}
  575. event.targets = []Target{target}
  576. client.EmitNonBlocking(event)
  577. return
  578. }
  579. // RemoveTarget removes a target to the client
  580. func (client *Client) RemoveTarget(target Target) (id string, err error) {
  581. if target == client.status {
  582. return "", ErrTargetIsStatus
  583. }
  584. client.mutex.Lock()
  585. defer client.mutex.Unlock()
  586. for i := range client.targets {
  587. if target == client.targets[i] {
  588. id = target.ID()
  589. event := NewEvent("hook", "remove_target")
  590. event.Args = []string{target.ID(), target.Kind(), target.Name()}
  591. client.EmitNonBlocking(event)
  592. client.targets[i] = client.targets[len(client.targets)-1]
  593. client.targets = client.targets[:len(client.targets)-1]
  594. // Ensure the channel has been parted
  595. if channel, ok := target.(*Channel); ok && !channel.parted {
  596. client.SendQueuedf("PART %s", channel.Name())
  597. }
  598. return
  599. }
  600. }
  601. err = ErrTargetNotFound
  602. return
  603. }
  604. // FindUser checks each channel to find user info about a user.
  605. func (client *Client) FindUser(nick string) (u list.User, ok bool) {
  606. client.mutex.RLock()
  607. defer client.mutex.RUnlock()
  608. for _, target := range client.targets {
  609. channel, ok := target.(*Channel)
  610. if !ok {
  611. continue
  612. }
  613. user, ok := channel.UserList().User(nick)
  614. if !ok {
  615. continue
  616. }
  617. return user, true
  618. }
  619. return list.User{}, false
  620. }
  621. // AddHandler adds a handler. This is thread safe, unlike adding global handlers.
  622. func (client *Client) AddHandler(handler Handler) {
  623. client.mutex.Lock()
  624. client.handlers = append(client.handlers[:0], client.handlers...)
  625. client.handlers = append(client.handlers, handler)
  626. client.mutex.Unlock()
  627. }
  628. func (client *Client) handleEventLoop() {
  629. ticker := time.NewTicker(time.Second * 30)
  630. for {
  631. select {
  632. case event, ok := <-client.events:
  633. {
  634. if !ok {
  635. goto end
  636. }
  637. client.handleEvent(event)
  638. // Turn an unhandled input into a raw command.
  639. if event.kind == "input" && !event.preventedDefault {
  640. client.SendQueued(strings.ToUpper(event.verb) + " " + event.Text)
  641. }
  642. event.cancel()
  643. }
  644. case <-ticker.C:
  645. {
  646. event := NewEvent("client", "tick")
  647. event.ctx, event.cancel = context.WithCancel(client.ctx)
  648. client.handleEvent(&event)
  649. event.cancel()
  650. }
  651. case <-client.ctx.Done():
  652. {
  653. goto end
  654. }
  655. }
  656. }
  657. end:
  658. ticker.Stop()
  659. _ = client.Disconnect(false)
  660. }
  661. func (client *Client) handleSendLoop() {
  662. lastRefresh := time.Time{}
  663. queue := client.config.SendRate
  664. for line := range client.sends {
  665. now := time.Now()
  666. deltaTime := now.Sub(lastRefresh)
  667. if deltaTime < time.Second {
  668. queue--
  669. if queue <= 0 {
  670. time.Sleep(time.Second - deltaTime)
  671. lastRefresh = now
  672. queue = client.config.SendRate - 1
  673. }
  674. } else {
  675. lastRefresh = now
  676. queue = client.config.SendRate - 1
  677. }
  678. _ = client.Send(line)
  679. }
  680. }
  681. // handleEvent is always first and gets to break a few rules.
  682. func (client *Client) handleEvent(event *Event) {
  683. sentCapEnd := false
  684. // Only use IRCv3 `server-time` to overwrite when requested. Frontends/dependents can still
  685. // get this information.
  686. if client.config.UseServerTime {
  687. if timeTag, ok := event.Tags["time"]; ok {
  688. serverTime, err := time.Parse(time.RFC3339Nano, timeTag)
  689. if err == nil && serverTime.Year() > 2000 {
  690. event.Time = serverTime
  691. }
  692. }
  693. }
  694. // For events that were created with targets, handle them now there now.
  695. for _, target := range event.targets {
  696. target.Handle(event, client)
  697. }
  698. switch event.name {
  699. // Ping Pong
  700. case "hook.tick":
  701. {
  702. client.mutex.RLock()
  703. lastSend := time.Since(client.lastSend)
  704. client.mutex.RUnlock()
  705. if lastSend > time.Second*120 {
  706. _ = client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
  707. }
  708. }
  709. case "packet.ping":
  710. {
  711. message := "PONG"
  712. for _, arg := range event.Args {
  713. message += " " + arg
  714. }
  715. if event.Text != "" {
  716. message += " :" + event.Text
  717. }
  718. _ = client.Send(message)
  719. }
  720. // Client Registration
  721. case "client.connect":
  722. {
  723. // Clear enabled caps and initiate negotiation.
  724. client.mutex.Lock()
  725. for key := range client.capEnabled {
  726. delete(client.capEnabled, key)
  727. }
  728. client.mutex.Unlock()
  729. _ = client.Send("CAP LS 302")
  730. // Send server password if configured.
  731. if client.config.Password != "" {
  732. _ = client.Sendf("PASS :%s", client.config.Password)
  733. }
  734. // Reuse nick or get from config
  735. nick := client.config.Nick
  736. client.mutex.RLock()
  737. if client.nick != "" {
  738. nick = client.nick
  739. }
  740. client.mutex.RUnlock()
  741. // Clear connection-specific data
  742. client.mutex.Lock()
  743. client.nick = ""
  744. client.user = ""
  745. client.host = ""
  746. client.capsRequested = client.capsRequested[:0]
  747. for key := range client.capData {
  748. delete(client.capData, key)
  749. }
  750. for key := range client.capEnabled {
  751. delete(client.capEnabled, key)
  752. }
  753. client.mutex.Unlock()
  754. // Start registration.
  755. _ = client.Sendf("NICK %s", nick)
  756. _ = client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
  757. }
  758. // Welcome message
  759. case "packet.001":
  760. {
  761. client.mutex.Lock()
  762. client.nick = event.Args[0]
  763. client.mutex.Unlock()
  764. // Send a WHO right away to gather enough client information for precise message cutting.
  765. _ = client.Sendf("WHO %s", event.Args[0])
  766. }
  767. // Nick rotation
  768. case "packet.431", "packet.432", "packet.433", "packet.436":
  769. {
  770. lockNickChange, _ := client.Value("internal.lockNickChange").(bool)
  771. // Ignore if client is registered
  772. if client.Nick() != "" {
  773. break
  774. }
  775. nick := event.Args[1]
  776. newNick := ""
  777. // "AltN" -> "AltN+1", ...
  778. prev := client.config.Nick
  779. for _, alt := range client.config.Alternatives {
  780. if nick == prev {
  781. newNick = alt
  782. break
  783. }
  784. prev = alt
  785. }
  786. if newNick == "" {
  787. // "LastAlt" -> "Nick23962"
  788. newNick = fmt.Sprintf("%s%05d", client.config.Nick, mathRand.Int31n(99999))
  789. }
  790. if lockNickChange {
  791. client.SetValue("internal.primedNickChange", newNick)
  792. } else {
  793. _ = client.Sendf("NICK %s", newNick)
  794. }
  795. }
  796. case "packet.nick":
  797. {
  798. client.handleInTargets(event.Nick, event)
  799. if event.Nick == client.nick {
  800. client.SetValue("nick", event.Arg(0))
  801. }
  802. }
  803. // ISupport
  804. case "packet.005":
  805. {
  806. for _, token := range event.Args[1:] {
  807. kvpair := strings.Split(token, "=")
  808. if len(kvpair) == 2 {
  809. client.isupport.Set(kvpair[0], kvpair[1])
  810. } else {
  811. client.isupport.Set(kvpair[0], "")
  812. }
  813. }
  814. }
  815. // Capability negotiation
  816. case "packet.cap":
  817. {
  818. capCommand := event.Args[1]
  819. capTokens := strings.Split(event.Text, " ")
  820. switch capCommand {
  821. case "LS":
  822. {
  823. client.SetValue("internal.lockNickChange", true)
  824. for _, token := range capTokens {
  825. split := strings.SplitN(token, "=", 2)
  826. key := split[0]
  827. if len(key) == 0 {
  828. continue
  829. }
  830. if len(split) == 2 {
  831. client.capData[key] = split[1]
  832. }
  833. for i := range supportedCaps {
  834. if supportedCaps[i] == key {
  835. client.mutex.Lock()
  836. client.capsRequested = append(client.capsRequested, key)
  837. client.mutex.Unlock()
  838. break
  839. }
  840. }
  841. }
  842. if len(event.Args) < 3 || event.Args[2] != "*" {
  843. client.mutex.RLock()
  844. requestedCount := len(client.capsRequested)
  845. client.mutex.RUnlock()
  846. if requestedCount > 0 {
  847. client.mutex.RLock()
  848. requestedCaps := strings.Join(client.capsRequested, " ")
  849. client.mutex.RUnlock()
  850. _ = client.Send("CAP REQ :" + requestedCaps)
  851. } else {
  852. sentCapEnd = true
  853. _ = client.Send("CAP END")
  854. }
  855. }
  856. }
  857. case "ACK":
  858. {
  859. for _, token := range capTokens {
  860. client.mutex.Lock()
  861. if !client.capEnabled[token] {
  862. client.capEnabled[token] = true
  863. }
  864. client.mutex.Unlock()
  865. // Special cases for supported tokens
  866. switch token {
  867. case "sasl":
  868. {
  869. if client.config.SASL == nil {
  870. break
  871. }
  872. mechanisms := strings.Split(client.capData[token], ",")
  873. selectedMechanism := ""
  874. if len(mechanisms) == 0 || mechanisms[0] == "" {
  875. selectedMechanism = "PLAIN"
  876. }
  877. for _, mechanism := range mechanisms {
  878. if mechanism == "PLAIN" && selectedMechanism == "" {
  879. selectedMechanism = "PLAIN"
  880. }
  881. }
  882. // TODO: Add better mechanisms
  883. if selectedMechanism != "" {
  884. _ = client.Sendf("AUTHENTICATE %s", selectedMechanism)
  885. client.SetValue("sasl.usingMethod", "PLAIN")
  886. }
  887. }
  888. case "draft/languages":
  889. {
  890. if len(client.config.Languages) == 0 {
  891. break
  892. }
  893. // draft/languages=15,en,~bs,~de,~el,~en-AU,~es,~fi,~fr-FR,~it,~no,~pl,~pt-BR,~ro,~tr-TR,~zh-CN
  894. langData := strings.Split(client.capData[token], ",")
  895. if len(langData) < 0 {
  896. break
  897. }
  898. maxCount, err := strconv.Atoi(langData[0])
  899. if err != nil {
  900. break
  901. }
  902. languages := make([]string, 0, maxCount)
  903. LanguageLoop:
  904. for _, lang := range client.config.Languages {
  905. for _, lang2 := range langData[1:] {
  906. if strings.HasPrefix(lang2, "~") {
  907. lang2 = lang2[1:]
  908. }
  909. if strings.EqualFold(lang, lang2) {
  910. languages = append(languages, lang)
  911. if len(languages) >= maxCount {
  912. break LanguageLoop
  913. }
  914. }
  915. }
  916. }
  917. if len(languages) > 0 {
  918. _ = client.Send("LANGUAGE " + strings.Join(languages, " "))
  919. }
  920. }
  921. }
  922. }
  923. if !client.Ready() {
  924. sentCapEnd = true
  925. _ = client.Send("CAP END")
  926. }
  927. }
  928. case "NAK":
  929. {
  930. // Remove offenders
  931. for _, token := range capTokens {
  932. client.mutex.Lock()
  933. for i := range client.capsRequested {
  934. if token == client.capsRequested[i] {
  935. client.capsRequested = append(client.capsRequested[:i], client.capsRequested[i+1:]...)
  936. break
  937. }
  938. }
  939. client.mutex.Unlock()
  940. }
  941. client.mutex.RLock()
  942. requestedCaps := strings.Join(client.capsRequested, " ")
  943. client.mutex.RUnlock()
  944. _ = client.Send("CAP REQ :" + requestedCaps)
  945. }
  946. case "NEW":
  947. {
  948. requests := make([]string, 0, len(capTokens))
  949. for _, token := range capTokens {
  950. for i := range supportedCaps {
  951. if supportedCaps[i] == token {
  952. requests = append(requests, token)
  953. }
  954. }
  955. }
  956. if len(requests) > 0 {
  957. _ = client.Send("CAP REQ :" + strings.Join(requests, " "))
  958. }
  959. }
  960. case "DEL":
  961. {
  962. for _, token := range capTokens {
  963. client.mutex.Lock()
  964. if client.capEnabled[token] {
  965. client.capEnabled[token] = false
  966. }
  967. client.mutex.Unlock()
  968. }
  969. }
  970. }
  971. }
  972. // SASL
  973. case "packet.authenticate":
  974. {
  975. if event.Arg(0) != "+" {
  976. break
  977. }
  978. method, ok := client.Value("sasl.usingMethod").(string)
  979. if !ok {
  980. break
  981. }
  982. switch method {
  983. case "PLAIN":
  984. {
  985. parts := [][]byte{
  986. []byte(client.config.SASL.AuthenticationIdentity),
  987. []byte(client.config.SASL.AuthorizationIdentity),
  988. []byte(client.config.SASL.Password),
  989. }
  990. plainString := base64.StdEncoding.EncodeToString(bytes.Join(parts, []byte{0x00}))
  991. _ = client.Sendf("AUTHENTICATE %s", plainString)
  992. }
  993. }
  994. }
  995. case "packet.904": // Auth failed
  996. {
  997. // Cancel authentication.
  998. _ = client.Sendf("AUTHENTICATE *")
  999. client.SetValue("sasl.usingMethod", (interface{})(nil))
  1000. }
  1001. case "packet.903", "packet.906": // Auth ended
  1002. {
  1003. // A bit dirty, but it'll get the nick rotation started again.
  1004. if client.Nick() == "" {
  1005. _ = client.Sendf("NICK %s", client.config.Nick)
  1006. }
  1007. }
  1008. // User/host detection
  1009. case "packet.352": // WHO reply
  1010. {
  1011. // Example args: test * ~irce 127.0.0.1 localhost.localnetwork Gissleh H :0 ...
  1012. nick := event.Args[5]
  1013. user := event.Args[2]
  1014. host := event.Args[3]
  1015. if nick == client.nick {
  1016. client.mutex.Lock()
  1017. client.user = user
  1018. client.host = host
  1019. client.mutex.Unlock()
  1020. }
  1021. }
  1022. case "packet.chghost":
  1023. {
  1024. if event.Nick == client.nick {
  1025. client.mutex.Lock()
  1026. client.user = event.Args[1]
  1027. client.host = event.Args[2]
  1028. client.mutex.Unlock()
  1029. }
  1030. // This may be relevant in channels where the client resides.
  1031. client.handleInTargets(event.Nick, event)
  1032. }
  1033. // Channel join/leave/mode handling
  1034. case "packet.join":
  1035. {
  1036. var channel *Channel
  1037. if event.Nick == client.nick {
  1038. channel = &Channel{
  1039. id: generateClientID("T"),
  1040. name: event.Arg(0),
  1041. userlist: list.New(&client.isupport),
  1042. }
  1043. _ = client.AddTarget(channel)
  1044. } else {
  1045. channel = client.Channel(event.Arg(0))
  1046. }
  1047. client.handleInTarget(channel, event)
  1048. }
  1049. case "packet.part":
  1050. {
  1051. channel := client.Channel(event.Arg(0))
  1052. if channel == nil {
  1053. break
  1054. }
  1055. if event.Nick == client.nick {
  1056. channel.parted = true
  1057. _, _ = client.RemoveTarget(channel)
  1058. } else {
  1059. client.handleInTarget(channel, event)
  1060. }
  1061. }
  1062. case "packet.kick":
  1063. {
  1064. channel := client.Channel(event.Arg(0))
  1065. if channel == nil {
  1066. break
  1067. }
  1068. if event.Arg(1) == client.nick {
  1069. channel.parted = true
  1070. _, _ = client.RemoveTarget(channel)
  1071. } else {
  1072. client.handleInTarget(channel, event)
  1073. }
  1074. }
  1075. case "packet.quit":
  1076. {
  1077. client.handleInTargets(event.Nick, event)
  1078. }
  1079. case "packet.353": // NAMES
  1080. {
  1081. channel := client.Channel(event.Arg(2))
  1082. if channel != nil {
  1083. client.handleInTarget(channel, event)
  1084. }
  1085. }
  1086. case "packet.366": // End of NAMES
  1087. {
  1088. channel := client.Channel(event.Arg(1))
  1089. if channel != nil {
  1090. client.handleInTarget(channel, event)
  1091. }
  1092. }
  1093. case "packet.invite":
  1094. {
  1095. inviteeNick := event.Arg(0)
  1096. channelName := event.Arg(1)
  1097. channel := client.Channel(channelName)
  1098. if client.config.AutoJoinInvites && inviteeNick == client.Nick() {
  1099. if channel == nil {
  1100. client.Join(channelName)
  1101. }
  1102. }
  1103. // Add channel target for rendering invite-notify invitations.
  1104. if channel != nil {
  1105. client.handleInTarget(channel, event)
  1106. }
  1107. }
  1108. case "packet.mode":
  1109. {
  1110. targetName := event.Arg(0)
  1111. if client.isupport.IsChannel(targetName) {
  1112. channel := client.Channel(targetName)
  1113. if channel != nil {
  1114. client.handleInTarget(channel, event)
  1115. }
  1116. }
  1117. }
  1118. // Message parsing
  1119. case "packet.privmsg", "ctcp.action":
  1120. {
  1121. // Target the message
  1122. target := Target(client.status)
  1123. targetName := event.Arg(0)
  1124. if targetName == client.Nick() {
  1125. targetName = event.Nick
  1126. }
  1127. if event.Nick == client.nick || event.Arg(0) == client.nick {
  1128. queryTarget := client.Target("query", targetName)
  1129. if queryTarget == nil {
  1130. query := &Query{
  1131. id: client.id,
  1132. user: list.User{
  1133. Nick: event.Nick,
  1134. User: event.User,
  1135. Host: event.Host,
  1136. },
  1137. }
  1138. if accountTag, ok := event.Tags["account"]; ok {
  1139. query.user.Account = accountTag
  1140. }
  1141. _ = client.AddTarget(query)
  1142. event.RenderTags["spawned"] = query.id
  1143. queryTarget = query
  1144. }
  1145. target = queryTarget
  1146. } else {
  1147. channel := client.Channel(targetName)
  1148. if channel != nil {
  1149. if user, ok := channel.UserList().User(event.Nick); ok {
  1150. event.RenderTags["prefixedNick"] = user.PrefixedNick
  1151. }
  1152. target = channel
  1153. }
  1154. }
  1155. client.handleInTarget(target, event)
  1156. }
  1157. case "packet.notice":
  1158. {
  1159. // Find channel target
  1160. targetName := event.Arg(0)
  1161. if client.isupport.IsChannel(targetName) {
  1162. channel := client.Channel(targetName)
  1163. if channel != nil {
  1164. if user, ok := channel.UserList().User(event.Nick); ok {
  1165. event.RenderTags["prefixedNick"] = user.PrefixedNick
  1166. }
  1167. client.handleInTarget(channel, event)
  1168. }
  1169. } else {
  1170. // Try to target by mentioned channel name.
  1171. for _, token := range strings.Fields(event.Text) {
  1172. if client.isupport.IsChannel(token) {
  1173. channel := client.Channel(token)
  1174. if channel == nil {
  1175. continue
  1176. }
  1177. if user, ok := channel.UserList().User(event.Nick); ok {
  1178. event.RenderTags["prefixedNick"] = user.PrefixedNick
  1179. }
  1180. client.handleInTarget(channel, event)
  1181. break
  1182. }
  1183. }
  1184. }
  1185. // Otherwise, it belongs in the status target
  1186. if len(event.targets) == 0 {
  1187. client.status.Handle(event, client)
  1188. client.handleInTarget(client.status, event)
  1189. }
  1190. }
  1191. // account-notify
  1192. case "packet.account":
  1193. {
  1194. client.handleInTargets(event.Nick, event)
  1195. }
  1196. // away-notify
  1197. case "packet.away":
  1198. {
  1199. client.handleInTargets(event.Nick, event)
  1200. }
  1201. // Auto-rejoin
  1202. case "packet.376", "packet.422":
  1203. {
  1204. client.mutex.RLock()
  1205. channels := make([]string, 0, len(client.targets))
  1206. rejoinEvent := NewEvent("info", "rejoin")
  1207. for _, target := range client.targets {
  1208. if channel, ok := target.(*Channel); ok {
  1209. channels = append(channels, channel.Name())
  1210. rejoinEvent.targets = append(rejoinEvent.targets, target)
  1211. }
  1212. }
  1213. client.mutex.RUnlock()
  1214. if len(channels) > 0 {
  1215. _ = client.Sendf("JOIN %s", strings.Join(channels, ","))
  1216. client.EmitNonBlocking(rejoinEvent)
  1217. }
  1218. client.mutex.Lock()
  1219. client.ready = true
  1220. client.mutex.Unlock()
  1221. client.EmitNonBlocking(NewEvent("hook", "ready"))
  1222. }
  1223. }
  1224. if sentCapEnd {
  1225. client.SetValue("internal.lockNickChange", false)
  1226. if primedNick, _ := client.Value("internal.primedNickChange").(string); primedNick != "" {
  1227. _ = client.Sendf("NICK %s", primedNick)
  1228. }
  1229. }
  1230. if len(event.targets) == 0 {
  1231. client.handleInTarget(client.status, event)
  1232. }
  1233. client.mutex.RLock()
  1234. clientHandlers := client.handlers
  1235. client.mutex.RUnlock()
  1236. for _, handler := range clientHandlers {
  1237. handler(event, client)
  1238. }
  1239. }
  1240. func (client *Client) handleInTargets(nick string, event *Event) {
  1241. client.mutex.RLock()
  1242. for i := range client.targets {
  1243. switch target := client.targets[i].(type) {
  1244. case *Channel:
  1245. {
  1246. if nick != "" {
  1247. if _, ok := target.UserList().User(event.Nick); !ok {
  1248. continue
  1249. }
  1250. }
  1251. target.Handle(event, client)
  1252. event.targets = append(event.targets, target)
  1253. }
  1254. case *Query:
  1255. {
  1256. if target.user.Nick == nick {
  1257. target.Handle(event, client)
  1258. event.targets = append(event.targets, target)
  1259. }
  1260. }
  1261. case *Status:
  1262. {
  1263. if client.nick == event.Nick {
  1264. target.Handle(event, client)
  1265. event.targets = append(event.targets, target)
  1266. }
  1267. }
  1268. }
  1269. }
  1270. client.mutex.RUnlock()
  1271. }
  1272. func (client *Client) handleInTarget(target Target, event *Event) {
  1273. if target == nil {
  1274. return
  1275. }
  1276. event.targets = append(event.targets, target)
  1277. target.Handle(event, client)
  1278. }
  1279. func generateClientID(prefix string) string {
  1280. buffer := [12]byte{}
  1281. _, err := rand.Read(buffer[:])
  1282. // Ugly fallback if crypto rand doesn't work.
  1283. if err != nil {
  1284. mathRand.Read(buffer[:])
  1285. }
  1286. binary.BigEndian.PutUint32(buffer[4:], uint32(time.Now().Unix()))
  1287. return prefix + hex.EncodeToString(buffer[:])[1:]
  1288. }