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