Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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