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.

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