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.

chanserv.go 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "bytes"
  6. "fmt"
  7. "hash/crc32"
  8. "sort"
  9. "strconv"
  10. "strings"
  11. "time"
  12. "github.com/goshuirc/irc-go/ircfmt"
  13. "github.com/oragono/oragono/irc/modes"
  14. "github.com/oragono/oragono/irc/sno"
  15. "github.com/oragono/oragono/irc/utils"
  16. )
  17. const chanservHelp = `ChanServ lets you register and manage channels.
  18. To see in-depth help for a specific ChanServ command, try:
  19. $b/CS HELP <command>$b
  20. Here are the commands you can use:
  21. %s`
  22. func chanregEnabled(server *Server) bool {
  23. return server.ChannelRegistrationEnabled()
  24. }
  25. var (
  26. chanservCommands = map[string]*serviceCommand{
  27. "op": {
  28. handler: csOpHandler,
  29. help: `Syntax: $bOP #channel [nickname]$b
  30. OP makes the given nickname, or yourself, a channel admin. You can only use
  31. this command if you're the founder of the channel.`,
  32. helpShort: `$bOP$b makes the given user (or yourself) a channel admin.`,
  33. authRequired: true,
  34. enabled: chanregEnabled,
  35. },
  36. "register": {
  37. handler: csRegisterHandler,
  38. help: `Syntax: $bREGISTER #channel$b
  39. REGISTER lets you own the given channel. If you rejoin this channel, you'll be
  40. given admin privs on it. Modes set on the channel and the topic will also be
  41. remembered.`,
  42. helpShort: `$bREGISTER$b lets you own a given channel.`,
  43. authRequired: true,
  44. enabled: chanregEnabled,
  45. },
  46. "unregister": {
  47. handler: csUnregisterHandler,
  48. help: `Syntax: $bUNREGISTER #channel [code]$b
  49. UNREGISTER deletes a channel registration, allowing someone else to claim it.
  50. To prevent accidental unregistrations, a verification code is required;
  51. invoking the command without a code will display the necessary code.`,
  52. helpShort: `$bUNREGISTER$b deletes a channel registration.`,
  53. enabled: chanregEnabled,
  54. },
  55. "amode": {
  56. handler: csAmodeHandler,
  57. help: `Syntax: $bAMODE #channel [mode change] [account]$b
  58. AMODE lists or modifies persistent mode settings that affect channel members.
  59. For example, $bAMODE #channel +o dan$b grants the the holder of the "dan"
  60. account the +o operator mode every time they join #channel. To list current
  61. accounts and modes, use $bAMODE #channel$b. Note that users are always
  62. referenced by their registered account names, not their nicknames.`,
  63. helpShort: `$bAMODE$b modifies persistent mode settings for channel members.`,
  64. enabled: chanregEnabled,
  65. },
  66. }
  67. )
  68. // csNotice sends the client a notice from ChanServ
  69. func csNotice(rb *ResponseBuffer, text string) {
  70. rb.Add(nil, "ChanServ", "NOTICE", rb.target.Nick(), text)
  71. }
  72. func csAmodeHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  73. channelName, modeChange := utils.ExtractParam(params)
  74. channel := server.channels.Get(channelName)
  75. if channel == nil {
  76. csNotice(rb, client.t("Channel does not exist"))
  77. return
  78. } else if channel.Founder() == "" {
  79. csNotice(rb, client.t("Channel is not registered"))
  80. return
  81. }
  82. modeChanges, unknown := modes.ParseChannelModeChanges(strings.Fields(modeChange)...)
  83. var change modes.ModeChange
  84. if len(modeChanges) > 1 || len(unknown) > 0 {
  85. csNotice(rb, client.t("Invalid mode change"))
  86. return
  87. } else if len(modeChanges) == 1 {
  88. change = modeChanges[0]
  89. } else {
  90. change = modes.ModeChange{Op: modes.List}
  91. }
  92. // normalize and validate the account argument
  93. accountIsValid := false
  94. change.Arg, _ = CasefoldName(change.Arg)
  95. switch change.Op {
  96. case modes.List:
  97. accountIsValid = true
  98. case modes.Add:
  99. // if we're adding a mode, the account must exist
  100. if change.Arg != "" {
  101. _, err := server.accounts.LoadAccount(change.Arg)
  102. accountIsValid = (err == nil)
  103. }
  104. case modes.Remove:
  105. // allow removal of accounts that may have been deleted
  106. accountIsValid = (change.Arg != "")
  107. }
  108. if !accountIsValid {
  109. csNotice(rb, client.t("Account does not exist"))
  110. return
  111. }
  112. affectedModes, err := channel.ProcessAccountToUmodeChange(client, change)
  113. if err == errInsufficientPrivs {
  114. csNotice(rb, client.t("Insufficient privileges"))
  115. return
  116. } else if err != nil {
  117. csNotice(rb, client.t("Internal error"))
  118. return
  119. }
  120. switch change.Op {
  121. case modes.List:
  122. // sort the persistent modes in descending order of priority
  123. sort.Slice(affectedModes, func(i, j int) bool {
  124. return umodeGreaterThan(affectedModes[i].Mode, affectedModes[j].Mode)
  125. })
  126. csNotice(rb, fmt.Sprintf(client.t("Channel %s has %d persistent modes set"), channelName, len(affectedModes)))
  127. for _, modeChange := range affectedModes {
  128. csNotice(rb, fmt.Sprintf(client.t("Account %s receives mode +%s"), modeChange.Arg, string(modeChange.Mode)))
  129. }
  130. case modes.Add, modes.Remove:
  131. if len(affectedModes) > 0 {
  132. csNotice(rb, fmt.Sprintf(client.t("Successfully set mode %s"), change.String()))
  133. } else {
  134. csNotice(rb, client.t("Change was a no-op"))
  135. }
  136. }
  137. }
  138. func csOpHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  139. channelName, clientToOp := utils.ExtractParam(params)
  140. if channelName == "" {
  141. csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bOP #channel [nickname]$b")))
  142. return
  143. }
  144. clientToOp = strings.TrimSpace(clientToOp)
  145. channelKey, err := CasefoldChannel(channelName)
  146. if err != nil {
  147. csNotice(rb, client.t("Channel name is not valid"))
  148. return
  149. }
  150. channelInfo := server.channels.Get(channelKey)
  151. if channelInfo == nil {
  152. csNotice(rb, client.t("Channel does not exist"))
  153. return
  154. }
  155. clientAccount := client.Account()
  156. if clientAccount == "" || clientAccount != channelInfo.Founder() {
  157. csNotice(rb, client.t("You must be the channel founder to op"))
  158. return
  159. }
  160. var target *Client
  161. if clientToOp != "" {
  162. casefoldedNickname, err := CasefoldName(clientToOp)
  163. target = server.clients.Get(casefoldedNickname)
  164. if err != nil || target == nil {
  165. csNotice(rb, client.t("Could not find given client"))
  166. return
  167. }
  168. } else {
  169. target = client
  170. }
  171. // give them privs
  172. givenMode := modes.ChannelOperator
  173. if client == target {
  174. givenMode = modes.ChannelFounder
  175. }
  176. change := channelInfo.applyModeToMember(target, givenMode, modes.Add, client.NickCasefolded(), rb)
  177. if change != nil {
  178. //TODO(dan): we should change the name of String and make it return a slice here
  179. //TODO(dan): unify this code with code in modes.go
  180. args := append([]string{channelName}, strings.Split(change.String(), " ")...)
  181. for _, member := range channelInfo.Members() {
  182. member.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "MODE", args...)
  183. }
  184. }
  185. csNotice(rb, fmt.Sprintf(client.t("Successfully op'd in channel %s"), channelName))
  186. server.logger.Info("chanserv", fmt.Sprintf("Client %s op'd [%s] in channel %s", client.nick, clientToOp, channelName))
  187. server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Client $c[grey][$r%s$c[grey]] CS OP'd $c[grey][$r%s$c[grey]] in channel $c[grey][$r%s$c[grey]]"), client.nickMaskString, clientToOp, channelName))
  188. }
  189. func csRegisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  190. channelName := strings.TrimSpace(params)
  191. if channelName == "" {
  192. csNotice(rb, ircfmt.Unescape(client.t("Syntax: $bREGISTER #channel$b")))
  193. return
  194. }
  195. channelKey, err := CasefoldChannel(channelName)
  196. if err != nil {
  197. csNotice(rb, client.t("Channel name is not valid"))
  198. return
  199. }
  200. channelInfo := server.channels.Get(channelKey)
  201. if channelInfo == nil || !channelInfo.ClientIsAtLeast(client, modes.ChannelOperator) {
  202. csNotice(rb, client.t("You must be an oper on the channel to register it"))
  203. return
  204. }
  205. // this provides the synchronization that allows exactly one registration of the channel:
  206. err = channelInfo.SetRegistered(client.Account())
  207. if err != nil {
  208. csNotice(rb, err.Error())
  209. return
  210. }
  211. // registration was successful: make the database reflect it
  212. go server.channelRegistry.StoreChannel(channelInfo, IncludeAllChannelAttrs)
  213. csNotice(rb, fmt.Sprintf(client.t("Channel %s successfully registered"), channelName))
  214. server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
  215. server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
  216. // give them founder privs
  217. change := channelInfo.applyModeToMember(client, modes.ChannelFounder, modes.Add, client.NickCasefolded(), rb)
  218. if change != nil {
  219. //TODO(dan): we should change the name of String and make it return a slice here
  220. //TODO(dan): unify this code with code in modes.go
  221. args := append([]string{channelName}, strings.Split(change.String(), " ")...)
  222. for _, member := range channelInfo.Members() {
  223. member.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "MODE", args...)
  224. }
  225. }
  226. }
  227. func csUnregisterHandler(server *Server, client *Client, command, params string, rb *ResponseBuffer) {
  228. channelName, verificationCode := utils.ExtractParam(params)
  229. channelKey, err := CasefoldChannel(channelName)
  230. if channelKey == "" || err != nil {
  231. csNotice(rb, client.t("Channel name is not valid"))
  232. return
  233. }
  234. channel := server.channels.Get(channelKey)
  235. if channel == nil {
  236. csNotice(rb, client.t("No such channel"))
  237. return
  238. }
  239. hasPrivs := client.HasRoleCapabs("chanreg")
  240. if !hasPrivs {
  241. founder := channel.Founder()
  242. hasPrivs = founder != "" && founder == client.Account()
  243. }
  244. if !hasPrivs {
  245. csNotice(rb, client.t("Insufficient privileges"))
  246. return
  247. }
  248. info := channel.ExportRegistration(0)
  249. expectedCode := unregisterConfirmationCode(info.Name, info.RegisteredAt)
  250. if expectedCode != verificationCode {
  251. csNotice(rb, ircfmt.Unescape(client.t("$bWarning: unregistering this channel will remove all stored channel attributes.$b")))
  252. csNotice(rb, fmt.Sprintf(client.t("To confirm channel unregistration, type: /CS UNREGISTER %s %s"), channelKey, expectedCode))
  253. return
  254. }
  255. channel.SetUnregistered()
  256. go server.channelRegistry.Delete(channelKey, info)
  257. csNotice(rb, fmt.Sprintf(client.t("Channel %s is now unregistered"), channelKey))
  258. }
  259. // deterministically generates a confirmation code for unregistering a channel / account
  260. func unregisterConfirmationCode(name string, registeredAt time.Time) (code string) {
  261. var codeInput bytes.Buffer
  262. codeInput.WriteString(name)
  263. codeInput.WriteString(strconv.FormatInt(registeredAt.Unix(), 16))
  264. return strconv.Itoa(int(crc32.ChecksumIEEE(codeInput.Bytes())))
  265. }