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.

1561 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", "Connect failed: "+err.Error()))
  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()))
  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()))
  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", "Read failed: "+err.Error()))
  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", err.Error()))
  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("invalid_target", "Target does not exist."))
  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. // IRCv3 `server-time`
  685. if timeTag, ok := event.Tags["time"]; ok {
  686. serverTime, err := time.Parse(time.RFC3339Nano, timeTag)
  687. if err == nil && serverTime.Year() > 2000 {
  688. event.Time = serverTime
  689. }
  690. }
  691. // For events that were created with targets, handle them now there now.
  692. for _, target := range event.targets {
  693. target.Handle(event, client)
  694. }
  695. switch event.name {
  696. // Ping Pong
  697. case "hook.tick":
  698. {
  699. client.mutex.RLock()
  700. lastSend := time.Since(client.lastSend)
  701. client.mutex.RUnlock()
  702. if lastSend > time.Second*120 {
  703. _ = client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
  704. }
  705. }
  706. case "packet.ping":
  707. {
  708. message := "PONG"
  709. for _, arg := range event.Args {
  710. message += " " + arg
  711. }
  712. if event.Text != "" {
  713. message += " :" + event.Text
  714. }
  715. _ = client.Send(message)
  716. }
  717. // Client Registration
  718. case "client.connect":
  719. {
  720. // Clear enabled caps and initiate negotiation.
  721. client.mutex.Lock()
  722. for key := range client.capEnabled {
  723. delete(client.capEnabled, key)
  724. }
  725. client.mutex.Unlock()
  726. _ = client.Send("CAP LS 302")
  727. // Send server password if configured.
  728. if client.config.Password != "" {
  729. _ = client.Sendf("PASS :%s", client.config.Password)
  730. }
  731. // Reuse nick or get from config
  732. nick := client.config.Nick
  733. client.mutex.RLock()
  734. if client.nick != "" {
  735. nick = client.nick
  736. }
  737. client.mutex.RUnlock()
  738. // Clear connection-specific data
  739. client.mutex.Lock()
  740. client.nick = ""
  741. client.user = ""
  742. client.host = ""
  743. client.capsRequested = client.capsRequested[:0]
  744. for key := range client.capData {
  745. delete(client.capData, key)
  746. }
  747. for key := range client.capEnabled {
  748. delete(client.capEnabled, key)
  749. }
  750. client.mutex.Unlock()
  751. // Start registration.
  752. _ = client.Sendf("NICK %s", nick)
  753. _ = client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
  754. }
  755. // Welcome message
  756. case "packet.001":
  757. {
  758. client.mutex.Lock()
  759. client.nick = event.Args[0]
  760. client.mutex.Unlock()
  761. // Send a WHO right away to gather enough client information for precise message cutting.
  762. _ = client.Sendf("WHO %s", event.Args[0])
  763. }
  764. // Nick rotation
  765. case "packet.431", "packet.432", "packet.433", "packet.436":
  766. {
  767. lockNickChange, _ := client.Value("internal.lockNickChange").(bool)
  768. // Ignore if client is registered
  769. if client.Nick() != "" {
  770. break
  771. }
  772. nick := event.Args[1]
  773. newNick := ""
  774. // "AltN" -> "AltN+1", ...
  775. prev := client.config.Nick
  776. for _, alt := range client.config.Alternatives {
  777. if nick == prev {
  778. newNick = alt
  779. break
  780. }
  781. prev = alt
  782. }
  783. if newNick == "" {
  784. // "LastAlt" -> "Nick23962"
  785. newNick = fmt.Sprintf("%s%05d", client.config.Nick, mathRand.Int31n(99999))
  786. }
  787. if lockNickChange {
  788. client.SetValue("internal.primedNickChange", newNick)
  789. } else {
  790. _ = client.Sendf("NICK %s", newNick)
  791. }
  792. }
  793. case "packet.nick":
  794. {
  795. client.handleInTargets(event.Nick, event)
  796. if event.Nick == client.nick {
  797. client.SetValue("nick", event.Arg(0))
  798. }
  799. }
  800. // ISupport
  801. case "packet.005":
  802. {
  803. for _, token := range event.Args[1:] {
  804. kvpair := strings.Split(token, "=")
  805. if len(kvpair) == 2 {
  806. client.isupport.Set(kvpair[0], kvpair[1])
  807. } else {
  808. client.isupport.Set(kvpair[0], "")
  809. }
  810. }
  811. }
  812. // Capability negotiation
  813. case "packet.cap":
  814. {
  815. capCommand := event.Args[1]
  816. capTokens := strings.Split(event.Text, " ")
  817. switch capCommand {
  818. case "LS":
  819. {
  820. client.SetValue("internal.lockNickChange", true)
  821. for _, token := range capTokens {
  822. split := strings.SplitN(token, "=", 2)
  823. key := split[0]
  824. if len(key) == 0 {
  825. continue
  826. }
  827. if len(split) == 2 {
  828. client.capData[key] = split[1]
  829. }
  830. for i := range supportedCaps {
  831. if supportedCaps[i] == key {
  832. client.mutex.Lock()
  833. client.capsRequested = append(client.capsRequested, key)
  834. client.mutex.Unlock()
  835. break
  836. }
  837. }
  838. }
  839. if len(event.Args) < 3 || event.Args[2] != "*" {
  840. client.mutex.RLock()
  841. requestedCount := len(client.capsRequested)
  842. client.mutex.RUnlock()
  843. if requestedCount > 0 {
  844. client.mutex.RLock()
  845. requestedCaps := strings.Join(client.capsRequested, " ")
  846. client.mutex.RUnlock()
  847. _ = client.Send("CAP REQ :" + requestedCaps)
  848. } else {
  849. sentCapEnd = true
  850. _ = client.Send("CAP END")
  851. }
  852. }
  853. }
  854. case "ACK":
  855. {
  856. for _, token := range capTokens {
  857. client.mutex.Lock()
  858. if !client.capEnabled[token] {
  859. client.capEnabled[token] = true
  860. }
  861. client.mutex.Unlock()
  862. // Special cases for supported tokens
  863. switch token {
  864. case "sasl":
  865. {
  866. if client.config.SASL == nil {
  867. break
  868. }
  869. mechanisms := strings.Split(client.capData[token], ",")
  870. selectedMechanism := ""
  871. if len(mechanisms) == 0 || mechanisms[0] == "" {
  872. selectedMechanism = "PLAIN"
  873. }
  874. for _, mechanism := range mechanisms {
  875. if mechanism == "PLAIN" && selectedMechanism == "" {
  876. selectedMechanism = "PLAIN"
  877. }
  878. }
  879. // TODO: Add better mechanisms
  880. if selectedMechanism != "" {
  881. _ = client.Sendf("AUTHENTICATE %s", selectedMechanism)
  882. client.SetValue("sasl.usingMethod", "PLAIN")
  883. }
  884. }
  885. case "draft/languages":
  886. {
  887. if len(client.config.Languages) == 0 {
  888. break
  889. }
  890. // draft/languages=15,en,~bs,~de,~el,~en-AU,~es,~fi,~fr-FR,~it,~no,~pl,~pt-BR,~ro,~tr-TR,~zh-CN
  891. langData := strings.Split(client.capData[token], ",")
  892. if len(langData) < 0 {
  893. break
  894. }
  895. maxCount, err := strconv.Atoi(langData[0])
  896. if err != nil {
  897. break
  898. }
  899. languages := make([]string, 0, maxCount)
  900. LanguageLoop:
  901. for _, lang := range client.config.Languages {
  902. for _, lang2 := range langData[1:] {
  903. if strings.HasPrefix(lang2, "~") {
  904. lang2 = lang2[1:]
  905. }
  906. if strings.EqualFold(lang, lang2) {
  907. languages = append(languages, lang)
  908. if len(languages) >= maxCount {
  909. break LanguageLoop
  910. }
  911. }
  912. }
  913. }
  914. if len(languages) > 0 {
  915. _ = client.Send("LANGUAGE " + strings.Join(languages, " "))
  916. }
  917. }
  918. }
  919. }
  920. if !client.Ready() {
  921. sentCapEnd = true
  922. _ = client.Send("CAP END")
  923. }
  924. }
  925. case "NAK":
  926. {
  927. // Remove offenders
  928. for _, token := range capTokens {
  929. client.mutex.Lock()
  930. for i := range client.capsRequested {
  931. if token == client.capsRequested[i] {
  932. client.capsRequested = append(client.capsRequested[:i], client.capsRequested[i+1:]...)
  933. break
  934. }
  935. }
  936. client.mutex.Unlock()
  937. }
  938. client.mutex.RLock()
  939. requestedCaps := strings.Join(client.capsRequested, " ")
  940. client.mutex.RUnlock()
  941. _ = client.Send("CAP REQ :" + requestedCaps)
  942. }
  943. case "NEW":
  944. {
  945. requests := make([]string, 0, len(capTokens))
  946. for _, token := range capTokens {
  947. for i := range supportedCaps {
  948. if supportedCaps[i] == token {
  949. requests = append(requests, token)
  950. }
  951. }
  952. }
  953. if len(requests) > 0 {
  954. _ = client.Send("CAP REQ :" + strings.Join(requests, " "))
  955. }
  956. }
  957. case "DEL":
  958. {
  959. for _, token := range capTokens {
  960. client.mutex.Lock()
  961. if client.capEnabled[token] {
  962. client.capEnabled[token] = false
  963. }
  964. client.mutex.Unlock()
  965. }
  966. }
  967. }
  968. }
  969. // SASL
  970. case "packet.authenticate":
  971. {
  972. if event.Arg(0) != "+" {
  973. break
  974. }
  975. method, ok := client.Value("sasl.usingMethod").(string)
  976. if !ok {
  977. break
  978. }
  979. switch method {
  980. case "PLAIN":
  981. {
  982. parts := [][]byte{
  983. []byte(client.config.SASL.AuthenticationIdentity),
  984. []byte(client.config.SASL.AuthorizationIdentity),
  985. []byte(client.config.SASL.Password),
  986. }
  987. plainString := base64.StdEncoding.EncodeToString(bytes.Join(parts, []byte{0x00}))
  988. _ = client.Sendf("AUTHENTICATE %s", plainString)
  989. }
  990. }
  991. }
  992. case "packet.904": // Auth failed
  993. {
  994. // Cancel authentication.
  995. _ = client.Sendf("AUTHENTICATE *")
  996. client.SetValue("sasl.usingMethod", (interface{})(nil))
  997. }
  998. case "packet.903", "packet.906": // Auth ended
  999. {
  1000. // A bit dirty, but it'll get the nick rotation started again.
  1001. if client.Nick() == "" {
  1002. _ = client.Sendf("NICK %s", client.config.Nick)
  1003. }
  1004. }
  1005. // User/host detection
  1006. case "packet.352": // WHO reply
  1007. {
  1008. // Example args: test * ~irce 127.0.0.1 localhost.localnetwork Gissleh H :0 ...
  1009. nick := event.Args[5]
  1010. user := event.Args[2]
  1011. host := event.Args[3]
  1012. if nick == client.nick {
  1013. client.mutex.Lock()
  1014. client.user = user
  1015. client.host = host
  1016. client.mutex.Unlock()
  1017. }
  1018. }
  1019. case "packet.chghost":
  1020. {
  1021. if event.Nick == client.nick {
  1022. client.mutex.Lock()
  1023. client.user = event.Args[1]
  1024. client.host = event.Args[2]
  1025. client.mutex.Unlock()
  1026. }
  1027. // This may be relevant in channels where the client resides.
  1028. client.handleInTargets(event.Nick, event)
  1029. }
  1030. // Channel join/leave/mode handling
  1031. case "packet.join":
  1032. {
  1033. var channel *Channel
  1034. if event.Nick == client.nick {
  1035. channel = &Channel{
  1036. id: generateClientID("T"),
  1037. name: event.Arg(0),
  1038. userlist: list.New(&client.isupport),
  1039. }
  1040. _ = client.AddTarget(channel)
  1041. } else {
  1042. channel = client.Channel(event.Arg(0))
  1043. }
  1044. client.handleInTarget(channel, event)
  1045. }
  1046. case "packet.part":
  1047. {
  1048. channel := client.Channel(event.Arg(0))
  1049. if channel == nil {
  1050. break
  1051. }
  1052. if event.Nick == client.nick {
  1053. channel.parted = true
  1054. _, _ = client.RemoveTarget(channel)
  1055. } else {
  1056. client.handleInTarget(channel, event)
  1057. }
  1058. }
  1059. case "packet.kick":
  1060. {
  1061. channel := client.Channel(event.Arg(0))
  1062. if channel == nil {
  1063. break
  1064. }
  1065. if event.Arg(1) == client.nick {
  1066. channel.parted = true
  1067. _, _ = client.RemoveTarget(channel)
  1068. } else {
  1069. client.handleInTarget(channel, event)
  1070. }
  1071. }
  1072. case "packet.quit":
  1073. {
  1074. client.handleInTargets(event.Nick, event)
  1075. }
  1076. case "packet.353": // NAMES
  1077. {
  1078. channel := client.Channel(event.Arg(2))
  1079. if channel != nil {
  1080. client.handleInTarget(channel, event)
  1081. }
  1082. }
  1083. case "packet.366": // End of NAMES
  1084. {
  1085. channel := client.Channel(event.Arg(1))
  1086. if channel != nil {
  1087. client.handleInTarget(channel, event)
  1088. }
  1089. }
  1090. case "packet.invite":
  1091. {
  1092. inviteeNick := event.Arg(0)
  1093. channelName := event.Arg(1)
  1094. channel := client.Channel(channelName)
  1095. if client.config.AutoJoinInvites && inviteeNick == client.Nick() {
  1096. if channel == nil {
  1097. client.Join(channelName)
  1098. }
  1099. }
  1100. // Add channel target for rendering invite-notify invitations.
  1101. if channel != nil {
  1102. client.handleInTarget(channel, event)
  1103. }
  1104. }
  1105. case "packet.mode":
  1106. {
  1107. targetName := event.Arg(0)
  1108. if client.isupport.IsChannel(targetName) {
  1109. channel := client.Channel(targetName)
  1110. if channel != nil {
  1111. client.handleInTarget(channel, event)
  1112. }
  1113. }
  1114. }
  1115. // Message parsing
  1116. case "packet.privmsg", "ctcp.action":
  1117. {
  1118. // Target the message
  1119. target := Target(client.status)
  1120. targetName := event.Arg(0)
  1121. if targetName == client.nick {
  1122. target := client.Target("query", targetName)
  1123. if target == nil {
  1124. query := &Query{
  1125. id: client.id,
  1126. user: list.User{
  1127. Nick: event.Nick,
  1128. User: event.User,
  1129. Host: event.Host,
  1130. },
  1131. }
  1132. if accountTag, ok := event.Tags["account"]; ok {
  1133. query.user.Account = accountTag
  1134. }
  1135. _ = client.AddTarget(query)
  1136. event.RenderTags["spawned"] = query.id
  1137. target = query
  1138. }
  1139. } else {
  1140. channel := client.Channel(targetName)
  1141. if channel != nil {
  1142. if user, ok := channel.UserList().User(event.Nick); ok {
  1143. event.RenderTags["prefixedNick"] = user.PrefixedNick
  1144. }
  1145. target = channel
  1146. } else {
  1147. target = client.status
  1148. }
  1149. }
  1150. client.handleInTarget(target, event)
  1151. }
  1152. case "packet.notice":
  1153. {
  1154. // Find channel target
  1155. targetName := event.Arg(0)
  1156. if client.isupport.IsChannel(targetName) {
  1157. channel := client.Channel(targetName)
  1158. if channel != nil {
  1159. if user, ok := channel.UserList().User(event.Nick); ok {
  1160. event.RenderTags["prefixedNick"] = user.PrefixedNick
  1161. }
  1162. client.handleInTarget(channel, event)
  1163. }
  1164. } else {
  1165. // Try to target by mentioned channel name.
  1166. for _, token := range strings.Fields(event.Text) {
  1167. if client.isupport.IsChannel(token) {
  1168. channel := client.Channel(token)
  1169. if channel == nil {
  1170. continue
  1171. }
  1172. if user, ok := channel.UserList().User(event.Nick); ok {
  1173. event.RenderTags["prefixedNick"] = user.PrefixedNick
  1174. }
  1175. client.handleInTarget(channel, event)
  1176. break
  1177. }
  1178. }
  1179. }
  1180. // Otherwise, it belongs in the status target
  1181. if len(event.targets) == 0 {
  1182. client.status.Handle(event, client)
  1183. client.handleInTarget(client.status, event)
  1184. }
  1185. }
  1186. // account-notify
  1187. case "packet.account":
  1188. {
  1189. client.handleInTargets(event.Nick, event)
  1190. }
  1191. // away-notify
  1192. case "packet.away":
  1193. {
  1194. client.handleInTargets(event.Nick, event)
  1195. }
  1196. // Auto-rejoin
  1197. case "packet.376", "packet.422":
  1198. {
  1199. client.mutex.RLock()
  1200. channels := make([]string, 0, len(client.targets))
  1201. rejoinEvent := NewEvent("info", "rejoin")
  1202. for _, target := range client.targets {
  1203. if channel, ok := target.(*Channel); ok {
  1204. channels = append(channels, channel.Name())
  1205. rejoinEvent.targets = append(rejoinEvent.targets, target)
  1206. }
  1207. }
  1208. client.mutex.RUnlock()
  1209. if len(channels) > 0 {
  1210. _ = client.Sendf("JOIN %s", strings.Join(channels, ","))
  1211. client.EmitNonBlocking(rejoinEvent)
  1212. }
  1213. client.mutex.Lock()
  1214. client.ready = true
  1215. client.mutex.Unlock()
  1216. client.EmitNonBlocking(NewEvent("hook", "ready"))
  1217. }
  1218. }
  1219. if sentCapEnd {
  1220. client.SetValue("internal.lockNickChange", false)
  1221. if primedNick, _ := client.Value("internal.primedNickChange").(string); primedNick != "" {
  1222. _ = client.Sendf("NICK %s", primedNick)
  1223. }
  1224. }
  1225. if len(event.targets) == 0 {
  1226. client.handleInTarget(client.status, event)
  1227. }
  1228. client.mutex.RLock()
  1229. clientHandlers := client.handlers
  1230. client.mutex.RUnlock()
  1231. for _, handler := range clientHandlers {
  1232. handler(event, client)
  1233. }
  1234. }
  1235. func (client *Client) handleInTargets(nick string, event *Event) {
  1236. client.mutex.RLock()
  1237. for i := range client.targets {
  1238. switch target := client.targets[i].(type) {
  1239. case *Channel:
  1240. {
  1241. if nick != "" {
  1242. if _, ok := target.UserList().User(event.Nick); !ok {
  1243. continue
  1244. }
  1245. }
  1246. target.Handle(event, client)
  1247. event.targets = append(event.targets, target)
  1248. }
  1249. case *Query:
  1250. {
  1251. if target.user.Nick == nick {
  1252. target.Handle(event, client)
  1253. event.targets = append(event.targets, target)
  1254. }
  1255. }
  1256. case *Status:
  1257. {
  1258. if client.nick == event.Nick {
  1259. target.Handle(event, client)
  1260. event.targets = append(event.targets, target)
  1261. }
  1262. }
  1263. }
  1264. }
  1265. client.mutex.RUnlock()
  1266. }
  1267. func (client *Client) handleInTarget(target Target, event *Event) {
  1268. if target == nil {
  1269. return
  1270. }
  1271. client.mutex.RLock()
  1272. target.Handle(event, client)
  1273. event.targets = append(event.targets, target)
  1274. client.mutex.RUnlock()
  1275. }
  1276. func generateClientID(prefix string) string {
  1277. buffer := [12]byte{}
  1278. _, err := rand.Read(buffer[:])
  1279. // Ugly fallback if crypto rand doesn't work.
  1280. if err != nil {
  1281. mathRand.Read(buffer[:])
  1282. }
  1283. binary.BigEndian.PutUint32(buffer[4:], uint32(time.Now().Unix()))
  1284. return prefix + hex.EncodeToString(buffer[:])[1:]
  1285. }