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.

capability.go 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
  3. // released under the MIT license
  4. package irc
  5. import (
  6. "strings"
  7. "github.com/DanielOaks/girc-go/ircmsg"
  8. )
  9. // Capabilities are optional features a client may request from a server.
  10. type Capability string
  11. const (
  12. AccountTag Capability = "account-tag"
  13. AccountNotify Capability = "account-notify"
  14. AwayNotify Capability = "away-notify"
  15. ExtendedJoin Capability = "extended-join"
  16. MultiPrefix Capability = "multi-prefix"
  17. SASL Capability = "sasl"
  18. ServerTime Capability = "server-time"
  19. UserhostInNames Capability = "userhost-in-names"
  20. )
  21. var (
  22. SupportedCapabilities = CapabilitySet{
  23. AccountTag: true,
  24. AccountNotify: true,
  25. AwayNotify: true,
  26. ExtendedJoin: true,
  27. MultiPrefix: true,
  28. SASL: true,
  29. ServerTime: true,
  30. UserhostInNames: true,
  31. }
  32. )
  33. func (capability Capability) String() string {
  34. return string(capability)
  35. }
  36. // CapModifiers are indicators showing the state of a capability after a REQ or
  37. // ACK.
  38. type CapModifier rune
  39. const (
  40. Ack CapModifier = '~'
  41. Disable CapModifier = '-'
  42. Sticky CapModifier = '='
  43. )
  44. func (mod CapModifier) String() string {
  45. return string(mod)
  46. }
  47. type CapState uint
  48. const (
  49. CapNone CapState = iota
  50. CapNegotiating CapState = iota
  51. CapNegotiated CapState = iota
  52. )
  53. type CapabilitySet map[Capability]bool
  54. func (set CapabilitySet) String() string {
  55. strs := make([]string, len(set))
  56. index := 0
  57. for capability := range set {
  58. strs[index] = string(capability)
  59. index += 1
  60. }
  61. return strings.Join(strs, " ")
  62. }
  63. func (set CapabilitySet) DisableString() string {
  64. parts := make([]string, len(set))
  65. index := 0
  66. for capability := range set {
  67. parts[index] = Disable.String() + capability.String()
  68. index += 1
  69. }
  70. return strings.Join(parts, " ")
  71. }
  72. // CAP <subcmd> [<caps>]
  73. func capHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  74. subCommand := strings.ToUpper(msg.Params[0])
  75. capabilities := make(CapabilitySet)
  76. var capString string
  77. if len(msg.Params) > 1 {
  78. capString = msg.Params[1]
  79. strs := strings.Split(capString, " ")
  80. for _, str := range strs {
  81. if len(str) > 0 {
  82. capabilities[Capability(str)] = true
  83. }
  84. }
  85. }
  86. switch subCommand {
  87. case "LS":
  88. if !client.registered {
  89. client.capState = CapNegotiating
  90. }
  91. // client.server needs to be here to workaround a parsing bug in weechat 1.4
  92. // and let it connect to the server (otherwise it doesn't respond to the CAP
  93. // message with anything and just hangs on connection)
  94. client.Send(nil, server.name, "CAP", client.nick, subCommand, SupportedCapabilities.String())
  95. case "LIST":
  96. client.Send(nil, server.name, "CAP", client.nick, subCommand, client.capabilities.String())
  97. case "REQ":
  98. // make sure all capabilities actually exist
  99. for capability := range capabilities {
  100. if !SupportedCapabilities[capability] {
  101. client.Send(nil, server.name, "CAP", client.nick, "NAK", capString)
  102. return false
  103. }
  104. }
  105. for capability := range capabilities {
  106. client.capabilities[capability] = true
  107. }
  108. client.Send(nil, server.name, "CAP", client.nick, "ACK", capString)
  109. case "END":
  110. if !client.registered {
  111. client.capState = CapNegotiated
  112. server.tryRegister(client)
  113. }
  114. default:
  115. client.Send(nil, server.name, ERR_INVALIDCAPCMD, client.nick, subCommand, "Invalid CAP subcommand")
  116. }
  117. return false
  118. }