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.

channelmanager.go 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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. channel.Join(client, key, isSajoin, rb)
  120. cm.maybeCleanup(channel, true)
  121. return nil
  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. var channel *Channel
  165. cfname, err := CasefoldChannel(channelName)
  166. if err != nil {
  167. return err
  168. }
  169. var entry *channelManagerEntry
  170. defer func() {
  171. if err == nil && channel != nil {
  172. // registration was successful: make the database reflect it
  173. err = channel.Store(IncludeAllAttrs)
  174. }
  175. }()
  176. cm.Lock()
  177. defer cm.Unlock()
  178. entry = cm.chans[cfname]
  179. if entry == nil {
  180. return errNoSuchChannel
  181. }
  182. channel = entry.channel
  183. err = channel.SetRegistered(account)
  184. if err != nil {
  185. return err
  186. }
  187. cm.registeredChannels.Add(cfname)
  188. if skel, err := Skeleton(channel.Name()); err == nil {
  189. cm.registeredSkeletons.Add(skel)
  190. }
  191. return nil
  192. }
  193. func (cm *ChannelManager) SetUnregistered(channelName string, account string) (err error) {
  194. cfname, err := CasefoldChannel(channelName)
  195. if err != nil {
  196. return err
  197. }
  198. info, err := cm.server.channelRegistry.LoadChannel(cfname)
  199. if err != nil {
  200. return err
  201. }
  202. if info.Founder != account {
  203. return errChannelNotOwnedByAccount
  204. }
  205. defer func() {
  206. if err == nil {
  207. err = cm.server.channelRegistry.Delete(info)
  208. }
  209. }()
  210. cm.Lock()
  211. defer cm.Unlock()
  212. entry := cm.chans[cfname]
  213. if entry != nil {
  214. entry.channel.SetUnregistered(account)
  215. delete(cm.registeredChannels, cfname)
  216. if skel, err := Skeleton(entry.channel.Name()); err == nil {
  217. delete(cm.registeredSkeletons, skel)
  218. }
  219. }
  220. return nil
  221. }
  222. // Rename renames a channel (but does not notify the members)
  223. func (cm *ChannelManager) Rename(name string, newName string) (err error) {
  224. cfname, err := CasefoldChannel(name)
  225. if err != nil {
  226. return errNoSuchChannel
  227. }
  228. newCfname, err := CasefoldChannel(newName)
  229. if err != nil {
  230. return errInvalidChannelName
  231. }
  232. newSkeleton, err := Skeleton(newName)
  233. if err != nil {
  234. return errInvalidChannelName
  235. }
  236. var channel *Channel
  237. var info RegisteredChannel
  238. defer func() {
  239. if channel != nil && info.Founder != "" {
  240. channel.Store(IncludeAllAttrs)
  241. // we just flushed the channel under its new name, therefore this delete
  242. // cannot be overwritten by a write to the old name:
  243. cm.server.channelRegistry.Delete(info)
  244. }
  245. }()
  246. cm.Lock()
  247. defer cm.Unlock()
  248. if cm.chans[newCfname] != nil || cm.registeredChannels.Has(newCfname) {
  249. return errChannelNameInUse
  250. }
  251. if cm.chansSkeletons.Has(newSkeleton) || cm.registeredSkeletons.Has(newSkeleton) {
  252. return errChannelNameInUse
  253. }
  254. entry := cm.chans[cfname]
  255. if entry == nil || !entry.channel.IsLoaded() {
  256. return errNoSuchChannel
  257. }
  258. channel = entry.channel
  259. info = channel.ExportRegistration(IncludeInitial)
  260. registered := info.Founder != ""
  261. delete(cm.chans, cfname)
  262. cm.chans[newCfname] = entry
  263. if registered {
  264. delete(cm.registeredChannels, cfname)
  265. if oldSkeleton, err := Skeleton(info.Name); err == nil {
  266. delete(cm.registeredSkeletons, oldSkeleton)
  267. }
  268. cm.registeredChannels.Add(newCfname)
  269. cm.registeredSkeletons.Add(newSkeleton)
  270. } else {
  271. delete(cm.chansSkeletons, entry.skeleton)
  272. cm.chansSkeletons.Add(newSkeleton)
  273. entry.skeleton = newSkeleton
  274. cm.chans[cfname] = entry
  275. }
  276. entry.channel.Rename(newName, newCfname)
  277. return nil
  278. }
  279. // Len returns the number of channels
  280. func (cm *ChannelManager) Len() int {
  281. cm.RLock()
  282. defer cm.RUnlock()
  283. return len(cm.chans)
  284. }
  285. // Channels returns a slice containing all current channels
  286. func (cm *ChannelManager) Channels() (result []*Channel) {
  287. cm.RLock()
  288. defer cm.RUnlock()
  289. result = make([]*Channel, 0, len(cm.chans))
  290. for _, entry := range cm.chans {
  291. if entry.channel.IsLoaded() {
  292. result = append(result, entry.channel)
  293. }
  294. }
  295. return
  296. }
  297. // Purge marks a channel as purged.
  298. func (cm *ChannelManager) Purge(chname string, record ChannelPurgeRecord) (err error) {
  299. chname, err = CasefoldChannel(chname)
  300. if err != nil {
  301. return errInvalidChannelName
  302. }
  303. cm.Lock()
  304. cm.purgedChannels.Add(chname)
  305. cm.Unlock()
  306. cm.server.channelRegistry.PurgeChannel(chname, record)
  307. return nil
  308. }
  309. // IsPurged queries whether a channel is purged.
  310. func (cm *ChannelManager) IsPurged(chname string) (result bool) {
  311. chname, err := CasefoldChannel(chname)
  312. if err != nil {
  313. return false
  314. }
  315. cm.RLock()
  316. result = cm.purgedChannels.Has(chname)
  317. cm.RUnlock()
  318. return
  319. }
  320. // Unpurge deletes a channel's purged status.
  321. func (cm *ChannelManager) Unpurge(chname string) (err error) {
  322. chname, err = CasefoldChannel(chname)
  323. if err != nil {
  324. return errNoSuchChannel
  325. }
  326. cm.Lock()
  327. found := cm.purgedChannels.Has(chname)
  328. delete(cm.purgedChannels, chname)
  329. cm.Unlock()
  330. cm.server.channelRegistry.UnpurgeChannel(chname)
  331. if !found {
  332. return errNoSuchChannel
  333. }
  334. return nil
  335. }