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.

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