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.

server.go 38KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196
  1. // Copyright (c) 2012-2014 Jeremy Latt
  2. // Copyright (c) 2014-2015 Edmund Huber
  3. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  4. // released under the MIT license
  5. package irc
  6. import (
  7. "bufio"
  8. "crypto/tls"
  9. "encoding/base64"
  10. "fmt"
  11. "log"
  12. "math/rand"
  13. "net"
  14. "os"
  15. "os/signal"
  16. "strconv"
  17. "strings"
  18. "sync"
  19. "syscall"
  20. "time"
  21. "github.com/goshuirc/irc-go/ircfmt"
  22. "github.com/goshuirc/irc-go/ircmsg"
  23. "github.com/oragono/oragono/irc/caps"
  24. "github.com/oragono/oragono/irc/connection_limits"
  25. "github.com/oragono/oragono/irc/isupport"
  26. "github.com/oragono/oragono/irc/languages"
  27. "github.com/oragono/oragono/irc/logger"
  28. "github.com/oragono/oragono/irc/modes"
  29. "github.com/oragono/oragono/irc/passwd"
  30. "github.com/oragono/oragono/irc/sno"
  31. "github.com/oragono/oragono/irc/utils"
  32. "github.com/tidwall/buntdb"
  33. )
  34. var (
  35. // common error line to sub values into
  36. errorMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "%s ")}[0]).Line()
  37. // common error responses
  38. couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line()
  39. // supportedUserModesString acts as a cache for when we introduce users
  40. supportedUserModesString = modes.SupportedUserModes.String()
  41. // supportedChannelModesString acts as a cache for when we introduce users
  42. supportedChannelModesString = modes.SupportedChannelModes.String()
  43. // SupportedCapabilities are the caps we advertise.
  44. // MaxLine, SASL and STS are set during server startup.
  45. SupportedCapabilities = caps.NewSet(caps.AccountTag, caps.AccountNotify, caps.AwayNotify, caps.Batch, caps.CapNotify, caps.ChgHost, caps.EchoMessage, caps.ExtendedJoin, caps.InviteNotify, caps.LabeledResponse, caps.Languages, caps.MessageTags, caps.MultiPrefix, caps.Rename, caps.Resume, caps.ServerTime, caps.UserhostInNames)
  46. // CapValues are the actual values we advertise to v3.2 clients.
  47. // actual values are set during server startup.
  48. CapValues = caps.NewValues()
  49. )
  50. // Limits holds the maximum limits for various things such as topic lengths.
  51. type Limits struct {
  52. AwayLen int
  53. ChannelLen int
  54. KickLen int
  55. MonitorEntries int
  56. NickLen int
  57. TopicLen int
  58. ChanListModes int
  59. LineLen LineLenLimits
  60. }
  61. // LineLenLimits holds the maximum limits for IRC lines.
  62. type LineLenLimits struct {
  63. Tags int
  64. Rest int
  65. }
  66. // ListenerWrapper wraps a listener so it can be safely reconfigured or stopped
  67. type ListenerWrapper struct {
  68. listener net.Listener
  69. tlsConfig *tls.Config
  70. shouldStop bool
  71. // lets the ListenerWrapper inform the server that it has stopped:
  72. stopEvent chan bool
  73. // protects atomic update of tlsConfig and shouldStop:
  74. configMutex sync.Mutex // tier 1
  75. }
  76. // Server is the main Oragono server.
  77. type Server struct {
  78. accountConfig *AccountConfig
  79. accounts *AccountManager
  80. batches *BatchManager
  81. channelRegistrationEnabled bool
  82. channels *ChannelManager
  83. channelRegistry *ChannelRegistry
  84. checkIdent bool
  85. clients *ClientManager
  86. configFilename string
  87. configurableStateMutex sync.RWMutex // tier 1; generic protection for server state modified by rehash()
  88. connectionLimiter *connection_limits.Limiter
  89. connectionThrottler *connection_limits.Throttler
  90. ctime time.Time
  91. defaultChannelModes modes.Modes
  92. dlines *DLineManager
  93. loggingRawIO bool
  94. isupport *isupport.List
  95. klines *KLineManager
  96. languages *languages.Manager
  97. limits Limits
  98. listeners map[string]*ListenerWrapper
  99. logger *logger.Manager
  100. MaxSendQBytes uint64
  101. monitorManager *MonitorManager
  102. motdLines []string
  103. name string
  104. nameCasefolded string
  105. networkName string
  106. operators map[string]Oper
  107. operclasses map[string]OperClass
  108. password []byte
  109. passwords *passwd.SaltedManager
  110. recoverFromErrors bool
  111. rehashMutex sync.Mutex // tier 4
  112. rehashSignal chan os.Signal
  113. proxyAllowedFrom []string
  114. signals chan os.Signal
  115. snomasks *SnoManager
  116. store *buntdb.DB
  117. storeFilename string
  118. stsEnabled bool
  119. webirc []webircConfig
  120. whoWas *WhoWasList
  121. }
  122. var (
  123. // ServerExitSignals are the signals the server will exit on.
  124. ServerExitSignals = []os.Signal{
  125. syscall.SIGINT,
  126. syscall.SIGTERM,
  127. syscall.SIGQUIT,
  128. }
  129. )
  130. type clientConn struct {
  131. Conn net.Conn
  132. IsTLS bool
  133. }
  134. // NewServer returns a new Oragono server.
  135. func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
  136. // initialize data structures
  137. server := &Server{
  138. batches: NewBatchManager(),
  139. channels: NewChannelManager(),
  140. clients: NewClientManager(),
  141. connectionLimiter: connection_limits.NewLimiter(),
  142. connectionThrottler: connection_limits.NewThrottler(),
  143. languages: languages.NewManager(config.Languages.Default, config.Languages.Data),
  144. listeners: make(map[string]*ListenerWrapper),
  145. logger: logger,
  146. monitorManager: NewMonitorManager(),
  147. rehashSignal: make(chan os.Signal, 1),
  148. signals: make(chan os.Signal, len(ServerExitSignals)),
  149. snomasks: NewSnoManager(),
  150. whoWas: NewWhoWasList(config.Limits.WhowasEntries),
  151. }
  152. if err := server.applyConfig(config, true); err != nil {
  153. return nil, err
  154. }
  155. // generate help info
  156. if err := GenerateHelpIndices(server.languages); err != nil {
  157. return nil, err
  158. }
  159. // Attempt to clean up when receiving these signals.
  160. signal.Notify(server.signals, ServerExitSignals...)
  161. signal.Notify(server.rehashSignal, syscall.SIGHUP)
  162. return server, nil
  163. }
  164. // setISupport sets up our RPL_ISUPPORT reply.
  165. func (server *Server) setISupport() {
  166. maxTargetsString := strconv.Itoa(maxTargets)
  167. server.configurableStateMutex.RLock()
  168. // add RPL_ISUPPORT tokens
  169. isupport := isupport.NewList()
  170. isupport.Add("AWAYLEN", strconv.Itoa(server.limits.AwayLen))
  171. isupport.Add("CASEMAPPING", "ascii")
  172. isupport.Add("CHANMODES", strings.Join([]string{modes.Modes{modes.BanMask, modes.ExceptMask, modes.InviteMask}.String(), "", modes.Modes{modes.UserLimit, modes.Key}.String(), modes.Modes{modes.InviteOnly, modes.Moderated, modes.NoOutside, modes.OpOnlyTopic, modes.ChanRoleplaying, modes.Secret}.String()}, ","))
  173. isupport.Add("CHANNELLEN", strconv.Itoa(server.limits.ChannelLen))
  174. isupport.Add("CHANTYPES", "#")
  175. isupport.Add("ELIST", "U")
  176. isupport.Add("EXCEPTS", "")
  177. isupport.Add("INVEX", "")
  178. isupport.Add("KICKLEN", strconv.Itoa(server.limits.KickLen))
  179. isupport.Add("MAXLIST", fmt.Sprintf("beI:%s", strconv.Itoa(server.limits.ChanListModes)))
  180. isupport.Add("MAXTARGETS", maxTargetsString)
  181. isupport.Add("MODES", "")
  182. isupport.Add("MONITOR", strconv.Itoa(server.limits.MonitorEntries))
  183. isupport.Add("NETWORK", server.networkName)
  184. isupport.Add("NICKLEN", strconv.Itoa(server.limits.NickLen))
  185. isupport.Add("PREFIX", "(qaohv)~&@%+")
  186. isupport.Add("RPCHAN", "E")
  187. isupport.Add("RPUSER", "E")
  188. isupport.Add("STATUSMSG", "~&@%+")
  189. isupport.Add("TARGMAX", fmt.Sprintf("NAMES:1,LIST:1,KICK:1,WHOIS:1,USERHOST:10,PRIVMSG:%s,TAGMSG:%s,NOTICE:%s,MONITOR:", maxTargetsString, maxTargetsString, maxTargetsString))
  190. isupport.Add("TOPICLEN", strconv.Itoa(server.limits.TopicLen))
  191. isupport.Add("UTF8MAPPING", casemappingName)
  192. // account registration
  193. if server.accountConfig.Registration.Enabled {
  194. // 'none' isn't shown in the REGCALLBACKS vars
  195. var enabledCallbacks []string
  196. for _, name := range server.accountConfig.Registration.EnabledCallbacks {
  197. if name != "*" {
  198. enabledCallbacks = append(enabledCallbacks, name)
  199. }
  200. }
  201. isupport.Add("REGCOMMANDS", "CREATE,VERIFY")
  202. isupport.Add("REGCALLBACKS", strings.Join(enabledCallbacks, ","))
  203. isupport.Add("REGCREDTYPES", "passphrase,certfp")
  204. }
  205. server.configurableStateMutex.RUnlock()
  206. isupport.RegenerateCachedReply()
  207. server.configurableStateMutex.Lock()
  208. server.isupport = isupport
  209. server.configurableStateMutex.Unlock()
  210. }
  211. func loadChannelList(channel *Channel, list string, maskMode modes.Mode) {
  212. if list == "" {
  213. return
  214. }
  215. channel.lists[maskMode].AddAll(strings.Split(list, " "))
  216. }
  217. // Shutdown shuts down the server.
  218. func (server *Server) Shutdown() {
  219. //TODO(dan): Make sure we disallow new nicks
  220. for _, client := range server.clients.AllClients() {
  221. client.Notice("Server is shutting down")
  222. }
  223. if err := server.store.Close(); err != nil {
  224. server.logger.Error("shutdown", fmt.Sprintln("Could not close datastore:", err))
  225. }
  226. }
  227. // Run starts the server.
  228. func (server *Server) Run() {
  229. // defer closing db/store
  230. defer server.store.Close()
  231. for {
  232. select {
  233. case <-server.signals:
  234. server.Shutdown()
  235. return
  236. case <-server.rehashSignal:
  237. go func() {
  238. server.logger.Info("rehash", "Rehashing due to SIGHUP")
  239. err := server.rehash()
  240. if err != nil {
  241. server.logger.Error("rehash", fmt.Sprintln("Failed to rehash:", err.Error()))
  242. }
  243. }()
  244. }
  245. }
  246. }
  247. func (server *Server) acceptClient(conn clientConn) {
  248. // check IP address
  249. ipaddr := utils.AddrToIP(conn.Conn.RemoteAddr())
  250. if ipaddr != nil {
  251. isBanned, banMsg := server.checkBans(ipaddr)
  252. if isBanned {
  253. // this might not show up properly on some clients, but our objective here is just to close the connection out before it has a load impact on us
  254. conn.Conn.Write([]byte(fmt.Sprintf(errorMsg, banMsg)))
  255. conn.Conn.Close()
  256. return
  257. }
  258. }
  259. server.logger.Debug("localconnect-ip", fmt.Sprintf("Client connecting from %v", ipaddr))
  260. // prolly don't need to alert snomasks on this, only on connection reg
  261. NewClient(server, conn.Conn, conn.IsTLS)
  262. }
  263. func (server *Server) checkBans(ipaddr net.IP) (banned bool, message string) {
  264. // check DLINEs
  265. isBanned, info := server.dlines.CheckIP(ipaddr)
  266. if isBanned {
  267. server.logger.Info("localconnect-ip", fmt.Sprintf("Client from %v rejected by d-line", ipaddr))
  268. return true, info.BanMessage("You are banned from this server (%s)")
  269. }
  270. // check connection limits
  271. err := server.connectionLimiter.AddClient(ipaddr, false)
  272. if err != nil {
  273. // too many connections from one client, tell the client and close the connection
  274. server.logger.Info("localconnect-ip", fmt.Sprintf("Client from %v rejected for connection limit", ipaddr))
  275. return true, "Too many clients from your network"
  276. }
  277. // check connection throttle
  278. err = server.connectionThrottler.AddClient(ipaddr)
  279. if err != nil {
  280. // too many connections too quickly from client, tell them and close the connection
  281. duration := server.connectionThrottler.BanDuration()
  282. length := &IPRestrictTime{
  283. Duration: duration,
  284. Expires: time.Now().Add(duration),
  285. }
  286. server.dlines.AddIP(ipaddr, length, server.connectionThrottler.BanMessage(), "Exceeded automated connection throttle", "auto.connection.throttler")
  287. // they're DLINE'd for 15 minutes or whatever, so we can reset the connection throttle now,
  288. // and once their temporary DLINE is finished they can fill up the throttler again
  289. server.connectionThrottler.ResetFor(ipaddr)
  290. // this might not show up properly on some clients, but our objective here is just to close it out before it has a load impact on us
  291. server.logger.Info(
  292. "localconnect-ip",
  293. fmt.Sprintf("Client from %v exceeded connection throttle, d-lining for %v", ipaddr, duration))
  294. return true, server.connectionThrottler.BanMessage()
  295. }
  296. return false, ""
  297. }
  298. //
  299. // IRC protocol listeners
  300. //
  301. // createListener starts the given listeners.
  302. func (server *Server) createListener(addr string, tlsConfig *tls.Config) *ListenerWrapper {
  303. // make listener
  304. var listener net.Listener
  305. var err error
  306. addr = strings.TrimPrefix(addr, "unix:")
  307. if strings.HasPrefix(addr, "/") {
  308. // https://stackoverflow.com/a/34881585
  309. os.Remove(addr)
  310. listener, err = net.Listen("unix", addr)
  311. } else {
  312. listener, err = net.Listen("tcp", addr)
  313. }
  314. if err != nil {
  315. log.Fatal(server, "listen error: ", err)
  316. }
  317. // throw our details to the server so we can be modified/killed later
  318. wrapper := ListenerWrapper{
  319. listener: listener,
  320. tlsConfig: tlsConfig,
  321. shouldStop: false,
  322. stopEvent: make(chan bool, 1),
  323. }
  324. var shouldStop bool
  325. // setup accept goroutine
  326. go func() {
  327. for {
  328. conn, err := listener.Accept()
  329. // synchronously access config data:
  330. // whether TLS is enabled and whether we should stop listening
  331. wrapper.configMutex.Lock()
  332. shouldStop = wrapper.shouldStop
  333. tlsConfig = wrapper.tlsConfig
  334. wrapper.configMutex.Unlock()
  335. if err == nil {
  336. if tlsConfig != nil {
  337. conn = tls.Server(conn, tlsConfig)
  338. }
  339. newConn := clientConn{
  340. Conn: conn,
  341. IsTLS: tlsConfig != nil,
  342. }
  343. // hand off the connection
  344. go server.acceptClient(newConn)
  345. }
  346. if shouldStop {
  347. listener.Close()
  348. wrapper.stopEvent <- true
  349. return
  350. }
  351. }
  352. }()
  353. return &wrapper
  354. }
  355. // generateMessageID returns a network-unique message ID.
  356. func (server *Server) generateMessageID() string {
  357. // we don't need the full like 30 chars since the unixnano below handles
  358. // most of our uniqueness requirements, so just truncate at 5
  359. lastbit := strconv.FormatInt(rand.Int63(), 36)
  360. if 5 < len(lastbit) {
  361. lastbit = lastbit[:4]
  362. }
  363. return fmt.Sprintf("%s%s", strconv.FormatInt(time.Now().UTC().UnixNano(), 36), lastbit)
  364. }
  365. //
  366. // server functionality
  367. //
  368. func (server *Server) tryRegister(c *Client) {
  369. if c.registered || !c.HasNick() || !c.HasUsername() ||
  370. (c.capState == caps.NegotiatingState) {
  371. return
  372. }
  373. // check KLINEs
  374. isBanned, info := server.klines.CheckMasks(c.AllNickmasks()...)
  375. if isBanned {
  376. reason := info.Reason
  377. if info.Time != nil {
  378. reason += fmt.Sprintf(" [%s]", info.Time.Duration.String())
  379. }
  380. c.Quit(fmt.Sprintf(c.t("You are banned from this server (%s)"), reason))
  381. c.destroy(false)
  382. return
  383. }
  384. // continue registration
  385. server.logger.Debug("localconnect", fmt.Sprintf("Client registered [%s] [u:%s] [r:%s]", c.nick, c.username, c.realname))
  386. server.snomasks.Send(sno.LocalConnects, fmt.Sprintf(ircfmt.Unescape("Client registered $c[grey][$r%s$c[grey]] [u:$r%s$c[grey]] [h:$r%s$c[grey]] [r:$r%s$c[grey]]"), c.nick, c.username, c.rawHostname, c.realname))
  387. c.Register()
  388. // send welcome text
  389. //NOTE(dan): we specifically use the NICK here instead of the nickmask
  390. // see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
  391. c.Send(nil, server.name, RPL_WELCOME, c.nick, fmt.Sprintf(c.t("Welcome to the Internet Relay Network %s"), c.nick))
  392. c.Send(nil, server.name, RPL_YOURHOST, c.nick, fmt.Sprintf(c.t("Your host is %[1]s, running version %[2]s"), server.name, Ver))
  393. c.Send(nil, server.name, RPL_CREATED, c.nick, fmt.Sprintf(c.t("This server was created %s"), server.ctime.Format(time.RFC1123)))
  394. //TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
  395. c.Send(nil, server.name, RPL_MYINFO, c.nick, server.name, Ver, supportedUserModesString, supportedChannelModesString)
  396. rb := NewResponseBuffer(c)
  397. c.RplISupport(rb)
  398. server.MOTD(c, rb)
  399. rb.Send()
  400. c.Send(nil, c.nickMaskString, RPL_UMODEIS, c.nick, c.ModeString())
  401. if server.logger.IsLoggingRawIO() {
  402. c.Notice(c.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
  403. }
  404. // if resumed, send fake channel joins
  405. if c.resumeDetails != nil {
  406. for _, name := range c.resumeDetails.SendFakeJoinsFor {
  407. channel := server.channels.Get(name)
  408. if channel == nil {
  409. continue
  410. }
  411. if c.capabilities.Has(caps.ExtendedJoin) {
  412. c.Send(nil, c.nickMaskString, "JOIN", channel.name, c.AccountName(), c.realname)
  413. } else {
  414. c.Send(nil, c.nickMaskString, "JOIN", channel.name)
  415. }
  416. // reuse the last rb
  417. channel.SendTopic(c, rb)
  418. channel.Names(c, rb)
  419. rb.Send()
  420. // construct and send fake modestring if necessary
  421. c.stateMutex.RLock()
  422. myModes := channel.members[c]
  423. c.stateMutex.RUnlock()
  424. if myModes == nil {
  425. continue
  426. }
  427. oldModes := myModes.String()
  428. if 0 < len(oldModes) {
  429. params := []string{channel.name, "+" + oldModes}
  430. for range oldModes {
  431. params = append(params, c.nick)
  432. }
  433. c.Send(nil, server.name, "MODE", params...)
  434. }
  435. }
  436. }
  437. }
  438. // t returns the translated version of the given string, based on the languages configured by the client.
  439. func (client *Client) t(originalString string) string {
  440. // grab this mutex to protect client.languages
  441. client.stateMutex.RLock()
  442. defer client.stateMutex.RUnlock()
  443. return client.server.languages.Translate(client.languages, originalString)
  444. }
  445. // MOTD serves the Message of the Day.
  446. func (server *Server) MOTD(client *Client, rb *ResponseBuffer) {
  447. server.configurableStateMutex.RLock()
  448. motdLines := server.motdLines
  449. server.configurableStateMutex.RUnlock()
  450. if len(motdLines) < 1 {
  451. rb.Add(nil, server.name, ERR_NOMOTD, client.nick, client.t("MOTD File is missing"))
  452. return
  453. }
  454. rb.Add(nil, server.name, RPL_MOTDSTART, client.nick, fmt.Sprintf(client.t("- %s Message of the day - "), server.name))
  455. for _, line := range motdLines {
  456. rb.Add(nil, server.name, RPL_MOTD, client.nick, line)
  457. }
  458. rb.Add(nil, server.name, RPL_ENDOFMOTD, client.nick, client.t("End of MOTD command"))
  459. }
  460. // wordWrap wraps the given text into a series of lines that don't exceed lineWidth characters.
  461. func wordWrap(text string, lineWidth int) []string {
  462. var lines []string
  463. var cacheLine, cacheWord string
  464. for _, char := range text {
  465. if char == '\r' {
  466. continue
  467. } else if char == '\n' {
  468. cacheLine += cacheWord
  469. lines = append(lines, cacheLine)
  470. cacheWord = ""
  471. cacheLine = ""
  472. } else if (char == ' ' || char == '-') && len(cacheLine)+len(cacheWord)+1 < lineWidth {
  473. // natural word boundary
  474. cacheLine += cacheWord + string(char)
  475. cacheWord = ""
  476. } else if lineWidth <= len(cacheLine)+len(cacheWord)+1 {
  477. // time to wrap to next line
  478. if len(cacheLine) < (lineWidth / 2) {
  479. // this word takes up more than half a line... just split in the middle of the word
  480. cacheLine += cacheWord + string(char)
  481. cacheWord = ""
  482. } else {
  483. cacheWord += string(char)
  484. }
  485. lines = append(lines, cacheLine)
  486. cacheLine = ""
  487. } else {
  488. // normal character
  489. cacheWord += string(char)
  490. }
  491. }
  492. if 0 < len(cacheWord) {
  493. cacheLine += cacheWord
  494. }
  495. if 0 < len(cacheLine) {
  496. lines = append(lines, cacheLine)
  497. }
  498. return lines
  499. }
  500. // SplitMessage represents a message that's been split for sending.
  501. type SplitMessage struct {
  502. For512 []string
  503. ForMaxLine string
  504. }
  505. func (server *Server) splitMessage(original string, origIs512 bool) SplitMessage {
  506. var newSplit SplitMessage
  507. newSplit.ForMaxLine = original
  508. if !origIs512 {
  509. newSplit.For512 = wordWrap(original, 400)
  510. } else {
  511. newSplit.For512 = []string{original}
  512. }
  513. return newSplit
  514. }
  515. // WhoisChannelsNames returns the common channel names between two users.
  516. func (client *Client) WhoisChannelsNames(target *Client) []string {
  517. isMultiPrefix := target.capabilities.Has(caps.MultiPrefix)
  518. var chstrs []string
  519. for _, channel := range client.Channels() {
  520. // channel is secret and the target can't see it
  521. if !target.flags[modes.Operator] && channel.HasMode(modes.Secret) && !channel.hasClient(target) {
  522. continue
  523. }
  524. chstrs = append(chstrs, channel.ClientPrefixes(client, isMultiPrefix)+channel.name)
  525. }
  526. return chstrs
  527. }
  528. func (client *Client) getWhoisOf(target *Client, rb *ResponseBuffer) {
  529. target.stateMutex.RLock()
  530. defer target.stateMutex.RUnlock()
  531. rb.Add(nil, client.server.name, RPL_WHOISUSER, client.nick, target.nick, target.username, target.hostname, "*", target.realname)
  532. whoischannels := client.WhoisChannelsNames(target)
  533. if whoischannels != nil {
  534. rb.Add(nil, client.server.name, RPL_WHOISCHANNELS, client.nick, target.nick, strings.Join(whoischannels, " "))
  535. }
  536. if target.class != nil {
  537. rb.Add(nil, client.server.name, RPL_WHOISOPERATOR, client.nick, target.nick, target.whoisLine)
  538. }
  539. if client.flags[modes.Operator] || client == target {
  540. rb.Add(nil, client.server.name, RPL_WHOISACTUALLY, client.nick, target.nick, fmt.Sprintf("%s@%s", target.username, utils.LookupHostname(target.IPString())), target.IPString(), client.t("Actual user@host, Actual IP"))
  541. }
  542. if target.flags[modes.TLS] {
  543. rb.Add(nil, client.server.name, RPL_WHOISSECURE, client.nick, target.nick, client.t("is using a secure connection"))
  544. }
  545. if target.LoggedIntoAccount() {
  546. rb.Add(nil, client.server.name, RPL_WHOISACCOUNT, client.nick, client.AccountName(), client.t("is logged in as"))
  547. }
  548. if target.flags[modes.Bot] {
  549. rb.Add(nil, client.server.name, RPL_WHOISBOT, client.nick, target.nick, ircfmt.Unescape(fmt.Sprintf(client.t("is a $bBot$b on %s"), client.server.networkName)))
  550. }
  551. if 0 < len(target.languages) {
  552. params := []string{client.nick, target.nick}
  553. for _, str := range client.server.languages.Codes(target.languages) {
  554. params = append(params, str)
  555. }
  556. params = append(params, client.t("can speak these languages"))
  557. rb.Add(nil, client.server.name, RPL_WHOISLANGUAGE, params...)
  558. }
  559. if target.certfp != "" && (client.flags[modes.Operator] || client == target) {
  560. rb.Add(nil, client.server.name, RPL_WHOISCERTFP, client.nick, target.nick, fmt.Sprintf(client.t("has client certificate fingerprint %s"), target.certfp))
  561. }
  562. rb.Add(nil, client.server.name, RPL_WHOISIDLE, client.nick, target.nick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), client.t("seconds idle, signon time"))
  563. }
  564. // rplWhoReply returns the WHO reply between one user and another channel/user.
  565. // <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
  566. // :<hopcount> <real name>
  567. func (target *Client) rplWhoReply(channel *Channel, client *Client, rb *ResponseBuffer) {
  568. channelName := "*"
  569. flags := ""
  570. if client.HasMode(modes.Away) {
  571. flags = "G"
  572. } else {
  573. flags = "H"
  574. }
  575. if client.HasMode(modes.Operator) {
  576. flags += "*"
  577. }
  578. if channel != nil {
  579. flags += channel.ClientPrefixes(client, target.capabilities.Has(caps.MultiPrefix))
  580. channelName = channel.name
  581. }
  582. rb.Add(nil, target.server.name, RPL_WHOREPLY, target.nick, channelName, client.Username(), client.Hostname(), client.server.name, client.Nick(), flags, strconv.Itoa(client.hops)+" "+client.Realname())
  583. }
  584. func whoChannel(client *Client, channel *Channel, friends ClientSet, rb *ResponseBuffer) {
  585. for _, member := range channel.Members() {
  586. if !client.flags[modes.Invisible] || friends[client] {
  587. client.rplWhoReply(channel, member, rb)
  588. }
  589. }
  590. }
  591. // rehash reloads the config and applies the changes from the config file.
  592. func (server *Server) rehash() error {
  593. server.logger.Debug("rehash", "Starting rehash")
  594. // only let one REHASH go on at a time
  595. server.rehashMutex.Lock()
  596. defer server.rehashMutex.Unlock()
  597. server.logger.Debug("rehash", "Got rehash lock")
  598. config, err := LoadConfig(server.configFilename)
  599. if err != nil {
  600. return fmt.Errorf("Error loading config file config: %s", err.Error())
  601. }
  602. err = server.applyConfig(config, false)
  603. if err != nil {
  604. return fmt.Errorf("Error applying config changes: %s", err.Error())
  605. }
  606. return nil
  607. }
  608. func (server *Server) applyConfig(config *Config, initial bool) error {
  609. if initial {
  610. server.ctime = time.Now()
  611. server.configFilename = config.Filename
  612. } else {
  613. // enforce configs that can't be changed after launch:
  614. if server.limits.LineLen.Tags != config.Limits.LineLen.Tags || server.limits.LineLen.Rest != config.Limits.LineLen.Rest {
  615. return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted")
  616. } else if server.name != config.Server.Name {
  617. return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted")
  618. } else if server.storeFilename != config.Datastore.Path {
  619. return fmt.Errorf("Datastore path cannot be changed after launching the server, rehash aborted")
  620. }
  621. }
  622. server.logger.Info("rehash", "Using config file", server.configFilename)
  623. casefoldedName, err := Casefold(config.Server.Name)
  624. if err != nil {
  625. return fmt.Errorf("Server name isn't valid [%s]: %s", config.Server.Name, err.Error())
  626. }
  627. // confirm operator stuff all exists and is fine
  628. operclasses, err := config.OperatorClasses()
  629. if err != nil {
  630. return fmt.Errorf("Error rehashing config file operclasses: %s", err.Error())
  631. }
  632. opers, err := config.Operators(operclasses)
  633. if err != nil {
  634. return fmt.Errorf("Error rehashing config file opers: %s", err.Error())
  635. }
  636. // TODO: support rehash of existing operator perms?
  637. // sanity checks complete, start modifying server state
  638. if initial {
  639. server.name = config.Server.Name
  640. server.nameCasefolded = casefoldedName
  641. }
  642. server.configurableStateMutex.Lock()
  643. server.networkName = config.Network.Name
  644. if config.Server.Password != "" {
  645. server.password = config.Server.PasswordBytes()
  646. } else {
  647. server.password = nil
  648. }
  649. // apply new WebIRC command restrictions
  650. server.webirc = config.Server.WebIRC
  651. // apply new PROXY command restrictions
  652. server.proxyAllowedFrom = config.Server.ProxyAllowedFrom
  653. server.recoverFromErrors = true
  654. if config.Debug.RecoverFromErrors != nil {
  655. server.recoverFromErrors = *config.Debug.RecoverFromErrors
  656. }
  657. server.configurableStateMutex.Unlock()
  658. err = server.connectionLimiter.ApplyConfig(config.Server.ConnectionLimiter)
  659. if err != nil {
  660. return err
  661. }
  662. err = server.connectionThrottler.ApplyConfig(config.Server.ConnectionThrottler)
  663. if err != nil {
  664. return err
  665. }
  666. // setup new and removed caps
  667. addedCaps := caps.NewSet()
  668. removedCaps := caps.NewSet()
  669. updatedCaps := caps.NewSet()
  670. // Translations
  671. currentLanguageValue, _ := CapValues.Get(caps.Languages)
  672. langCodes := []string{strconv.Itoa(len(config.Languages.Data) + 1), "en"}
  673. for _, info := range config.Languages.Data {
  674. if info.Incomplete {
  675. langCodes = append(langCodes, "~"+info.Code)
  676. } else {
  677. langCodes = append(langCodes, info.Code)
  678. }
  679. }
  680. newLanguageValue := strings.Join(langCodes, ",")
  681. server.logger.Debug("rehash", "Languages:", newLanguageValue)
  682. if currentLanguageValue != newLanguageValue {
  683. updatedCaps.Add(caps.Languages)
  684. CapValues.Set(caps.Languages, newLanguageValue)
  685. }
  686. lm := languages.NewManager(config.Languages.Default, config.Languages.Data)
  687. server.logger.Debug("rehash", "Regenerating HELP indexes for new languages")
  688. GenerateHelpIndices(lm)
  689. server.languages = lm
  690. // SASL
  691. oldAccountConfig := server.AccountConfig()
  692. authPreviouslyEnabled := oldAccountConfig != nil && oldAccountConfig.AuthenticationEnabled
  693. if config.Accounts.AuthenticationEnabled && !authPreviouslyEnabled {
  694. // enabling SASL
  695. SupportedCapabilities.Enable(caps.SASL)
  696. CapValues.Set(caps.SASL, "PLAIN,EXTERNAL")
  697. addedCaps.Add(caps.SASL)
  698. } else if !config.Accounts.AuthenticationEnabled && authPreviouslyEnabled {
  699. // disabling SASL
  700. SupportedCapabilities.Disable(caps.SASL)
  701. removedCaps.Add(caps.SASL)
  702. }
  703. server.configurableStateMutex.Lock()
  704. server.accountConfig = &config.Accounts
  705. server.configurableStateMutex.Unlock()
  706. nickReservationPreviouslyDisabled := oldAccountConfig != nil && !oldAccountConfig.NickReservation.Enabled
  707. nickReservationNowEnabled := config.Accounts.NickReservation.Enabled
  708. if nickReservationPreviouslyDisabled && nickReservationNowEnabled {
  709. server.accounts.buildNickToAccountIndex()
  710. }
  711. // STS
  712. stsValue := config.Server.STS.Value()
  713. var stsDisabled bool
  714. stsCurrentCapValue, _ := CapValues.Get(caps.STS)
  715. server.logger.Debug("rehash", "STS Vals", stsCurrentCapValue, stsValue, fmt.Sprintf("server[%v] config[%v]", server.stsEnabled, config.Server.STS.Enabled))
  716. if config.Server.STS.Enabled && !server.stsEnabled {
  717. // enabling STS
  718. SupportedCapabilities.Enable(caps.STS)
  719. addedCaps.Add(caps.STS)
  720. CapValues.Set(caps.STS, stsValue)
  721. } else if !config.Server.STS.Enabled && server.stsEnabled {
  722. // disabling STS
  723. SupportedCapabilities.Disable(caps.STS)
  724. removedCaps.Add(caps.STS)
  725. stsDisabled = true
  726. } else if config.Server.STS.Enabled && server.stsEnabled && stsValue != stsCurrentCapValue {
  727. // STS policy updated
  728. CapValues.Set(caps.STS, stsValue)
  729. updatedCaps.Add(caps.STS)
  730. }
  731. server.stsEnabled = config.Server.STS.Enabled
  732. // burst new and removed caps
  733. var capBurstClients ClientSet
  734. added := make(map[caps.Version]string)
  735. var removed string
  736. // updated caps get DEL'd and then NEW'd
  737. // so, we can just add updated ones to both removed and added lists here and they'll be correctly handled
  738. server.logger.Debug("rehash", "Updated Caps", updatedCaps.String(caps.Cap301, CapValues), strconv.Itoa(updatedCaps.Count()))
  739. for _, capab := range updatedCaps.List() {
  740. addedCaps.Enable(capab)
  741. removedCaps.Enable(capab)
  742. }
  743. if 0 < addedCaps.Count() || 0 < removedCaps.Count() {
  744. capBurstClients = server.clients.AllWithCaps(caps.CapNotify)
  745. added[caps.Cap301] = addedCaps.String(caps.Cap301, CapValues)
  746. added[caps.Cap302] = addedCaps.String(caps.Cap302, CapValues)
  747. // removed never has values, so we leave it as Cap301
  748. removed = removedCaps.String(caps.Cap301, CapValues)
  749. }
  750. for sClient := range capBurstClients {
  751. if stsDisabled {
  752. // remove STS policy
  753. //TODO(dan): this is an ugly hack. we can write this better.
  754. stsPolicy := "sts=duration=0"
  755. if 0 < addedCaps.Count() {
  756. added[caps.Cap302] = added[caps.Cap302] + " " + stsPolicy
  757. } else {
  758. addedCaps.Enable(caps.STS)
  759. added[caps.Cap302] = stsPolicy
  760. }
  761. }
  762. // DEL caps and then send NEW ones so that updated caps get removed/added correctly
  763. if 0 < removedCaps.Count() {
  764. sClient.Send(nil, server.name, "CAP", sClient.nick, "DEL", removed)
  765. }
  766. if 0 < addedCaps.Count() {
  767. sClient.Send(nil, server.name, "CAP", sClient.nick, "NEW", added[sClient.capVersion])
  768. }
  769. }
  770. // set server options
  771. server.configurableStateMutex.Lock()
  772. lineLenConfig := LineLenLimits{
  773. Tags: config.Limits.LineLen.Tags,
  774. Rest: config.Limits.LineLen.Rest,
  775. }
  776. server.limits = Limits{
  777. AwayLen: int(config.Limits.AwayLen),
  778. ChannelLen: int(config.Limits.ChannelLen),
  779. KickLen: int(config.Limits.KickLen),
  780. MonitorEntries: int(config.Limits.MonitorEntries),
  781. NickLen: int(config.Limits.NickLen),
  782. TopicLen: int(config.Limits.TopicLen),
  783. ChanListModes: int(config.Limits.ChanListModes),
  784. LineLen: lineLenConfig,
  785. }
  786. server.operclasses = *operclasses
  787. server.operators = opers
  788. server.checkIdent = config.Server.CheckIdent
  789. // registration
  790. server.channelRegistrationEnabled = config.Channels.Registration.Enabled
  791. server.defaultChannelModes = ParseDefaultChannelModes(config)
  792. server.configurableStateMutex.Unlock()
  793. // set new sendqueue size
  794. if config.Server.MaxSendQBytes != server.MaxSendQBytes {
  795. server.configurableStateMutex.Lock()
  796. server.MaxSendQBytes = config.Server.MaxSendQBytes
  797. server.configurableStateMutex.Unlock()
  798. // update on all clients
  799. for _, sClient := range server.clients.AllClients() {
  800. sClient.socket.MaxSendQBytes = config.Server.MaxSendQBytes
  801. }
  802. }
  803. // set RPL_ISUPPORT
  804. var newISupportReplies [][]string
  805. oldISupportList := server.isupport
  806. server.setISupport()
  807. if oldISupportList != nil {
  808. newISupportReplies = oldISupportList.GetDifference(server.isupport)
  809. }
  810. server.loadMOTD(config.Server.MOTD, config.Server.MOTDFormatting)
  811. // reload logging config
  812. err = server.logger.ApplyConfig(config.Logging)
  813. if err != nil {
  814. return err
  815. }
  816. nowLoggingRawIO := server.logger.IsLoggingRawIO()
  817. // notify clients if raw i/o logging was enabled by a rehash
  818. sendRawOutputNotice := !initial && !server.loggingRawIO && nowLoggingRawIO
  819. server.loggingRawIO = nowLoggingRawIO
  820. server.storeFilename = config.Datastore.Path
  821. server.logger.Info("rehash", "Using datastore", server.storeFilename)
  822. if initial {
  823. if err := server.loadDatastore(server.storeFilename); err != nil {
  824. return err
  825. }
  826. }
  827. // we are now open for business
  828. server.setupListeners(config)
  829. if !initial {
  830. // push new info to all of our clients
  831. for _, sClient := range server.clients.AllClients() {
  832. for _, tokenline := range newISupportReplies {
  833. sClient.Send(nil, server.name, RPL_ISUPPORT, append([]string{sClient.nick}, tokenline...)...)
  834. }
  835. if sendRawOutputNotice {
  836. sClient.Notice(sClient.t("This server is in debug mode and is logging all user I/O. If you do not wish for everything you send to be readable by the server owner(s), please disconnect."))
  837. }
  838. }
  839. }
  840. return nil
  841. }
  842. func (server *Server) loadMOTD(motdPath string, useFormatting bool) error {
  843. server.logger.Info("rehash", "Using MOTD", motdPath)
  844. motdLines := make([]string, 0)
  845. if motdPath != "" {
  846. file, err := os.Open(motdPath)
  847. if err == nil {
  848. defer file.Close()
  849. reader := bufio.NewReader(file)
  850. for {
  851. line, err := reader.ReadString('\n')
  852. if err != nil {
  853. break
  854. }
  855. line = strings.TrimRight(line, "\r\n")
  856. if useFormatting {
  857. line = ircfmt.Unescape(line)
  858. }
  859. // "- " is the required prefix for MOTD, we just add it here to make
  860. // bursting it out to clients easier
  861. line = fmt.Sprintf("- %s", line)
  862. motdLines = append(motdLines, line)
  863. }
  864. } else {
  865. return err
  866. }
  867. }
  868. server.configurableStateMutex.Lock()
  869. server.motdLines = motdLines
  870. server.configurableStateMutex.Unlock()
  871. return nil
  872. }
  873. func (server *Server) loadDatastore(datastorePath string) error {
  874. // open the datastore and load server state for which it (rather than config)
  875. // is the source of truth
  876. db, err := OpenDatabase(datastorePath)
  877. if err == nil {
  878. server.store = db
  879. } else {
  880. return fmt.Errorf("Failed to open datastore: %s", err.Error())
  881. }
  882. // load *lines (from the datastores)
  883. server.logger.Debug("startup", "Loading D/Klines")
  884. server.loadDLines()
  885. server.loadKLines()
  886. // load password manager
  887. server.logger.Debug("startup", "Loading passwords")
  888. err = server.store.View(func(tx *buntdb.Tx) error {
  889. saltString, err := tx.Get(keySalt)
  890. if err != nil {
  891. return fmt.Errorf("Could not retrieve salt string: %s", err.Error())
  892. }
  893. salt, err := base64.StdEncoding.DecodeString(saltString)
  894. if err != nil {
  895. return err
  896. }
  897. pwm := passwd.NewSaltedManager(salt)
  898. server.passwords = &pwm
  899. return nil
  900. })
  901. if err != nil {
  902. return fmt.Errorf("Could not load salt: %s", err.Error())
  903. }
  904. server.channelRegistry = NewChannelRegistry(server)
  905. server.accounts = NewAccountManager(server)
  906. return nil
  907. }
  908. func (server *Server) setupListeners(config *Config) {
  909. logListener := func(addr string, tlsconfig *tls.Config) {
  910. server.logger.Info("listeners",
  911. fmt.Sprintf("now listening on %s, tls=%t.", addr, (tlsconfig != nil)),
  912. )
  913. }
  914. // update or destroy all existing listeners
  915. tlsListeners := config.TLSListeners()
  916. for addr := range server.listeners {
  917. currentListener := server.listeners[addr]
  918. var stillConfigured bool
  919. for _, newaddr := range config.Server.Listen {
  920. if newaddr == addr {
  921. stillConfigured = true
  922. break
  923. }
  924. }
  925. // pass new config information to the listener, to be picked up after
  926. // its next Accept(). this is like sending over a buffered channel of
  927. // size 1, but where sending a second item overwrites the buffered item
  928. // instead of blocking.
  929. currentListener.configMutex.Lock()
  930. currentListener.shouldStop = !stillConfigured
  931. currentListener.tlsConfig = tlsListeners[addr]
  932. currentListener.configMutex.Unlock()
  933. if stillConfigured {
  934. logListener(addr, currentListener.tlsConfig)
  935. } else {
  936. // tell the listener it should stop by interrupting its Accept() call:
  937. currentListener.listener.Close()
  938. // TODO(golang1.10) delete stopEvent once issue #21856 is released
  939. <-currentListener.stopEvent
  940. delete(server.listeners, addr)
  941. server.logger.Info("listeners", fmt.Sprintf("stopped listening on %s.", addr))
  942. }
  943. }
  944. // create new listeners that were not previously configured
  945. for _, newaddr := range config.Server.Listen {
  946. _, exists := server.listeners[newaddr]
  947. if !exists {
  948. // make new listener
  949. tlsConfig := tlsListeners[newaddr]
  950. server.listeners[newaddr] = server.createListener(newaddr, tlsConfig)
  951. logListener(newaddr, tlsConfig)
  952. }
  953. }
  954. if len(tlsListeners) == 0 {
  955. server.logger.Warning("startup", "You are not exposing an SSL/TLS listening port. You should expose at least one port (typically 6697) to accept TLS connections")
  956. }
  957. var usesStandardTLSPort bool
  958. for addr := range config.TLSListeners() {
  959. if strings.Contains(addr, "6697") {
  960. usesStandardTLSPort = true
  961. break
  962. }
  963. }
  964. if 0 < len(tlsListeners) && !usesStandardTLSPort {
  965. server.logger.Warning("startup", "Port 6697 is the standard TLS port for IRC. You should (also) expose port 6697 as a TLS port to ensure clients can connect securely")
  966. }
  967. }
  968. // elistMatcher takes and matches ELIST conditions
  969. type elistMatcher struct {
  970. MinClientsActive bool
  971. MinClients int
  972. MaxClientsActive bool
  973. MaxClients int
  974. }
  975. // Matches checks whether the given channel matches our matches.
  976. func (matcher *elistMatcher) Matches(channel *Channel) bool {
  977. if matcher.MinClientsActive {
  978. if len(channel.Members()) < matcher.MinClients {
  979. return false
  980. }
  981. }
  982. if matcher.MaxClientsActive {
  983. if len(channel.Members()) < len(channel.members) {
  984. return false
  985. }
  986. }
  987. return true
  988. }
  989. // RplList returns the RPL_LIST numeric for the given channel.
  990. func (target *Client) RplList(channel *Channel, rb *ResponseBuffer) {
  991. // get the correct number of channel members
  992. var memberCount int
  993. if target.flags[modes.Operator] || channel.hasClient(target) {
  994. memberCount = len(channel.Members())
  995. } else {
  996. for _, member := range channel.Members() {
  997. if !member.HasMode(modes.Invisible) {
  998. memberCount++
  999. }
  1000. }
  1001. }
  1002. rb.Add(nil, target.server.name, RPL_LIST, target.nick, channel.name, strconv.Itoa(memberCount), channel.topic)
  1003. }
  1004. // ResumeDetails are the details that we use to resume connections.
  1005. type ResumeDetails struct {
  1006. OldNick string
  1007. Timestamp *time.Time
  1008. SendFakeJoinsFor []string
  1009. }
  1010. var (
  1011. infoString1 = strings.Split(` ▄▄▄ ▄▄▄· ▄▄ • ▐ ▄
  1012. ▪ ▀▄ █·▐█ ▀█ ▐█ ▀ ▪▪ •█▌▐█▪
  1013. ▄█▀▄ ▐▀▀▄ ▄█▀▀█ ▄█ ▀█▄ ▄█▀▄▪▐█▐▐▌ ▄█▀▄
  1014. ▐█▌.▐▌▐█•█▌▐█ ▪▐▌▐█▄▪▐█▐█▌ ▐▌██▐█▌▐█▌.▐▌
  1015. ▀█▄▀▪.▀ ▀ ▀ ▀ ·▀▀▀▀ ▀█▄▀ ▀▀ █▪ ▀█▄▀▪
  1016. https://oragono.io/
  1017. https://github.com/oragono/oragono
  1018. https://crowdin.com/project/oragono
  1019. `, "\n")
  1020. infoString2 = strings.Split(` Daniel Oakley, DanielOaks, <daniel@danieloaks.net>
  1021. Shivaram Lingamneni, slingamn, <slingamn@cs.stanford.edu>
  1022. `, "\n")
  1023. infoString3 = strings.Split(` 3onyc
  1024. Edmund Huber
  1025. Euan Kemp (euank)
  1026. Jeremy Latt
  1027. Martin Lindhe (martinlindhe)
  1028. Roberto Besser (besser)
  1029. Robin Burchell (rburchell)
  1030. Sean Enck (enckse)
  1031. soul9
  1032. Vegax
  1033. `, "\n")
  1034. )