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.

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