parent
ca9fa9198c
commit
a9a066faac
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -36,6 +37,19 @@ func (v *stringSliceFlag) Set(s string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadMOTD(srv *soju.Server, filename string) error {
|
||||||
|
if filename == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
srv.SetMOTD(strings.TrimSpace(string(b)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var listen []string
|
var listen []string
|
||||||
var configPath string
|
var configPath string
|
||||||
@ -91,6 +105,10 @@ func main() {
|
|||||||
srv.MaxUserNetworks = cfg.MaxUserNetworks
|
srv.MaxUserNetworks = cfg.MaxUserNetworks
|
||||||
srv.Debug = debug
|
srv.Debug = debug
|
||||||
|
|
||||||
|
if err := loadMOTD(srv, cfg.MOTDPath); err != nil {
|
||||||
|
log.Fatalf("failed to load MOTD: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
for _, listen := range cfg.Listen {
|
for _, listen := range cfg.Listen {
|
||||||
listenURI := listen
|
listenURI := listen
|
||||||
if !strings.Contains(listenURI, ":/") {
|
if !strings.Contains(listenURI, ":/") {
|
||||||
@ -224,8 +242,8 @@ func main() {
|
|||||||
for sig := range sigCh {
|
for sig := range sigCh {
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGHUP:
|
case syscall.SIGHUP:
|
||||||
|
log.Print("reloading TLS certificate and MOTD")
|
||||||
if cfg.TLS != nil {
|
if cfg.TLS != nil {
|
||||||
log.Print("reloading TLS certificate")
|
|
||||||
cert, err := tls.LoadX509KeyPair(cfg.TLS.CertPath, cfg.TLS.KeyPath)
|
cert, err := tls.LoadX509KeyPair(cfg.TLS.CertPath, cfg.TLS.KeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to reload TLS certificate and key: %v", err)
|
log.Printf("failed to reload TLS certificate and key: %v", err)
|
||||||
@ -233,6 +251,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
tlsCert.Store(&cert)
|
tlsCert.Store(&cert)
|
||||||
}
|
}
|
||||||
|
if err := loadMOTD(srv, cfg.MOTDPath); err != nil {
|
||||||
|
log.Printf("failed to reload MOTD: %v", err)
|
||||||
|
}
|
||||||
case syscall.SIGINT, syscall.SIGTERM:
|
case syscall.SIGINT, syscall.SIGTERM:
|
||||||
log.Print("shutting down server")
|
log.Print("shutting down server")
|
||||||
srv.Shutdown()
|
srv.Shutdown()
|
||||||
|
@ -40,6 +40,7 @@ type Server struct {
|
|||||||
Listen []string
|
Listen []string
|
||||||
Hostname string
|
Hostname string
|
||||||
TLS *TLS
|
TLS *TLS
|
||||||
|
MOTDPath string
|
||||||
|
|
||||||
SQLDriver string
|
SQLDriver string
|
||||||
SQLSource string
|
SQLSource string
|
||||||
@ -128,6 +129,10 @@ func parse(cfg scfg.Block) (*Server, error) {
|
|||||||
if srv.MaxUserNetworks, err = strconv.Atoi(max); err != nil {
|
if srv.MaxUserNetworks, err = strconv.Atoi(max); err != nil {
|
||||||
return nil, fmt.Errorf("directive %q: %v", d.Name, err)
|
return nil, fmt.Errorf("directive %q: %v", d.Name, err)
|
||||||
}
|
}
|
||||||
|
case "motd":
|
||||||
|
if err := d.ParseParams(&srv.MOTDPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown directive %q", d.Name)
|
return nil, fmt.Errorf("unknown directive %q", d.Name)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,8 @@ soju supports two connection modes:
|
|||||||
For per-client history to work, clients need to indicate their name. This can
|
For per-client history to work, clients need to indicate their name. This can
|
||||||
be done by adding a "@<client>" suffix to the username.
|
be done by adding a "@<client>" suffix to the username.
|
||||||
|
|
||||||
soju will reload the TLS certificate and key when it receives the HUP signal.
|
soju will reload the TLS certificate/key and the MOTD file when it receives the
|
||||||
|
HUP signal.
|
||||||
|
|
||||||
Administrators can broadcast a message to all bouncer users via _/notice
|
Administrators can broadcast a message to all bouncer users via _/notice
|
||||||
$<hostname> <text>_, or via _/notice $\* <text>_ in multi-upstream mode. All
|
$<hostname> <text>_, or via _/notice $\* <text>_ in multi-upstream mode. All
|
||||||
@ -142,6 +143,10 @@ The following directives are supported:
|
|||||||
*max-user-networks* <limit>
|
*max-user-networks* <limit>
|
||||||
Maximum number of networks per user. By default, there is no limit.
|
Maximum number of networks per user. By default, there is no limit.
|
||||||
|
|
||||||
|
*motd* <path>
|
||||||
|
Path to the MOTD file. The bouncer MOTD is sent to clients which aren't
|
||||||
|
bound to a specific network. By default, no MOTD is sent.
|
||||||
|
|
||||||
# IRC SERVICE
|
# IRC SERVICE
|
||||||
|
|
||||||
soju exposes an IRC service called *BouncerServ* to manage the bouncer.
|
soju exposes an IRC service called *BouncerServ* to manage the bouncer.
|
||||||
|
@ -1119,20 +1119,29 @@ func (dc *downstreamConn) welcome() error {
|
|||||||
for _, msg := range generateIsupport(dc.srv.prefix(), dc.nick, isupport) {
|
for _, msg := range generateIsupport(dc.srv.prefix(), dc.nick, isupport) {
|
||||||
dc.SendMessage(msg)
|
dc.SendMessage(msg)
|
||||||
}
|
}
|
||||||
motdHint := "No MOTD"
|
|
||||||
if uc := dc.upstream(); uc != nil {
|
if uc := dc.upstream(); uc != nil {
|
||||||
motdHint = "Use /motd to read the message of the day"
|
|
||||||
dc.SendMessage(&irc.Message{
|
dc.SendMessage(&irc.Message{
|
||||||
Prefix: dc.srv.prefix(),
|
Prefix: dc.srv.prefix(),
|
||||||
Command: irc.RPL_UMODEIS,
|
Command: irc.RPL_UMODEIS,
|
||||||
Params: []string{dc.nick, string(uc.modes)},
|
Params: []string{dc.nick, string(uc.modes)},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
dc.SendMessage(&irc.Message{
|
|
||||||
Prefix: dc.srv.prefix(),
|
if motd := dc.user.srv.MOTD(); motd != "" && dc.network == nil {
|
||||||
Command: irc.ERR_NOMOTD,
|
for _, msg := range generateMOTD(dc.srv.prefix(), dc.nick, motd) {
|
||||||
Params: []string{dc.nick, motdHint},
|
dc.SendMessage(msg)
|
||||||
})
|
}
|
||||||
|
} else {
|
||||||
|
motdHint := "No MOTD"
|
||||||
|
if dc.network != nil {
|
||||||
|
motdHint = "Use /motd to read the message of the day"
|
||||||
|
}
|
||||||
|
dc.SendMessage(&irc.Message{
|
||||||
|
Prefix: dc.srv.prefix(),
|
||||||
|
Command: irc.ERR_NOMOTD,
|
||||||
|
Params: []string{dc.nick, motdHint},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
dc.updateNick()
|
dc.updateNick()
|
||||||
dc.updateRealname()
|
dc.updateRealname()
|
||||||
|
25
irc.go
25
irc.go
@ -379,6 +379,31 @@ func generateIsupport(prefix *irc.Prefix, nick string, tokens []string) []*irc.M
|
|||||||
return msgs
|
return msgs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func generateMOTD(prefix *irc.Prefix, nick string, motd string) []*irc.Message {
|
||||||
|
var msgs []*irc.Message
|
||||||
|
msgs = append(msgs, &irc.Message{
|
||||||
|
Prefix: prefix,
|
||||||
|
Command: irc.RPL_MOTDSTART,
|
||||||
|
Params: []string{nick, fmt.Sprintf("- Message of the Day -")},
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, l := range strings.Split(motd, "\n") {
|
||||||
|
msgs = append(msgs, &irc.Message{
|
||||||
|
Prefix: prefix,
|
||||||
|
Command: irc.RPL_MOTD,
|
||||||
|
Params: []string{nick, l},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs = append(msgs, &irc.Message{
|
||||||
|
Prefix: prefix,
|
||||||
|
Command: irc.RPL_ENDOFMOTD,
|
||||||
|
Params: []string{nick, "End of /MOTD command."},
|
||||||
|
})
|
||||||
|
|
||||||
|
return msgs
|
||||||
|
}
|
||||||
|
|
||||||
type joinSorter struct {
|
type joinSorter struct {
|
||||||
channels []string
|
channels []string
|
||||||
keys []string
|
keys []string
|
||||||
|
14
server.go
14
server.go
@ -63,10 +63,12 @@ type Server struct {
|
|||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
listeners map[net.Listener]struct{}
|
listeners map[net.Listener]struct{}
|
||||||
users map[string]*user
|
users map[string]*user
|
||||||
|
|
||||||
|
motd atomic.Value // string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(db Database) *Server {
|
func NewServer(db Database) *Server {
|
||||||
return &Server{
|
srv := &Server{
|
||||||
Logger: log.New(log.Writer(), "", log.LstdFlags),
|
Logger: log.New(log.Writer(), "", log.LstdFlags),
|
||||||
HistoryLimit: 1000,
|
HistoryLimit: 1000,
|
||||||
MaxUserNetworks: -1,
|
MaxUserNetworks: -1,
|
||||||
@ -74,6 +76,8 @@ func NewServer(db Database) *Server {
|
|||||||
listeners: make(map[net.Listener]struct{}),
|
listeners: make(map[net.Listener]struct{}),
|
||||||
users: make(map[string]*user),
|
users: make(map[string]*user),
|
||||||
}
|
}
|
||||||
|
srv.motd.Store("")
|
||||||
|
return srv
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) prefix() *irc.Prefix {
|
func (s *Server) prefix() *irc.Prefix {
|
||||||
@ -268,3 +272,11 @@ func (s *Server) Stats() *ServerStats {
|
|||||||
stats.Downstreams = atomic.LoadInt64(&s.connCount)
|
stats.Downstreams = atomic.LoadInt64(&s.connCount)
|
||||||
return &stats
|
return &stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) SetMOTD(motd string) {
|
||||||
|
s.motd.Store(motd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) MOTD() string {
|
||||||
|
return s.motd.Load().(string)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user