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.

1544 lines
36 KiB

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