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.

995 lines
21 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
  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. "strconv"
  14. "strings"
  15. "sync"
  16. "time"
  17. "git.aiterp.net/gisle/irc/ircutil"
  18. "git.aiterp.net/gisle/irc/isupport"
  19. "git.aiterp.net/gisle/irc/list"
  20. )
  21. var supportedCaps = []string{
  22. "server-time",
  23. "cap-notify",
  24. "multi-prefix",
  25. "userhost-in-names",
  26. "account-notify",
  27. "away-notify",
  28. "extended-join",
  29. "chghost",
  30. "account-tag",
  31. }
  32. // ErrNoConnection is returned if you try to do something requiring a connection,
  33. // but there is none.
  34. var ErrNoConnection = errors.New("irc: no connection")
  35. // ErrTargetAlreadyAdded is returned by Client.AddTarget if that target has already been
  36. // added to the client.
  37. var ErrTargetAlreadyAdded = errors.New("irc: target already added")
  38. // ErrTargetConflict is returned by Clinet.AddTarget if there already exists a target
  39. // matching the name and kind.
  40. var ErrTargetConflict = errors.New("irc: target name and kind match existing target")
  41. // ErrTargetNotFound is returned by Clinet.RemoveTarget if the target is not part of
  42. // the client's target list
  43. var ErrTargetNotFound = errors.New("irc: target not found")
  44. // ErrTargetIsStatus is returned by Clinet.RemoveTarget if the target is the client's
  45. // status target
  46. var ErrTargetIsStatus = errors.New("irc: cannot remove status target")
  47. // A Client is an IRC client. You need to use New to construct it
  48. type Client struct {
  49. id string
  50. config Config
  51. mutex sync.RWMutex
  52. conn net.Conn
  53. ctx context.Context
  54. cancel context.CancelFunc
  55. events chan *Event
  56. sends chan string
  57. lastSend time.Time
  58. capEnabled map[string]bool
  59. capData map[string]string
  60. capsRequested []string
  61. nick string
  62. user string
  63. host string
  64. quit bool
  65. isupport isupport.ISupport
  66. values map[string]interface{}
  67. status *Status
  68. targets []Target
  69. targteIds map[Target]string
  70. }
  71. // New creates a new client. The context can be context.Background if you want manually to
  72. // tear down clients upon quitting.
  73. func New(ctx context.Context, config Config) *Client {
  74. client := &Client{
  75. id: generateClientID(),
  76. values: make(map[string]interface{}),
  77. events: make(chan *Event, 64),
  78. sends: make(chan string, 64),
  79. capEnabled: make(map[string]bool),
  80. capData: make(map[string]string),
  81. config: config.WithDefaults(),
  82. targteIds: make(map[Target]string, 16),
  83. status: &Status{},
  84. }
  85. client.AddTarget(client.status)
  86. client.ctx, client.cancel = context.WithCancel(ctx)
  87. go client.handleEventLoop()
  88. go client.handleSendLoop()
  89. return client
  90. }
  91. // Context gets the client's context. It's cancelled if the parent context used
  92. // in New is, or Destroy is called.
  93. func (client *Client) Context() context.Context {
  94. return client.ctx
  95. }
  96. // ID gets the unique identifier for the client, which could be used in data structures
  97. func (client *Client) ID() string {
  98. client.mutex.RLock()
  99. defer client.mutex.RUnlock()
  100. return client.id
  101. }
  102. // Nick gets the nick of the client
  103. func (client *Client) Nick() string {
  104. client.mutex.RLock()
  105. defer client.mutex.RUnlock()
  106. return client.nick
  107. }
  108. // User gets the user/ident of the client
  109. func (client *Client) User() string {
  110. client.mutex.RLock()
  111. defer client.mutex.RUnlock()
  112. return client.user
  113. }
  114. // Host gets the hostname of the client
  115. func (client *Client) Host() string {
  116. client.mutex.RLock()
  117. defer client.mutex.RUnlock()
  118. return client.host
  119. }
  120. // ISupport gets the client's ISupport. This is mutable, and changes to it
  121. // *will* affect the client.
  122. func (client *Client) ISupport() *isupport.ISupport {
  123. return &client.isupport
  124. }
  125. // Connect connects to the server by addr.
  126. func (client *Client) Connect(addr string, ssl bool) (err error) {
  127. var conn net.Conn
  128. if client.Connected() {
  129. client.Disconnect()
  130. }
  131. client.isupport.Reset()
  132. client.mutex.Lock()
  133. client.quit = false
  134. client.mutex.Unlock()
  135. client.EmitSync(context.Background(), NewEvent("client", "connecting"))
  136. if ssl {
  137. conn, err = tls.Dial("tcp", addr, &tls.Config{
  138. InsecureSkipVerify: client.config.SkipSSLVerification,
  139. })
  140. if err != nil {
  141. return err
  142. }
  143. } else {
  144. conn, err = net.Dial("tcp", addr)
  145. if err != nil {
  146. return err
  147. }
  148. }
  149. client.Emit(NewEvent("client", "connect"))
  150. go func() {
  151. reader := bufio.NewReader(conn)
  152. replacer := strings.NewReplacer("\r", "", "\n", "")
  153. for {
  154. line, err := reader.ReadString('\n')
  155. if err != nil {
  156. break
  157. }
  158. line = replacer.Replace(line)
  159. event, err := ParsePacket(line)
  160. if err != nil {
  161. continue
  162. }
  163. client.Emit(event)
  164. }
  165. client.mutex.Lock()
  166. client.conn = nil
  167. client.mutex.Unlock()
  168. client.Emit(NewEvent("client", "disconnect"))
  169. }()
  170. client.mutex.Lock()
  171. client.conn = conn
  172. client.mutex.Unlock()
  173. return nil
  174. }
  175. // Disconnect disconnects from the server. It will either return the
  176. // close error, or ErrNoConnection if there is no connection
  177. func (client *Client) Disconnect() error {
  178. client.mutex.Lock()
  179. defer client.mutex.Unlock()
  180. if client.conn == nil {
  181. return ErrNoConnection
  182. }
  183. client.quit = true
  184. err := client.conn.Close()
  185. return err
  186. }
  187. // Connected returns true if the client has a connection
  188. func (client *Client) Connected() bool {
  189. client.mutex.RLock()
  190. defer client.mutex.RUnlock()
  191. return client.conn != nil
  192. }
  193. // Send sends a line to the server. A line-feed will be automatically added if one
  194. // is not provided.
  195. func (client *Client) Send(line string) error {
  196. client.mutex.RLock()
  197. conn := client.conn
  198. client.mutex.RUnlock()
  199. if conn == nil {
  200. return ErrNoConnection
  201. }
  202. if !strings.HasSuffix(line, "\n") {
  203. line += "\r\n"
  204. }
  205. _, err := conn.Write([]byte(line))
  206. if err != nil {
  207. client.EmitNonBlocking(NewErrorEvent("network", err.Error()))
  208. client.Disconnect()
  209. }
  210. return err
  211. }
  212. // Sendf is Send with a fmt.Sprintf
  213. func (client *Client) Sendf(format string, a ...interface{}) error {
  214. return client.Send(fmt.Sprintf(format, a...))
  215. }
  216. // SendQueued appends a message to a queue that will only send 2 messages
  217. // per second to avoid flooding. If the queue is ull, a goroutine will be
  218. // spawned to queue it, so this function will always return immediately.
  219. // Order may not be guaranteed, however, but if you're sending 64 messages
  220. // at once that may not be your greatest concern.
  221. //
  222. // Failed sends will be discarded quietly to avoid a backup from being
  223. // thrown on a new connection.
  224. func (client *Client) SendQueued(line string) {
  225. select {
  226. case client.sends <- line:
  227. default:
  228. go func() { client.sends <- line }()
  229. }
  230. }
  231. // SendQueuedf is SendQueued with a fmt.Sprintf
  232. func (client *Client) SendQueuedf(format string, a ...interface{}) {
  233. client.SendQueued(fmt.Sprintf(format, a...))
  234. }
  235. // Emit sends an event through the client's event, and it will return immediately
  236. // unless the internal channel is filled up. The returned context can be used to
  237. // wait for the event, or the client's destruction.
  238. func (client *Client) Emit(event Event) context.Context {
  239. event.ctx, event.cancel = context.WithCancel(client.ctx)
  240. client.events <- &event
  241. return event.ctx
  242. }
  243. // EmitNonBlocking is just like emit, but it will spin off a goroutine if the channel is full.
  244. // This lets it be called from other handlers without ever blocking. See Emit for what the
  245. // returned context is for.
  246. func (client *Client) EmitNonBlocking(event Event) context.Context {
  247. event.ctx, event.cancel = context.WithCancel(client.ctx)
  248. select {
  249. case client.events <- &event:
  250. default:
  251. go func() { client.events <- &event }()
  252. }
  253. return event.ctx
  254. }
  255. // EmitSync emits an event and waits for either its context to complete or the one
  256. // passed to it (e.g. a request's context). It's a shorthand for Emit with its
  257. // return value used in a `select` along with a passed context.
  258. func (client *Client) EmitSync(ctx context.Context, event Event) (err error) {
  259. eventCtx := client.Emit(event)
  260. select {
  261. case <-eventCtx.Done():
  262. {
  263. if err := eventCtx.Err(); err != context.Canceled {
  264. return err
  265. }
  266. return nil
  267. }
  268. case <-ctx.Done():
  269. {
  270. return ctx.Err()
  271. }
  272. }
  273. }
  274. // Value gets a client value.
  275. func (client *Client) Value(key string) (v interface{}, ok bool) {
  276. client.mutex.RLock()
  277. v, ok = client.values[key]
  278. client.mutex.RUnlock()
  279. return
  280. }
  281. // SetValue sets a client value.
  282. func (client *Client) SetValue(key string, value interface{}) {
  283. client.mutex.Lock()
  284. client.values[key] = value
  285. client.mutex.Unlock()
  286. }
  287. // Destroy destroys the client, which will lead to a disconnect. Cancelling the
  288. // parent context will do the same.
  289. func (client *Client) Destroy() {
  290. client.Disconnect()
  291. client.cancel()
  292. close(client.sends)
  293. close(client.events)
  294. }
  295. // Destroyed returns true if the client has been destroyed, either by
  296. // Destroy or the parent context.
  297. func (client *Client) Destroyed() bool {
  298. select {
  299. case <-client.ctx.Done():
  300. return true
  301. default:
  302. return false
  303. }
  304. }
  305. // PrivmsgOverhead returns the overhead on a privmsg to the target. If `action` is true,
  306. // it will also count the extra overhead of a CTCP ACTION.
  307. func (client *Client) PrivmsgOverhead(targetName string, action bool) int {
  308. client.mutex.RLock()
  309. defer client.mutex.RUnlock()
  310. // Return a really safe estimate if user or host is missing.
  311. if client.user == "" || client.host == "" {
  312. return 200
  313. }
  314. return ircutil.MessageOverhead(client.nick, client.user, client.host, targetName, action)
  315. }
  316. // Join joins one or more channels without a key.
  317. func (client *Client) Join(channels ...string) error {
  318. return client.Sendf("JOIN %s", strings.Join(channels, ","))
  319. }
  320. // Target gets a target by kind and name
  321. func (client *Client) Target(kind string, name string) Target {
  322. client.mutex.RLock()
  323. defer client.mutex.RUnlock()
  324. for _, target := range client.targets {
  325. if target.Kind() == kind && target.Name() == name {
  326. return target
  327. }
  328. }
  329. return nil
  330. }
  331. // Channel is a shorthand for getting a channel target and type asserting it.
  332. func (client *Client) Channel(name string) *Channel {
  333. target := client.Target("channel", name)
  334. if target == nil {
  335. return nil
  336. }
  337. return target.(*Channel)
  338. }
  339. // AddTarget adds a target to the client, generating a unique ID for it.
  340. func (client *Client) AddTarget(target Target) (id string, err error) {
  341. client.mutex.Lock()
  342. defer client.mutex.Unlock()
  343. for i := range client.targets {
  344. if target == client.targets[i] {
  345. err = ErrTargetAlreadyAdded
  346. return
  347. } else if target.Kind() == client.targets[i].Kind() && target.Name() == client.targets[i].Name() {
  348. err = ErrTargetConflict
  349. return
  350. }
  351. }
  352. id = generateClientID()
  353. client.targets = append(client.targets, target)
  354. client.targteIds[target] = id
  355. return
  356. }
  357. // RemoveTarget removes a target to the client
  358. func (client *Client) RemoveTarget(target Target) (id string, err error) {
  359. if target == client.status {
  360. return "", ErrTargetIsStatus
  361. }
  362. client.mutex.Lock()
  363. defer client.mutex.Unlock()
  364. for i := range client.targets {
  365. if target == client.targets[i] {
  366. id = client.targteIds[target]
  367. client.targets[i] = client.targets[len(client.targets)-1]
  368. client.targets = client.targets[:len(client.targets)-1]
  369. delete(client.targteIds, target)
  370. return
  371. }
  372. }
  373. err = ErrTargetNotFound
  374. return
  375. }
  376. func (client *Client) handleEventLoop() {
  377. ticker := time.NewTicker(time.Second * 30)
  378. for {
  379. select {
  380. case event, ok := <-client.events:
  381. {
  382. if !ok {
  383. goto end
  384. }
  385. client.handleEvent(event)
  386. emit(event, client)
  387. event.cancel()
  388. }
  389. case <-ticker.C:
  390. {
  391. event := NewEvent("client", "tick")
  392. event.ctx, event.cancel = context.WithCancel(client.ctx)
  393. client.handleEvent(&event)
  394. emit(&event, client)
  395. event.cancel()
  396. }
  397. case <-client.ctx.Done():
  398. {
  399. goto end
  400. }
  401. }
  402. }
  403. end:
  404. ticker.Stop()
  405. client.Disconnect()
  406. event := NewEvent("client", "destroy")
  407. event.ctx, event.cancel = context.WithCancel(client.ctx)
  408. client.handleEvent(&event)
  409. emit(&event, client)
  410. event.cancel()
  411. }
  412. func (client *Client) handleSendLoop() {
  413. lastRefresh := time.Time{}
  414. queue := 2
  415. for line := range client.sends {
  416. now := time.Now()
  417. deltaTime := now.Sub(lastRefresh)
  418. if deltaTime < time.Second {
  419. queue--
  420. if queue <= 0 {
  421. time.Sleep(time.Second - deltaTime)
  422. lastRefresh = now
  423. queue = 0
  424. }
  425. } else {
  426. lastRefresh = now
  427. }
  428. client.Send(line)
  429. }
  430. }
  431. // handleEvent is always first and gets to break a few rules.
  432. func (client *Client) handleEvent(event *Event) {
  433. // IRCv3 `server-time`
  434. if timeTag, ok := event.Tags["time"]; ok {
  435. serverTime, err := time.Parse(time.RFC3339Nano, timeTag)
  436. if err == nil && serverTime.Year() > 2000 {
  437. event.Time = serverTime
  438. }
  439. }
  440. switch event.name {
  441. // Ping Pong
  442. case "hook.tick":
  443. {
  444. client.mutex.RLock()
  445. lastSend := time.Since(client.lastSend)
  446. client.mutex.RUnlock()
  447. if lastSend > time.Second*120 {
  448. client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
  449. }
  450. }
  451. case "packet.ping":
  452. {
  453. message := "PONG"
  454. for _, arg := range event.Args {
  455. message += " " + arg
  456. }
  457. if event.Text != "" {
  458. message += " :" + event.Text
  459. }
  460. client.Send(message + "")
  461. }
  462. // Client Registration
  463. case "client.connect":
  464. {
  465. client.Send("CAP LS 302")
  466. if client.config.Password != "" {
  467. client.Sendf("PASS :%s", client.config.Password)
  468. }
  469. nick := client.config.Nick
  470. client.mutex.RLock()
  471. if client.nick != "" {
  472. nick = client.nick
  473. }
  474. client.mutex.RUnlock()
  475. client.Sendf("NICK %s", nick)
  476. client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
  477. }
  478. case "packet.001":
  479. {
  480. client.mutex.Lock()
  481. client.nick = event.Args[0]
  482. client.mutex.Unlock()
  483. client.Sendf("WHO %s", event.Args[0])
  484. }
  485. case "packet.443":
  486. {
  487. client.mutex.RLock()
  488. hasRegistered := client.nick != ""
  489. client.mutex.RUnlock()
  490. if !hasRegistered {
  491. nick := event.Args[1]
  492. // "AltN" -> "AltN+1", ...
  493. prev := client.config.Nick
  494. sent := false
  495. for _, alt := range client.config.Alternatives {
  496. if nick == prev {
  497. client.Sendf("NICK %s", alt)
  498. sent = true
  499. break
  500. }
  501. prev = alt
  502. }
  503. if !sent {
  504. // "LastAlt" -> "Nick23962"
  505. client.Sendf("NICK %s%05d", client.config.Nick, mathRand.Int31n(99999))
  506. }
  507. }
  508. }
  509. case "packet.nick":
  510. {
  511. client.handleInTargets(event.Nick, event)
  512. if event.Nick == client.nick {
  513. client.SetValue("nick", event.Arg(0))
  514. }
  515. }
  516. // Handle ISupport
  517. case "packet.005":
  518. {
  519. for _, token := range event.Args[1:] {
  520. kvpair := strings.Split(token, "=")
  521. if len(kvpair) == 2 {
  522. client.isupport.Set(kvpair[0], kvpair[1])
  523. } else {
  524. client.isupport.Set(kvpair[0], "")
  525. }
  526. }
  527. }
  528. // Capability negotiation
  529. case "packet.cap":
  530. {
  531. capCommand := event.Args[1]
  532. capTokens := strings.Split(event.Text, " ")
  533. switch capCommand {
  534. case "LS":
  535. {
  536. for _, token := range capTokens {
  537. split := strings.SplitN(token, "=", 2)
  538. key := split[0]
  539. if len(key) == 0 {
  540. continue
  541. }
  542. if len(split) == 2 {
  543. client.capData[key] = split[1]
  544. }
  545. for i := range supportedCaps {
  546. if supportedCaps[i] == token {
  547. client.mutex.Lock()
  548. client.capsRequested = append(client.capsRequested, token)
  549. client.mutex.Unlock()
  550. break
  551. }
  552. }
  553. }
  554. if len(event.Args) < 3 || event.Args[2] != "*" {
  555. client.mutex.RLock()
  556. requestedCount := len(client.capsRequested)
  557. client.mutex.RUnlock()
  558. if requestedCount > 0 {
  559. client.mutex.RLock()
  560. requestedCaps := strings.Join(client.capsRequested, " ")
  561. client.mutex.RUnlock()
  562. client.Send("CAP REQ :" + requestedCaps)
  563. } else {
  564. client.Send("CAP END")
  565. }
  566. }
  567. }
  568. case "ACK":
  569. {
  570. for _, token := range capTokens {
  571. client.mutex.Lock()
  572. if client.capEnabled[token] {
  573. client.capEnabled[token] = true
  574. }
  575. client.mutex.Unlock()
  576. }
  577. client.Send("CAP END")
  578. }
  579. case "NAK":
  580. {
  581. // Remove offenders
  582. for _, token := range capTokens {
  583. client.mutex.Lock()
  584. for i := range client.capsRequested {
  585. if token == client.capsRequested[i] {
  586. client.capsRequested = append(client.capsRequested[:i], client.capsRequested[i+1:]...)
  587. break
  588. }
  589. }
  590. client.mutex.Unlock()
  591. }
  592. client.mutex.RLock()
  593. requestedCaps := strings.Join(client.capsRequested, " ")
  594. client.mutex.RUnlock()
  595. client.Send("CAP REQ :" + requestedCaps)
  596. }
  597. case "NEW":
  598. {
  599. requests := make([]string, 0, len(capTokens))
  600. for _, token := range capTokens {
  601. for i := range supportedCaps {
  602. if supportedCaps[i] == token {
  603. requests = append(requests, token)
  604. }
  605. }
  606. }
  607. if len(requests) > 0 {
  608. client.Send("CAP REQ :" + strings.Join(requests, " "))
  609. }
  610. }
  611. case "DEL":
  612. {
  613. for _, token := range capTokens {
  614. client.mutex.Lock()
  615. if client.capEnabled[token] {
  616. client.capEnabled[token] = false
  617. }
  618. client.mutex.Unlock()
  619. }
  620. }
  621. }
  622. }
  623. // User/host detection
  624. case "packet.352": // WHO reply
  625. {
  626. // Example args: test * ~irce 127.0.0.1 localhost.localnetwork Gissleh H :0 ...
  627. nick := event.Args[5]
  628. user := event.Args[2]
  629. host := event.Args[3]
  630. if nick == client.nick {
  631. client.mutex.Lock()
  632. client.user = user
  633. client.host = host
  634. client.mutex.Unlock()
  635. }
  636. }
  637. case "packet.chghost":
  638. {
  639. if event.Nick == client.nick {
  640. client.mutex.Lock()
  641. client.user = event.Args[1]
  642. client.host = event.Args[2]
  643. client.mutex.Unlock()
  644. }
  645. // This may be relevant in channels where the client resides.
  646. client.handleInTargets(event.Nick, event)
  647. }
  648. // Channel join/leave/mode handling
  649. case "packet.join":
  650. {
  651. var channel *Channel
  652. if event.Nick == client.nick {
  653. channel = &Channel{name: event.Arg(0), userlist: list.New(&client.isupport)}
  654. client.AddTarget(channel)
  655. } else {
  656. channel = client.Channel(event.Arg(0))
  657. }
  658. event.targets = append(event.targets, channel)
  659. if channel != nil {
  660. channel.Handle(event, client)
  661. }
  662. }
  663. case "packet.part":
  664. {
  665. channel := client.Channel(event.Arg(0))
  666. if channel == nil {
  667. break
  668. }
  669. channel.Handle(event, client)
  670. if event.Nick == client.nick {
  671. client.RemoveTarget(channel)
  672. } else {
  673. event.targets = append(event.targets, channel)
  674. }
  675. }
  676. case "packet.quit":
  677. {
  678. client.handleInTargets(event.Nick, event)
  679. }
  680. case "packet.353": // NAMES
  681. {
  682. channel := client.Channel(event.Arg(2))
  683. if channel != nil {
  684. channel.Handle(event, client)
  685. }
  686. }
  687. case "packet.366": // End of NAMES
  688. {
  689. channel := client.Channel(event.Arg(1))
  690. if channel != nil {
  691. channel.Handle(event, client)
  692. }
  693. }
  694. case "packet.mode":
  695. {
  696. targetName := event.Arg(0)
  697. if client.isupport.IsChannel(targetName) {
  698. channel := client.Channel(targetName)
  699. if channel != nil {
  700. channel.Handle(event, client)
  701. }
  702. }
  703. }
  704. // Message parsing
  705. case "packet.privmsg", "ctcp.action":
  706. {
  707. // Target the mssage
  708. target := Target(client.status)
  709. spawned := false
  710. targetName := event.Arg(0)
  711. if targetName == client.nick {
  712. target := client.Target("query", targetName)
  713. if target == nil {
  714. query := &Query{user: list.User{
  715. Nick: event.Nick,
  716. User: event.User,
  717. Host: event.Host,
  718. }}
  719. client.AddTarget(query)
  720. spawned = true
  721. target = query
  722. }
  723. } else {
  724. target = client.Channel(targetName)
  725. if target == nil {
  726. target = client.status
  727. }
  728. }
  729. target.Handle(event, client)
  730. event.targets = append(event.targets, target)
  731. if spawned {
  732. // TODO: Message has higher importance // 0:Normal, 1:Important, 2:Highlight
  733. }
  734. }
  735. case "packet.notice":
  736. {
  737. // Try to target by mentioned channel name
  738. for _, token := range strings.Fields(event.Text) {
  739. if client.isupport.IsChannel(token) {
  740. channel := client.Channel(token)
  741. if channel == nil {
  742. continue
  743. }
  744. channel.Handle(event, client)
  745. event.targets = append(event.targets, channel)
  746. break
  747. }
  748. }
  749. // Otherwise, it belongs in the status target
  750. if len(event.targets) == 0 {
  751. client.status.Handle(event, client)
  752. event.targets = append(event.targets, client.status)
  753. }
  754. }
  755. // account-notify
  756. case "packet.account":
  757. {
  758. client.handleInTargets(event.Nick, event)
  759. }
  760. // away-notify
  761. case "packet.away":
  762. {
  763. client.handleInTargets(event.Nick, event)
  764. }
  765. }
  766. if len(event.targets) == 0 {
  767. event.targets = append(event.targets, client.status)
  768. }
  769. }
  770. func (client *Client) handleInTargets(nick string, event *Event) {
  771. client.mutex.RLock()
  772. for i := range client.targets {
  773. switch target := client.targets[i].(type) {
  774. case *Channel:
  775. {
  776. if nick != "" {
  777. if _, ok := target.UserList().User(event.Nick); !ok {
  778. continue
  779. }
  780. }
  781. event.targets = append(event.targets, target)
  782. target.Handle(event, client)
  783. }
  784. case *Query:
  785. {
  786. if target.user.Nick == nick {
  787. target.Handle(event, client)
  788. event.targets = append(event.targets, target)
  789. }
  790. }
  791. case *Status:
  792. {
  793. if client.nick == event.Nick {
  794. target.Handle(event, client)
  795. event.targets = append(event.targets, target)
  796. }
  797. }
  798. }
  799. }
  800. client.mutex.RUnlock()
  801. }
  802. func generateClientID() string {
  803. bytes := make([]byte, 12)
  804. _, err := rand.Read(bytes)
  805. // Ugly fallback if crypto rand doesn't work.
  806. if err != nil {
  807. rng := mathRand.NewSource(time.Now().UnixNano())
  808. result := strconv.FormatInt(rng.Int63(), 16)
  809. for len(result) < 24 {
  810. result += strconv.FormatInt(rng.Int63(), 16)
  811. }
  812. return result[:24]
  813. }
  814. binary.BigEndian.PutUint32(bytes[4:], uint32(time.Now().Unix()))
  815. return hex.EncodeToString(bytes)
  816. }