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.

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