Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

capability.go 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2016-2017 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. // Capability represents an optional feature that a client may request from the server.
  10. type Capability string
  11. const (
  12. AccountTag Capability = "account-tag"
  13. AccountNotify Capability = "account-notify"
  14. AwayNotify Capability = "away-notify"
  15. CapNotify Capability = "cap-notify"
  16. ChgHost Capability = "chghost"
  17. EchoMessage Capability = "echo-message"
  18. ExtendedJoin Capability = "extended-join"
  19. InviteNotify Capability = "invite-notify"
  20. MaxLine Capability = "draft/maxline"
  21. MessageIDs Capability = "draft/message-ids"
  22. MessageTags Capability = "draft/message-tags-0.2"
  23. MultiPrefix Capability = "multi-prefix"
  24. SASL Capability = "sasl"
  25. ServerTime Capability = "server-time"
  26. STS Capability = "draft/sts"
  27. UserhostInNames Capability = "userhost-in-names"
  28. )
  29. var (
  30. // SupportedCapabilities are the caps we advertise.
  31. SupportedCapabilities = CapabilitySet{
  32. AccountTag: true,
  33. AccountNotify: true,
  34. AwayNotify: true,
  35. CapNotify: true,
  36. ChgHost: true,
  37. EchoMessage: true,
  38. ExtendedJoin: true,
  39. InviteNotify: true,
  40. MessageIDs: true,
  41. // MaxLine is set during server startup
  42. MessageTags: true,
  43. MultiPrefix: true,
  44. // SASL is set during server startup
  45. ServerTime: true,
  46. // STS is set during server startup
  47. UserhostInNames: true,
  48. }
  49. // CapValues are the actual values we advertise to v3.2 clients.
  50. CapValues = map[Capability]string{
  51. SASL: "PLAIN,EXTERNAL",
  52. }
  53. )
  54. func (capability Capability) String() string {
  55. return string(capability)
  56. }
  57. // CapState shows whether we're negotiating caps, finished, etc for connection registration.
  58. type CapState uint
  59. const (
  60. // CapNone means CAP hasn't been negotiated at all.
  61. CapNone CapState = iota
  62. // CapNegotiating means CAP is being negotiated and registration should be paused.
  63. CapNegotiating CapState = iota
  64. // CapNegotiated means CAP negotiation has been successfully ended and reg should complete.
  65. CapNegotiated CapState = iota
  66. )
  67. // CapVersion is used to select which max version of CAP the client supports.
  68. type CapVersion uint
  69. const (
  70. // Cap301 refers to the base CAP spec.
  71. Cap301 CapVersion = 301
  72. // Cap302 refers to the IRCv3.2 CAP spec.
  73. Cap302 CapVersion = 302
  74. )
  75. // CapabilitySet is used to track supported, enabled, and existing caps.
  76. type CapabilitySet map[Capability]bool
  77. func (set CapabilitySet) String(version CapVersion) string {
  78. strs := make([]string, len(set))
  79. index := 0
  80. for capability := range set {
  81. capString := string(capability)
  82. if version == Cap302 {
  83. val, exists := CapValues[capability]
  84. if exists {
  85. capString += "=" + val
  86. }
  87. }
  88. strs[index] = capString
  89. index++
  90. }
  91. return strings.Join(strs, " ")
  92. }
  93. // CAP <subcmd> [<caps>]
  94. func capHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
  95. subCommand := strings.ToUpper(msg.Params[0])
  96. capabilities := make(CapabilitySet)
  97. var capString string
  98. if len(msg.Params) > 1 {
  99. capString = msg.Params[1]
  100. strs := strings.Split(capString, " ")
  101. for _, str := range strs {
  102. if len(str) > 0 {
  103. capabilities[Capability(str)] = true
  104. }
  105. }
  106. }
  107. switch subCommand {
  108. case "LS":
  109. if !client.registered {
  110. client.capState = CapNegotiating
  111. }
  112. if len(msg.Params) > 1 && msg.Params[1] == "302" {
  113. client.capVersion = 302
  114. }
  115. // weechat 1.4 has a bug here where it won't accept the CAP reply unless it contains
  116. // the server.name source... otherwise it doesn't respond to the CAP message with
  117. // anything and just hangs on connection.
  118. //TODO(dan): limit number of caps and send it multiline in 3.2 style as appropriate.
  119. client.Send(nil, server.name, "CAP", client.nick, subCommand, SupportedCapabilities.String(client.capVersion))
  120. case "LIST":
  121. client.Send(nil, server.name, "CAP", client.nick, subCommand, client.capabilities.String(Cap301)) // values not sent on LIST so force 3.1
  122. case "REQ":
  123. // make sure all capabilities actually exist
  124. for capability := range capabilities {
  125. if !SupportedCapabilities[capability] {
  126. client.Send(nil, server.name, "CAP", client.nick, "NAK", capString)
  127. return false
  128. }
  129. }
  130. for capability := range capabilities {
  131. client.capabilities[capability] = true
  132. }
  133. client.Send(nil, server.name, "CAP", client.nick, "ACK", capString)
  134. case "END":
  135. if !client.registered {
  136. client.capState = CapNegotiated
  137. server.tryRegister(client)
  138. }
  139. default:
  140. client.Send(nil, server.name, ERR_INVALIDCAPCMD, client.nick, subCommand, "Invalid CAP subcommand")
  141. }
  142. return false
  143. }