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.

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