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.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  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. MultiPrefix Capability = "multi-prefix"
  13. SASL Capability = "sasl"
  14. )
  15. var (
  16. SupportedCapabilities = CapabilitySet{
  17. MultiPrefix: true,
  18. }
  19. )
  20. func (capability Capability) String() string {
  21. return string(capability)
  22. }
  23. // CapModifiers are indicators showing the state of a capability after a REQ or
  24. // ACK.
  25. type CapModifier rune
  26. const (
  27. Ack CapModifier = '~'
  28. Disable CapModifier = '-'
  29. Sticky CapModifier = '='
  30. )
  31. func (mod CapModifier) String() string {
  32. return string(mod)
  33. }
  34. type CapState uint
  35. const (
  36. CapNone CapState = iota
  37. CapNegotiating CapState = iota
  38. CapNegotiated CapState = iota
  39. )
  40. type CapabilitySet map[Capability]bool
  41. func (set CapabilitySet) String() string {
  42. strs := make([]string, len(set))
  43. index := 0
  44. for capability := range set {
  45. strs[index] = string(capability)
  46. index += 1
  47. }
  48. return strings.Join(strs, " ")
  49. }
  50. func (set CapabilitySet) DisableString() string {
  51. parts := make([]string, len(set))
  52. index := 0
  53. for capability := range set {
  54. parts[index] = Disable.String() + capability.String()
  55. index += 1
  56. }
  57. return strings.Join(parts, " ")
  58. }
  59. // CAP <subcmd> [<caps>]
  60. func capHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  61. subCommand := strings.ToUpper(msg.Params[0])
  62. capabilities := make(CapabilitySet)
  63. var capString string
  64. if len(msg.Params) > 1 {
  65. capString = msg.Params[1]
  66. strs := strings.Split(capString, " ")
  67. for _, str := range strs {
  68. if len(str) > 0 {
  69. capabilities[Capability(str)] = true
  70. }
  71. }
  72. }
  73. switch subCommand {
  74. case "LS":
  75. if !client.registered {
  76. client.capState = CapNegotiating
  77. }
  78. // client.server needs to be here to workaround a parsing bug in weechat 1.4
  79. // and let it connect to the server (otherwise it doesn't respond to the CAP
  80. // message with anything and just hangs on connection)
  81. client.Send(nil, server.nameString, "CAP", client.nickString, subCommand, SupportedCapabilities.String())
  82. case "LIST":
  83. client.Send(nil, server.nameString, "CAP", client.nickString, subCommand, client.capabilities.String())
  84. case "REQ":
  85. // make sure all capabilities actually exist
  86. for capability := range capabilities {
  87. if !SupportedCapabilities[capability] {
  88. client.Send(nil, server.nameString, "CAP", client.nickString, "NAK", capString)
  89. return false
  90. }
  91. }
  92. for capability := range capabilities {
  93. client.capabilities[capability] = true
  94. }
  95. client.Send(nil, server.nameString, "CAP", client.nickString, "ACK", capString)
  96. case "END":
  97. if !client.registered {
  98. client.capState = CapNegotiated
  99. server.tryRegister(client)
  100. }
  101. default:
  102. client.Send(nil, server.nameString, ERR_INVALIDCAPCMD, client.nickString, subCommand, "Invalid CAP subcommand")
  103. }
  104. return false
  105. }