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.
 
 
 
 
 
 

329 lines
7.7 KiB

  1. package main
  2. import (
  3. "crypto/rand"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "math/big"
  8. "strings"
  9. "sync"
  10. "time"
  11. "github.com/gorilla/sessions"
  12. "github.com/zorchenhimer/MovieNight/common"
  13. )
  14. var settings *Settings
  15. var sstore *sessions.CookieStore
  16. type Settings struct {
  17. // Non-Saved settings
  18. filename string
  19. cmdLineKey string // stream key from the command line
  20. // Saved settings
  21. AdminPassword string
  22. ApprovedEmotes []string // list of channels that have been approved for emote use. Global emotes are always "approved".
  23. Bans []BanInfo
  24. LetThemLurk bool // whether or not to announce users joining/leaving chat
  25. ListenAddress string
  26. LogFile string
  27. LogLevel common.LogLevel
  28. MaxMessageCount int
  29. NewPin bool // Auto generate a new pin on start. Overwrites RoomAccessPin if set.
  30. PageTitle string // primary value for the page <title> element
  31. RegenAdminPass bool // regenerate admin password on start?
  32. RoomAccess AccessMode
  33. RoomAccessPin string // The current pin
  34. RtmpListenAddress string // host:port that the RTMP server listens on
  35. SessionKey string // key for session data
  36. StreamKey string
  37. StreamStats bool
  38. TitleLength int // maximum length of the title that can be set with the /playing
  39. TwitchClientID string // client id from twitch developers portal
  40. TwitchClientSecret string // OAuth from twitch developers portal: https://dev.twitch.tv/docs/authentication/getting-tokens-oauth#oauth-client-credentials-flow
  41. WrappedEmotesOnly bool // only allow "wrapped" emotes. eg :Kappa: and [Kappa] but not Kappa
  42. // Rate limiting stuff, in seconds
  43. RateLimitChat time.Duration
  44. RateLimitNick time.Duration
  45. RateLimitColor time.Duration
  46. RateLimitAuth time.Duration
  47. RateLimitDuplicate time.Duration // Amount of seconds between allowed duplicate messages
  48. // Send the NoCache header?
  49. NoCache bool
  50. lock sync.RWMutex
  51. }
  52. type AccessMode string
  53. const (
  54. AccessOpen AccessMode = "open"
  55. AccessPin AccessMode = "pin"
  56. AccessRequest AccessMode = "request"
  57. )
  58. type BanInfo struct {
  59. IP string
  60. Names []string
  61. When time.Time
  62. }
  63. func LoadSettings(filename string) (*Settings, error) {
  64. raw, err := ioutil.ReadFile(filename)
  65. if err != nil {
  66. return nil, fmt.Errorf("error reading file: %s", err)
  67. }
  68. var s *Settings
  69. err = json.Unmarshal(raw, &s)
  70. if err != nil {
  71. return nil, fmt.Errorf("error unmarshaling: %s", err)
  72. }
  73. s.filename = filename
  74. if err = common.SetupLogging(s.LogLevel, s.LogFile); err != nil {
  75. return nil, fmt.Errorf("Unable to setup logger: %s", err)
  76. }
  77. // have a default of 200
  78. if s.MaxMessageCount == 0 {
  79. s.MaxMessageCount = 300
  80. } else if s.MaxMessageCount < 0 {
  81. return s, fmt.Errorf("value for MaxMessageCount must be greater than 0, given %d", s.MaxMessageCount)
  82. }
  83. if s.RegenAdminPass == true || s.AdminPassword == "" {
  84. s.AdminPassword, err = generatePass(time.Now().Unix())
  85. if err != nil {
  86. return nil, fmt.Errorf("unable to generate admin password: %s", err)
  87. }
  88. }
  89. // Set to -1 to reset
  90. if s.RateLimitChat == -1 {
  91. s.RateLimitChat = 1
  92. } else if s.RateLimitChat < 0 {
  93. s.RateLimitChat = 0
  94. }
  95. if s.RateLimitNick == -1 {
  96. s.RateLimitNick = 300
  97. } else if s.RateLimitNick < 0 {
  98. s.RateLimitNick = 0
  99. }
  100. if s.RateLimitColor == -1 {
  101. s.RateLimitColor = 60
  102. } else if s.RateLimitColor < 0 {
  103. s.RateLimitColor = 0
  104. }
  105. if s.RateLimitAuth == -1 {
  106. s.RateLimitAuth = 5
  107. } else if s.RateLimitAuth < 0 {
  108. common.LogInfoln("It's not recommended to disable the authentication rate limit.")
  109. s.RateLimitAuth = 0
  110. }
  111. if s.RateLimitDuplicate == -1 {
  112. s.RateLimitDuplicate = 30
  113. } else if s.RateLimitDuplicate < 0 {
  114. s.RateLimitDuplicate = 0
  115. }
  116. if s.WrappedEmotesOnly {
  117. common.LogInfoln("Only allowing wrapped emotes")
  118. common.WrappedEmotesOnly = true
  119. }
  120. // Print this stuff before we multiply it by time.Second
  121. common.LogInfof("RateLimitChat: %v", s.RateLimitChat)
  122. common.LogInfof("RateLimitNick: %v", s.RateLimitNick)
  123. common.LogInfof("RateLimitColor: %v", s.RateLimitColor)
  124. common.LogInfof("RateLimitAuth: %v", s.RateLimitAuth)
  125. if len(s.RoomAccess) == 0 {
  126. s.RoomAccess = AccessOpen
  127. }
  128. if (s.RoomAccess != AccessOpen && len(s.RoomAccessPin) == 0) || s.NewPin {
  129. pin, err := s.generateNewPin()
  130. if err != nil {
  131. common.LogErrorf("Unable to generate new pin: %v", err)
  132. }
  133. common.LogInfof("New pin generated: %s", pin)
  134. }
  135. // Don't use LogInfof() here. Log isn't setup yet when LoadSettings() is called from init().
  136. fmt.Printf("Settings reloaded. New admin password: %s\n", s.AdminPassword)
  137. if s.TitleLength <= 0 {
  138. s.TitleLength = 50
  139. }
  140. // Is this a good way to do this? Probably not...
  141. if len(s.SessionKey) == 0 {
  142. out := ""
  143. large := big.NewInt(int64(1 << 60))
  144. large = large.Add(large, large)
  145. for len(out) < 50 {
  146. num, err := rand.Int(rand.Reader, large)
  147. if err != nil {
  148. panic("Error generating session key: " + err.Error())
  149. }
  150. out = fmt.Sprintf("%s%X", out, num)
  151. }
  152. s.SessionKey = out
  153. }
  154. // Save admin password to file
  155. if err = s.Save(); err != nil {
  156. return nil, fmt.Errorf("Unable to save settings: %s", err)
  157. }
  158. return s, nil
  159. }
  160. func generatePass(seed int64) (string, error) {
  161. out := ""
  162. for len(out) < 20 {
  163. num, err := rand.Int(rand.Reader, big.NewInt(int64(15)))
  164. if err != nil {
  165. return "", err
  166. }
  167. out = fmt.Sprintf("%s%X", out, num)
  168. }
  169. return out, nil
  170. }
  171. func (s *Settings) Save() error {
  172. defer s.lock.Unlock()
  173. s.lock.Lock()
  174. return s.unlockedSave()
  175. }
  176. // unlockedSave expects the calling function to lock the RWMutex
  177. func (s *Settings) unlockedSave() error {
  178. marshaled, err := json.MarshalIndent(s, "", "\t")
  179. if err != nil {
  180. return fmt.Errorf("error marshaling: %s", err)
  181. }
  182. err = ioutil.WriteFile(s.filename, marshaled, 0777)
  183. if err != nil {
  184. return fmt.Errorf("error saving: %s", err)
  185. }
  186. return nil
  187. }
  188. func (s *Settings) AddBan(host string, names []string) error {
  189. defer s.lock.Unlock()
  190. s.lock.Lock()
  191. if host == "127.0.0.1" {
  192. return fmt.Errorf("Cannot add a ban for localhost.")
  193. }
  194. b := BanInfo{
  195. Names: names,
  196. IP: host,
  197. When: time.Now(),
  198. }
  199. s.Bans = append(s.Bans, b)
  200. common.LogInfof("[BAN] %q (%s) has been banned.\n", strings.Join(names, ", "), host)
  201. return s.unlockedSave()
  202. }
  203. func (s *Settings) RemoveBan(name string) error {
  204. defer s.lock.Unlock()
  205. s.lock.Lock()
  206. name = strings.ToLower(name)
  207. newBans := []BanInfo{}
  208. for _, b := range s.Bans {
  209. for _, n := range b.Names {
  210. if n == name {
  211. common.LogInfof("[ban] Removed ban for %s [%s]\n", b.IP, n)
  212. } else {
  213. newBans = append(newBans, b)
  214. }
  215. }
  216. }
  217. s.Bans = newBans
  218. return s.unlockedSave()
  219. }
  220. func (s *Settings) IsBanned(host string) (bool, []string) {
  221. defer s.lock.RUnlock()
  222. s.lock.RLock()
  223. for _, b := range s.Bans {
  224. if b.IP == host {
  225. return true, b.Names
  226. }
  227. }
  228. return false, nil
  229. }
  230. func (s *Settings) SetTempKey(key string) {
  231. defer s.lock.Unlock()
  232. s.lock.Lock()
  233. s.cmdLineKey = key
  234. }
  235. func (s *Settings) GetStreamKey() string {
  236. defer s.lock.RUnlock()
  237. s.lock.RLock()
  238. if len(s.cmdLineKey) > 0 {
  239. return s.cmdLineKey
  240. }
  241. return s.StreamKey
  242. }
  243. func (s *Settings) generateNewPin() (string, error) {
  244. defer s.lock.Unlock()
  245. s.lock.Lock()
  246. num, err := rand.Int(rand.Reader, big.NewInt(int64(9999)))
  247. if err != nil {
  248. return "", err
  249. }
  250. s.RoomAccessPin = fmt.Sprintf("%04d", num)
  251. if err = s.unlockedSave(); err != nil {
  252. return "", err
  253. }
  254. return s.RoomAccessPin, nil
  255. }
  256. func (s *Settings) AddApprovedEmotes(channels []string) error {
  257. defer s.lock.Unlock()
  258. s.lock.Lock()
  259. approved := map[string]int{}
  260. for _, e := range s.ApprovedEmotes {
  261. approved[e] = 1
  262. }
  263. for _, name := range channels {
  264. approved[name] = 1
  265. }
  266. filtered := []string{}
  267. for key, _ := range approved {
  268. filtered = append(filtered, key)
  269. }
  270. s.ApprovedEmotes = filtered
  271. return s.unlockedSave()
  272. }