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.

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