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.

1092 lines
24 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
  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. // SendCTCP sends a queued message with the following CTCP verb and text. If reply is true,
  236. // it will use a NOTICE instead of PRIVMSG.
  237. func (client *Client) SendCTCP(verb, targetName string, reply bool, text string) {
  238. ircVerb := "PRIVMSG"
  239. if reply {
  240. ircVerb = "NOTICE"
  241. }
  242. client.SendQueuedf("%s %s :\x01%s %s\x01", ircVerb, targetName, verb, text)
  243. }
  244. // SendCTCPf is SendCTCP with a fmt.Sprintf
  245. func (client *Client) SendCTCPf(verb, targetName string, reply bool, format string, a ...interface{}) {
  246. client.SendCTCP(verb, targetName, reply, fmt.Sprintf(format, a...))
  247. }
  248. // Emit sends an event through the client's event, and it will return immediately
  249. // unless the internal channel is filled up. The returned context can be used to
  250. // wait for the event, or the client's destruction.
  251. func (client *Client) Emit(event Event) context.Context {
  252. event.ctx, event.cancel = context.WithCancel(client.ctx)
  253. client.events <- &event
  254. return event.ctx
  255. }
  256. // EmitNonBlocking is just like emit, but it will spin off a goroutine if the channel is full.
  257. // This lets it be called from other handlers without ever blocking. See Emit for what the
  258. // returned context is for.
  259. func (client *Client) EmitNonBlocking(event Event) context.Context {
  260. event.ctx, event.cancel = context.WithCancel(client.ctx)
  261. select {
  262. case client.events <- &event:
  263. default:
  264. go func() { client.events <- &event }()
  265. }
  266. return event.ctx
  267. }
  268. // EmitSync emits an event and waits for either its context to complete or the one
  269. // passed to it (e.g. a request's context). It's a shorthand for Emit with its
  270. // return value used in a `select` along with a passed context.
  271. func (client *Client) EmitSync(ctx context.Context, event Event) (err error) {
  272. eventCtx := client.Emit(event)
  273. select {
  274. case <-eventCtx.Done():
  275. {
  276. if err := eventCtx.Err(); err != context.Canceled {
  277. return err
  278. }
  279. return nil
  280. }
  281. case <-ctx.Done():
  282. {
  283. return ctx.Err()
  284. }
  285. }
  286. }
  287. // EmitInput emits an input event parsed from the line.
  288. func (client *Client) EmitInput(line string, target Target) context.Context {
  289. event := ParseInput(line)
  290. if target != nil {
  291. client.mutex.RLock()
  292. event.targets = append(event.targets, target)
  293. event.targetIds[target] = client.targteIds[target]
  294. client.mutex.RUnlock()
  295. } else {
  296. client.mutex.RLock()
  297. event.targets = append(event.targets, client.status)
  298. event.targetIds[client.status] = client.targteIds[client.status]
  299. client.mutex.RUnlock()
  300. }
  301. return client.Emit(event)
  302. }
  303. // Value gets a client value.
  304. func (client *Client) Value(key string) (v interface{}, ok bool) {
  305. client.mutex.RLock()
  306. v, ok = client.values[key]
  307. client.mutex.RUnlock()
  308. return
  309. }
  310. // SetValue sets a client value.
  311. func (client *Client) SetValue(key string, value interface{}) {
  312. client.mutex.Lock()
  313. client.values[key] = value
  314. client.mutex.Unlock()
  315. }
  316. // Destroy destroys the client, which will lead to a disconnect. Cancelling the
  317. // parent context will do the same.
  318. func (client *Client) Destroy() {
  319. client.Disconnect()
  320. client.cancel()
  321. close(client.sends)
  322. close(client.events)
  323. }
  324. // Destroyed returns true if the client has been destroyed, either by
  325. // Destroy or the parent context.
  326. func (client *Client) Destroyed() bool {
  327. select {
  328. case <-client.ctx.Done():
  329. return true
  330. default:
  331. return false
  332. }
  333. }
  334. // PrivmsgOverhead returns the overhead on a privmsg to the target. If `action` is true,
  335. // it will also count the extra overhead of a CTCP ACTION.
  336. func (client *Client) PrivmsgOverhead(targetName string, action bool) int {
  337. client.mutex.RLock()
  338. defer client.mutex.RUnlock()
  339. // Return a really safe estimate if user or host is missing.
  340. if client.user == "" || client.host == "" {
  341. return 200
  342. }
  343. return ircutil.MessageOverhead(client.nick, client.user, client.host, targetName, action)
  344. }
  345. // Join joins one or more channels without a key.
  346. func (client *Client) Join(channels ...string) error {
  347. return client.Sendf("JOIN %s", strings.Join(channels, ","))
  348. }
  349. // Target gets a target by kind and name
  350. func (client *Client) Target(kind string, name string) Target {
  351. client.mutex.RLock()
  352. defer client.mutex.RUnlock()
  353. for _, target := range client.targets {
  354. if target.Kind() == kind && target.Name() == name {
  355. return target
  356. }
  357. }
  358. return nil
  359. }
  360. // Channel is a shorthand for getting a channel target and type asserting it.
  361. func (client *Client) Channel(name string) *Channel {
  362. target := client.Target("channel", name)
  363. if target == nil {
  364. return nil
  365. }
  366. return target.(*Channel)
  367. }
  368. // Query is a shorthand for getting a query target and type asserting it.
  369. func (client *Client) Query(name string) *Query {
  370. target := client.Target("query", name)
  371. if target == nil {
  372. return nil
  373. }
  374. return target.(*Query)
  375. }
  376. // AddTarget adds a target to the client, generating a unique ID for it.
  377. func (client *Client) AddTarget(target Target) (id string, err error) {
  378. client.mutex.Lock()
  379. defer client.mutex.Unlock()
  380. for i := range client.targets {
  381. if target == client.targets[i] {
  382. err = ErrTargetAlreadyAdded
  383. return
  384. } else if target.Kind() == client.targets[i].Kind() && target.Name() == client.targets[i].Name() {
  385. err = ErrTargetConflict
  386. return
  387. }
  388. }
  389. id = generateClientID()
  390. client.targets = append(client.targets, target)
  391. client.targteIds[target] = id
  392. return
  393. }
  394. // RemoveTarget removes a target to the client
  395. func (client *Client) RemoveTarget(target Target) (id string, err error) {
  396. if target == client.status {
  397. return "", ErrTargetIsStatus
  398. }
  399. client.mutex.Lock()
  400. defer client.mutex.Unlock()
  401. for i := range client.targets {
  402. if target == client.targets[i] {
  403. id = client.targteIds[target]
  404. client.targets[i] = client.targets[len(client.targets)-1]
  405. client.targets = client.targets[:len(client.targets)-1]
  406. delete(client.targteIds, target)
  407. return
  408. }
  409. }
  410. err = ErrTargetNotFound
  411. return
  412. }
  413. // FindUser checks each channel to find user info about a user.
  414. func (client *Client) FindUser(nick string) (u list.User, ok bool) {
  415. client.mutex.RLock()
  416. defer client.mutex.RUnlock()
  417. for _, target := range client.targets {
  418. channel, ok := target.(*Channel)
  419. if !ok {
  420. continue
  421. }
  422. user, ok := channel.UserList().User(nick)
  423. if !ok {
  424. continue
  425. }
  426. return user, true
  427. }
  428. return list.User{}, false
  429. }
  430. func (client *Client) handleEventLoop() {
  431. ticker := time.NewTicker(time.Second * 30)
  432. for {
  433. select {
  434. case event, ok := <-client.events:
  435. {
  436. if !ok {
  437. goto end
  438. }
  439. client.handleEvent(event)
  440. emit(event, client)
  441. // Turn an unhandled input into a raw command.
  442. if event.kind == "input" && !event.Killed() {
  443. client.SendQueued(strings.ToUpper(event.verb) + " " + event.Text)
  444. }
  445. event.cancel()
  446. }
  447. case <-ticker.C:
  448. {
  449. event := NewEvent("client", "tick")
  450. event.ctx, event.cancel = context.WithCancel(client.ctx)
  451. client.handleEvent(&event)
  452. emit(&event, client)
  453. event.cancel()
  454. }
  455. case <-client.ctx.Done():
  456. {
  457. goto end
  458. }
  459. }
  460. }
  461. end:
  462. ticker.Stop()
  463. client.Disconnect()
  464. event := NewEvent("client", "destroy")
  465. event.ctx, event.cancel = context.WithCancel(client.ctx)
  466. client.handleEvent(&event)
  467. emit(&event, client)
  468. event.cancel()
  469. }
  470. func (client *Client) handleSendLoop() {
  471. lastRefresh := time.Time{}
  472. queue := client.config.SendRate
  473. for line := range client.sends {
  474. now := time.Now()
  475. deltaTime := now.Sub(lastRefresh)
  476. if deltaTime < time.Second {
  477. queue--
  478. if queue <= 0 {
  479. time.Sleep(time.Second - deltaTime)
  480. lastRefresh = now
  481. queue = client.config.SendRate - 1
  482. }
  483. } else {
  484. lastRefresh = now
  485. queue = client.config.SendRate - 1
  486. }
  487. client.Send(line)
  488. }
  489. }
  490. // handleEvent is always first and gets to break a few rules.
  491. func (client *Client) handleEvent(event *Event) {
  492. // IRCv3 `server-time`
  493. if timeTag, ok := event.Tags["time"]; ok {
  494. serverTime, err := time.Parse(time.RFC3339Nano, timeTag)
  495. if err == nil && serverTime.Year() > 2000 {
  496. event.Time = serverTime
  497. }
  498. }
  499. switch event.name {
  500. // Ping Pong
  501. case "hook.tick":
  502. {
  503. client.mutex.RLock()
  504. lastSend := time.Since(client.lastSend)
  505. client.mutex.RUnlock()
  506. if lastSend > time.Second*120 {
  507. client.Sendf("PING :%x%x%x", mathRand.Int63(), mathRand.Int63(), mathRand.Int63())
  508. }
  509. }
  510. case "packet.ping":
  511. {
  512. message := "PONG"
  513. for _, arg := range event.Args {
  514. message += " " + arg
  515. }
  516. if event.Text != "" {
  517. message += " :" + event.Text
  518. }
  519. client.Send(message + "")
  520. }
  521. // Client Registration
  522. case "client.connect":
  523. {
  524. client.Send("CAP LS 302")
  525. if client.config.Password != "" {
  526. client.Sendf("PASS :%s", client.config.Password)
  527. }
  528. nick := client.config.Nick
  529. client.mutex.RLock()
  530. if client.nick != "" {
  531. nick = client.nick
  532. }
  533. client.mutex.RUnlock()
  534. client.Sendf("NICK %s", nick)
  535. client.Sendf("USER %s 8 * :%s", client.config.User, client.config.RealName)
  536. }
  537. case "packet.001":
  538. {
  539. client.mutex.Lock()
  540. client.nick = event.Args[0]
  541. client.mutex.Unlock()
  542. client.Sendf("WHO %s", event.Args[0])
  543. }
  544. case "packet.443":
  545. {
  546. client.mutex.RLock()
  547. hasRegistered := client.nick != ""
  548. client.mutex.RUnlock()
  549. if !hasRegistered {
  550. nick := event.Args[1]
  551. // "AltN" -> "AltN+1", ...
  552. prev := client.config.Nick
  553. sent := false
  554. for _, alt := range client.config.Alternatives {
  555. if nick == prev {
  556. client.Sendf("NICK %s", alt)
  557. sent = true
  558. break
  559. }
  560. prev = alt
  561. }
  562. if !sent {
  563. // "LastAlt" -> "Nick23962"
  564. client.Sendf("NICK %s%05d", client.config.Nick, mathRand.Int31n(99999))
  565. }
  566. }
  567. }
  568. case "packet.nick":
  569. {
  570. client.handleInTargets(event.Nick, event)
  571. if event.Nick == client.nick {
  572. client.SetValue("nick", event.Arg(0))
  573. }
  574. }
  575. // Handle ISupport
  576. case "packet.005":
  577. {
  578. for _, token := range event.Args[1:] {
  579. kvpair := strings.Split(token, "=")
  580. if len(kvpair) == 2 {
  581. client.isupport.Set(kvpair[0], kvpair[1])
  582. } else {
  583. client.isupport.Set(kvpair[0], "")
  584. }
  585. }
  586. }
  587. // Capability negotiation
  588. case "packet.cap":
  589. {
  590. capCommand := event.Args[1]
  591. capTokens := strings.Split(event.Text, " ")
  592. switch capCommand {
  593. case "LS":
  594. {
  595. for _, token := range capTokens {
  596. split := strings.SplitN(token, "=", 2)
  597. key := split[0]
  598. if len(key) == 0 {
  599. continue
  600. }
  601. if len(split) == 2 {
  602. client.capData[key] = split[1]
  603. }
  604. for i := range supportedCaps {
  605. if supportedCaps[i] == token {
  606. client.mutex.Lock()
  607. client.capsRequested = append(client.capsRequested, token)
  608. client.mutex.Unlock()
  609. break
  610. }
  611. }
  612. }
  613. if len(event.Args) < 3 || event.Args[2] != "*" {
  614. client.mutex.RLock()
  615. requestedCount := len(client.capsRequested)
  616. client.mutex.RUnlock()
  617. if requestedCount > 0 {
  618. client.mutex.RLock()
  619. requestedCaps := strings.Join(client.capsRequested, " ")
  620. client.mutex.RUnlock()
  621. client.Send("CAP REQ :" + requestedCaps)
  622. } else {
  623. client.Send("CAP END")
  624. }
  625. }
  626. }
  627. case "ACK":
  628. {
  629. for _, token := range capTokens {
  630. client.mutex.Lock()
  631. if client.capEnabled[token] {
  632. client.capEnabled[token] = true
  633. }
  634. client.mutex.Unlock()
  635. }
  636. client.Send("CAP END")
  637. }
  638. case "NAK":
  639. {
  640. // Remove offenders
  641. for _, token := range capTokens {
  642. client.mutex.Lock()
  643. for i := range client.capsRequested {
  644. if token == client.capsRequested[i] {
  645. client.capsRequested = append(client.capsRequested[:i], client.capsRequested[i+1:]...)
  646. break
  647. }
  648. }
  649. client.mutex.Unlock()
  650. }
  651. client.mutex.RLock()
  652. requestedCaps := strings.Join(client.capsRequested, " ")
  653. client.mutex.RUnlock()
  654. client.Send("CAP REQ :" + requestedCaps)
  655. }
  656. case "NEW":
  657. {
  658. requests := make([]string, 0, len(capTokens))
  659. for _, token := range capTokens {
  660. for i := range supportedCaps {
  661. if supportedCaps[i] == token {
  662. requests = append(requests, token)
  663. }
  664. }
  665. }
  666. if len(requests) > 0 {
  667. client.Send("CAP REQ :" + strings.Join(requests, " "))
  668. }
  669. }
  670. case "DEL":
  671. {
  672. for _, token := range capTokens {
  673. client.mutex.Lock()
  674. if client.capEnabled[token] {
  675. client.capEnabled[token] = false
  676. }
  677. client.mutex.Unlock()
  678. }
  679. }
  680. }
  681. }
  682. // User/host detection
  683. case "packet.352": // WHO reply
  684. {
  685. // Example args: test * ~irce 127.0.0.1 localhost.localnetwork Gissleh H :0 ...
  686. nick := event.Args[5]
  687. user := event.Args[2]
  688. host := event.Args[3]
  689. if nick == client.nick {
  690. client.mutex.Lock()
  691. client.user = user
  692. client.host = host
  693. client.mutex.Unlock()
  694. }
  695. }
  696. case "packet.chghost":
  697. {
  698. if event.Nick == client.nick {
  699. client.mutex.Lock()
  700. client.user = event.Args[1]
  701. client.host = event.Args[2]
  702. client.mutex.Unlock()
  703. }
  704. // This may be relevant in channels where the client resides.
  705. client.handleInTargets(event.Nick, event)
  706. }
  707. // Channel join/leave/mode handling
  708. case "packet.join":
  709. {
  710. var channel *Channel
  711. if event.Nick == client.nick {
  712. channel = &Channel{name: event.Arg(0), userlist: list.New(&client.isupport)}
  713. client.AddTarget(channel)
  714. } else {
  715. channel = client.Channel(event.Arg(0))
  716. }
  717. client.handleInTarget(channel, event)
  718. if channel != nil {
  719. channel.Handle(event, client)
  720. }
  721. }
  722. case "packet.part":
  723. {
  724. channel := client.Channel(event.Arg(0))
  725. if channel == nil {
  726. break
  727. }
  728. channel.Handle(event, client)
  729. if event.Nick == client.nick {
  730. client.RemoveTarget(channel)
  731. } else {
  732. client.handleInTarget(channel, event)
  733. }
  734. }
  735. case "packet.quit":
  736. {
  737. client.handleInTargets(event.Nick, event)
  738. }
  739. case "packet.353": // NAMES
  740. {
  741. channel := client.Channel(event.Arg(2))
  742. if channel != nil {
  743. client.handleInTarget(channel, event)
  744. }
  745. }
  746. case "packet.366": // End of NAMES
  747. {
  748. channel := client.Channel(event.Arg(1))
  749. if channel != nil {
  750. client.handleInTarget(channel, event)
  751. }
  752. }
  753. case "packet.mode":
  754. {
  755. targetName := event.Arg(0)
  756. if client.isupport.IsChannel(targetName) {
  757. channel := client.Channel(targetName)
  758. if channel != nil {
  759. client.handleInTarget(channel, event)
  760. }
  761. }
  762. }
  763. // Message parsing
  764. case "packet.privmsg", "ctcp.action":
  765. {
  766. // Target the mssage
  767. target := Target(client.status)
  768. spawned := false
  769. targetName := event.Arg(0)
  770. if targetName == client.nick {
  771. target := client.Target("query", targetName)
  772. if target == nil {
  773. query := &Query{user: list.User{
  774. Nick: event.Nick,
  775. User: event.User,
  776. Host: event.Host,
  777. }}
  778. client.AddTarget(query)
  779. spawned = true
  780. target = query
  781. }
  782. } else {
  783. channel := client.Channel(targetName)
  784. if channel != nil {
  785. if user, ok := channel.UserList().User(event.Nick); ok {
  786. event.RenderTags["prefixedNick"] = user.PrefixedNick
  787. }
  788. target = channel
  789. } else {
  790. target = client.status
  791. }
  792. }
  793. client.handleInTarget(target, event)
  794. if spawned {
  795. // TODO: Message has higher importance // 0:Normal, 1:Important, 2:Highlight
  796. }
  797. }
  798. case "packet.notice":
  799. {
  800. // Try to target by mentioned channel name
  801. for _, token := range strings.Fields(event.Text) {
  802. if client.isupport.IsChannel(token) {
  803. channel := client.Channel(token)
  804. if channel == nil {
  805. continue
  806. }
  807. if user, ok := channel.UserList().User(event.Nick); ok {
  808. event.RenderTags["prefixedNick"] = user.PrefixedNick
  809. }
  810. client.handleInTarget(channel, event)
  811. break
  812. }
  813. }
  814. // Otherwise, it belongs in the status target
  815. if len(event.targets) == 0 {
  816. client.status.Handle(event, client)
  817. client.handleInTarget(client.status, event)
  818. }
  819. }
  820. // account-notify
  821. case "packet.account":
  822. {
  823. client.handleInTargets(event.Nick, event)
  824. }
  825. // away-notify
  826. case "packet.away":
  827. {
  828. client.handleInTargets(event.Nick, event)
  829. }
  830. }
  831. if len(event.targets) == 0 {
  832. client.handleInTarget(client.status, event)
  833. }
  834. }
  835. func (client *Client) handleInTargets(nick string, event *Event) {
  836. client.mutex.RLock()
  837. for i := range client.targets {
  838. switch target := client.targets[i].(type) {
  839. case *Channel:
  840. {
  841. if nick != "" {
  842. if _, ok := target.UserList().User(event.Nick); !ok {
  843. continue
  844. }
  845. }
  846. target.Handle(event, client)
  847. event.targets = append(event.targets, target)
  848. event.targetIds[target] = client.targteIds[target]
  849. }
  850. case *Query:
  851. {
  852. if target.user.Nick == nick {
  853. target.Handle(event, client)
  854. event.targets = append(event.targets, target)
  855. event.targetIds[target] = client.targteIds[target]
  856. }
  857. }
  858. case *Status:
  859. {
  860. if client.nick == event.Nick {
  861. target.Handle(event, client)
  862. event.targets = append(event.targets, target)
  863. event.targetIds[target] = client.targteIds[target]
  864. }
  865. }
  866. }
  867. }
  868. client.mutex.RUnlock()
  869. }
  870. func (client *Client) handleInTarget(target Target, event *Event) {
  871. client.mutex.RLock()
  872. target.Handle(event, client)
  873. event.targets = append(event.targets, target)
  874. event.targetIds[target] = client.targteIds[target]
  875. client.mutex.RUnlock()
  876. }
  877. func generateClientID() string {
  878. bytes := make([]byte, 12)
  879. _, err := rand.Read(bytes)
  880. // Ugly fallback if crypto rand doesn't work.
  881. if err != nil {
  882. rng := mathRand.NewSource(time.Now().UnixNano())
  883. result := strconv.FormatInt(rng.Int63(), 16)
  884. for len(result) < 24 {
  885. result += strconv.FormatInt(rng.Int63(), 16)
  886. }
  887. return result[:24]
  888. }
  889. binary.BigEndian.PutUint32(bytes[4:], uint32(time.Now().Unix()))
  890. return hex.EncodeToString(bytes)
  891. }