Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

capability.go 3.4KB

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