Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

channelmanager.go 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. // Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "sync"
  6. )
  7. type channelManagerEntry struct {
  8. channel *Channel
  9. // this is a refcount for joins, so we can avoid a race where we incorrectly
  10. // think the channel is empty (without holding a lock across the entire Channel.Join()
  11. // call)
  12. pendingJoins int
  13. skeleton string
  14. }
  15. // ChannelManager keeps track of all the channels on the server,
  16. // providing synchronization for creation of new channels on first join,
  17. // cleanup of empty channels on last part, and renames.
  18. type ChannelManager struct {
  19. sync.RWMutex // tier 2
  20. // chans is the main data structure, mapping casefolded name -> *Channel
  21. chans map[string]*channelManagerEntry
  22. chansSkeletons StringSet // skeletons of *unregistered* chans
  23. registeredChannels StringSet // casefolds of registered chans
  24. registeredSkeletons StringSet // skeletons of registered chans
  25. purgedChannels StringSet // casefolds of purged chans
  26. server *Server
  27. }
  28. // NewChannelManager returns a new ChannelManager.
  29. func (cm *ChannelManager) Initialize(server *Server) {
  30. cm.chans = make(map[string]*channelManagerEntry)
  31. cm.chansSkeletons = make(StringSet)
  32. cm.server = server
  33. cm.loadRegisteredChannels(server.Config())
  34. // purging should work even if registration is disabled
  35. cm.purgedChannels = cm.server.channelRegistry.PurgedChannels()
  36. }
  37. func (cm *ChannelManager) loadRegisteredChannels(config *Config) {
  38. if !config.Channels.Registration.Enabled {
  39. return
  40. }
  41. rawNames := cm.server.channelRegistry.AllChannels()
  42. registeredChannels := make(StringSet, len(rawNames))
  43. registeredSkeletons := make(StringSet, len(rawNames))
  44. for _, name := range rawNames {
  45. cfname, err := CasefoldChannel(name)
  46. if err == nil {
  47. registeredChannels.Add(cfname)
  48. }
  49. skeleton, err := Skeleton(name)
  50. if err == nil {
  51. registeredSkeletons.Add(skeleton)
  52. }
  53. }
  54. cm.Lock()
  55. defer cm.Unlock()
  56. cm.registeredChannels = registeredChannels
  57. cm.registeredSkeletons = registeredSkeletons
  58. }
  59. // Get returns an existing channel with name equivalent to `name`, or nil
  60. func (cm *ChannelManager) Get(name string) (channel *Channel) {
  61. name, err := CasefoldChannel(name)
  62. if err == nil {
  63. cm.RLock()
  64. defer cm.RUnlock()
  65. entry := cm.chans[name]
  66. // if the channel is still loading, pretend we don't have it
  67. if entry != nil && entry.channel.IsLoaded() {
  68. return entry.channel
  69. }
  70. }
  71. return nil
  72. }
  73. // Join causes `client` to join the channel named `name`, creating it if necessary.
  74. func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin bool, rb *ResponseBuffer) error {
  75. server := client.server
  76. casefoldedName, err := CasefoldChannel(name)
  77. skeleton, skerr := Skeleton(name)
  78. if err != nil || skerr != nil || len(casefoldedName) > server.Config().Limits.ChannelLen {
  79. return errNoSuchChannel
  80. }
  81. channel, err := func() (*Channel, error) {
  82. cm.Lock()
  83. defer cm.Unlock()
  84. if cm.purgedChannels.Has(casefoldedName) {
  85. return nil, errChannelPurged
  86. }
  87. entry := cm.chans[casefoldedName]
  88. if entry == nil {
  89. registered := cm.registeredChannels.Has(casefoldedName)
  90. // enforce OpOnlyCreation
  91. if !registered && server.Config().Channels.OpOnlyCreation && !client.HasRoleCapabs("chanreg") {
  92. return nil, errInsufficientPrivs
  93. }
  94. // enforce confusables
  95. if cm.chansSkeletons.Has(skeleton) || (!registered && cm.registeredSkeletons.Has(skeleton)) {
  96. return nil, errConfusableIdentifier
  97. }
  98. entry = &channelManagerEntry{
  99. channel: NewChannel(server, name, casefoldedName, registered),
  100. pendingJoins: 0,
  101. }
  102. if !registered {
  103. // for an unregistered channel, we already have the correct unfolded name
  104. // and therefore the final skeleton. for a registered channel, we don't have
  105. // the unfolded name yet (it needs to be loaded from the db), but we already
  106. // have the final skeleton in `registeredSkeletons` so we don't need to track it
  107. cm.chansSkeletons.Add(skeleton)
  108. entry.skeleton = skeleton
  109. }
  110. cm.chans[casefoldedName] = entry
  111. }
  112. entry.pendingJoins += 1
  113. return entry.channel, nil
  114. }()
  115. if err != nil {
  116. return err
  117. }
  118. channel.EnsureLoaded()
  119. err = channel.Join(client, key, isSajoin, rb)
  120. cm.maybeCleanup(channel, true)
  121. return err
  122. }
  123. func (cm *ChannelManager) maybeCleanup(channel *Channel, afterJoin bool) {
  124. cm.Lock()
  125. defer cm.Unlock()
  126. cfname := channel.NameCasefolded()
  127. entry := cm.chans[cfname]
  128. if entry == nil || entry.channel != channel {
  129. return
  130. }
  131. if afterJoin {
  132. entry.pendingJoins -= 1
  133. }
  134. if entry.pendingJoins == 0 && entry.channel.IsClean() {
  135. delete(cm.chans, cfname)
  136. if entry.skeleton != "" {
  137. delete(cm.chansSkeletons, entry.skeleton)
  138. }
  139. }
  140. }
  141. // Part parts `client` from the channel named `name`, deleting it if it's empty.
  142. func (cm *ChannelManager) Part(client *Client, name string, message string, rb *ResponseBuffer) error {
  143. var channel *Channel
  144. casefoldedName, err := CasefoldChannel(name)
  145. if err != nil {
  146. return errNoSuchChannel
  147. }
  148. cm.RLock()
  149. entry := cm.chans[casefoldedName]
  150. if entry != nil {
  151. channel = entry.channel
  152. }
  153. cm.RUnlock()
  154. if channel == nil {
  155. return errNoSuchChannel
  156. }
  157. channel.Part(client, message, rb)
  158. return nil
  159. }
  160. func (cm *ChannelManager) Cleanup(channel *Channel) {
  161. cm.maybeCleanup(channel, false)
  162. }
  163. func (cm *ChannelManager) SetRegistered(channelName string, account string) (err error) {
  164. if cm.server.Defcon() <= 4 {
  165. return errFeatureDisabled
  166. }
  167. var channel *Channel
  168. cfname, err := CasefoldChannel(channelName)
  169. if err != nil {
  170. return err
  171. }
  172. var entry *channelManagerEntry
  173. defer func() {
  174. if err == nil && channel != nil {
  175. // registration was successful: make the database reflect it
  176. err = channel.Store(IncludeAllAttrs)
  177. }
  178. }()
  179. cm.Lock()
  180. defer cm.Unlock()
  181. entry = cm.chans[cfname]
  182. if entry == nil {
  183. return errNoSuchChannel
  184. }
  185. channel = entry.channel
  186. err = channel.SetRegistered(account)
  187. if err != nil {
  188. return err
  189. }
  190. cm.registeredChannels.Add(cfname)
  191. if skel, err := Skeleton(channel.Name()); err == nil {
  192. cm.registeredSkeletons.Add(skel)
  193. }
  194. return nil
  195. }
  196. func (cm *ChannelManager) SetUnregistered(channelName string, account string) (err error) {
  197. cfname, err := CasefoldChannel(channelName)
  198. if err != nil {
  199. return err
  200. }
  201. info, err := cm.server.channelRegistry.LoadChannel(cfname)
  202. if err != nil {
  203. return err
  204. }
  205. if info.Founder != account {
  206. return errChannelNotOwnedByAccount
  207. }
  208. defer func() {
  209. if err == nil {
  210. err = cm.server.channelRegistry.Delete(info)
  211. }
  212. }()
  213. cm.Lock()
  214. defer cm.Unlock()
  215. entry := cm.chans[cfname]
  216. if entry != nil {
  217. entry.channel.SetUnregistered(account)
  218. delete(cm.registeredChannels, cfname)
  219. if skel, err := Skeleton(entry.channel.Name()); err == nil {
  220. delete(cm.registeredSkeletons, skel)
  221. }
  222. }
  223. return nil
  224. }
  225. // Rename renames a channel (but does not notify the members)
  226. func (cm *ChannelManager) Rename(name string, newName string) (err error) {
  227. cfname, err := CasefoldChannel(name)
  228. if err != nil {
  229. return errNoSuchChannel
  230. }
  231. newCfname, err := CasefoldChannel(newName)
  232. if err != nil {
  233. return errInvalidChannelName
  234. }
  235. newSkeleton, err := Skeleton(newName)
  236. if err != nil {
  237. return errInvalidChannelName
  238. }
  239. var channel *Channel
  240. var info RegisteredChannel
  241. defer func() {
  242. if channel != nil && info.Founder != "" {
  243. channel.Store(IncludeAllAttrs)
  244. // we just flushed the channel under its new name, therefore this delete
  245. // cannot be overwritten by a write to the old name:
  246. cm.server.channelRegistry.Delete(info)
  247. }
  248. }()
  249. cm.Lock()
  250. defer cm.Unlock()
  251. if cm.chans[newCfname] != nil || cm.registeredChannels.Has(newCfname) {
  252. return errChannelNameInUse
  253. }
  254. if cm.chansSkeletons.Has(newSkeleton) || cm.registeredSkeletons.Has(newSkeleton) {
  255. return errChannelNameInUse
  256. }
  257. entry := cm.chans[cfname]
  258. if entry == nil || !entry.channel.IsLoaded() {
  259. return errNoSuchChannel
  260. }
  261. channel = entry.channel
  262. info = channel.ExportRegistration(IncludeInitial)
  263. registered := info.Founder != ""
  264. delete(cm.chans, cfname)
  265. cm.chans[newCfname] = entry
  266. if registered {
  267. delete(cm.registeredChannels, cfname)
  268. if oldSkeleton, err := Skeleton(info.Name); err == nil {
  269. delete(cm.registeredSkeletons, oldSkeleton)
  270. }
  271. cm.registeredChannels.Add(newCfname)
  272. cm.registeredSkeletons.Add(newSkeleton)
  273. } else {
  274. delete(cm.chansSkeletons, entry.skeleton)
  275. cm.chansSkeletons.Add(newSkeleton)
  276. entry.skeleton = newSkeleton
  277. cm.chans[cfname] = entry
  278. }
  279. entry.channel.Rename(newName, newCfname)
  280. return nil
  281. }
  282. // Len returns the number of channels
  283. func (cm *ChannelManager) Len() int {
  284. cm.RLock()
  285. defer cm.RUnlock()
  286. return len(cm.chans)
  287. }
  288. // Channels returns a slice containing all current channels
  289. func (cm *ChannelManager) Channels() (result []*Channel) {
  290. cm.RLock()
  291. defer cm.RUnlock()
  292. result = make([]*Channel, 0, len(cm.chans))
  293. for _, entry := range cm.chans {
  294. if entry.channel.IsLoaded() {
  295. result = append(result, entry.channel)
  296. }
  297. }
  298. return
  299. }
  300. // Purge marks a channel as purged.
  301. func (cm *ChannelManager) Purge(chname string, record ChannelPurgeRecord) (err error) {
  302. chname, err = CasefoldChannel(chname)
  303. if err != nil {
  304. return errInvalidChannelName
  305. }
  306. cm.Lock()
  307. cm.purgedChannels.Add(chname)
  308. cm.Unlock()
  309. cm.server.channelRegistry.PurgeChannel(chname, record)
  310. return nil
  311. }
  312. // IsPurged queries whether a channel is purged.
  313. func (cm *ChannelManager) IsPurged(chname string) (result bool) {
  314. chname, err := CasefoldChannel(chname)
  315. if err != nil {
  316. return false
  317. }
  318. cm.RLock()
  319. result = cm.purgedChannels.Has(chname)
  320. cm.RUnlock()
  321. return
  322. }
  323. // Unpurge deletes a channel's purged status.
  324. func (cm *ChannelManager) Unpurge(chname string) (err error) {
  325. chname, err = CasefoldChannel(chname)
  326. if err != nil {
  327. return errNoSuchChannel
  328. }
  329. cm.Lock()
  330. found := cm.purgedChannels.Has(chname)
  331. delete(cm.purgedChannels, chname)
  332. cm.Unlock()
  333. cm.server.channelRegistry.UnpurgeChannel(chname)
  334. if !found {
  335. return errNoSuchChannel
  336. }
  337. return nil
  338. }