From 83d021fcb7b4f6d7daf8459383b97607016d1a84 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sat, 1 Mar 2014 14:34:51 -0800 Subject: [PATCH 01/15] switch to gcfg for conf file - add some validation for config file - add comments explaining config - remove TLS listener since most clients can't use it anyway - remove unused nick generation function --- config.json | 39 ------------------ ergonomadic.conf | 16 ++++++++ ergonomadic.go | 12 +++--- irc/config.go | 100 +++++++++++++++++++++-------------------------- irc/server.go | 56 +++++--------------------- 5 files changed, 76 insertions(+), 147 deletions(-) delete mode 100644 config.json create mode 100644 ergonomadic.conf diff --git a/config.json b/config.json deleted file mode 100644 index b607611a..00000000 --- a/config.json +++ /dev/null @@ -1,39 +0,0 @@ -// Ergonomadic IRC Server Config -// ----------------------------- -// Passwords are generated by `ergonomadic -genpasswd "$plaintext"`. -// Comments are not allowed in the actual config file. -{ - // `name` is usually a hostname. - "name": "irc.example.com", - - // The path to the MOTD is relative to this file's directory. - "motd": "motd.txt", - - // PASS command password - "password": "JDJhJDA0JHBBenUyV3Z5UU5iWUpiYmlNMlNLZC5VRDZDM21HUzFVbmxLUUI3NTVTLkZJOERLdUFaUWNt", - - // `listeners` are places to bind and listen for - // connections. http://golang.org/pkg/net/#Dial demonstrates valid - // values for `net` and `address`. `net` is optional and defaults - // to `tcp`. - "listeners": [ { - "address": "localhost:7777" - }, { - "net": "tcp6", - "address": "[::1]:7777" - } ], - - // Operators for the OPER command - "operators": [ { - "name": "root", - "password": "JDJhJDA0JHBBenUyV3Z5UU5iWUpiYmlNMlNLZC5VRDZDM21HUzFVbmxLUUI3NTVTLkZJOERLdUFaUWNt" - } ], - - // Global debug flags. `net` generates a lot of output. - "debug": { - "net": true, - "client": false, - "channel": false, - "server": false - } -} diff --git a/ergonomadic.conf b/ergonomadic.conf new file mode 100644 index 00000000..6181e9a7 --- /dev/null +++ b/ergonomadic.conf @@ -0,0 +1,16 @@ +[server] +name = "irc.example.com" ; required, usually a hostname +database = "ergonomadic.db" ; path relative to this file +listen = "localhost:6667" ; see `net.Listen` for examples +listen = "[::1]:6667" ; multiple `listen`s are allowed. +motd = "motd.txt" ; path relative to this file +password = "JDJhJDA0JHJzVFFlNXdOUXNhLmtkSGRUQVVEVHVYWXRKUmdNQ3FKVTRrczRSMTlSWGRPZHRSMVRzQmtt" + +[operator "root"] +password = "JDJhJDA0JEhkcm10UlNFRkRXb25iOHZuSDVLZXVBWlpyY0xyNkQ4dlBVc1VMWVk1LlFjWFpQbGxZNUtl" + +[debug] +net = true +client = false +channel = false +server = false diff --git a/ergonomadic.go b/ergonomadic.go index e04f9e8a..6537ff0b 100644 --- a/ergonomadic.go +++ b/ergonomadic.go @@ -22,9 +22,9 @@ func genPasswd(passwd string) { } func initDB(config *irc.Config) { - os.Remove(config.Database()) + os.Remove(config.Server.Database) - db, err := sql.Open("sqlite3", config.Database()) + db, err := sql.Open("sqlite3", config.Server.Database) if err != nil { log.Fatal(err) } @@ -64,10 +64,10 @@ func main() { } // TODO move to data structures - irc.DEBUG_NET = config.Debug["net"] - irc.DEBUG_CLIENT = config.Debug["client"] - irc.DEBUG_CHANNEL = config.Debug["channel"] - irc.DEBUG_SERVER = config.Debug["server"] + irc.DEBUG_NET = config.Debug.Net + irc.DEBUG_CLIENT = config.Debug.Client + irc.DEBUG_CHANNEL = config.Debug.Channel + irc.DEBUG_SERVER = config.Debug.Server irc.NewServer(config).Run() } diff --git a/irc/config.go b/irc/config.go index 07518f00..8fea2974 100644 --- a/irc/config.go +++ b/irc/config.go @@ -1,18 +1,22 @@ package irc import ( + "code.google.com/p/gcfg" "encoding/base64" - "encoding/json" + "errors" "log" - "os" "path/filepath" ) -func decodePassword(password string) []byte { - if password == "" { +type PassConfig struct { + Password string +} + +func (conf *PassConfig) PasswordBytes() []byte { + if conf.Password == "" { return nil } - bytes, err := base64.StdEncoding.DecodeString(password) + bytes, err := base64.StdEncoding.DecodeString(conf.Password) if err != nil { log.Fatal(err) } @@ -20,72 +24,56 @@ func decodePassword(password string) []byte { } type Config struct { - Debug map[string]bool - Listeners []ListenerConfig - MOTD string - Name string - Operators []OperatorConfig - Password string - directory string + Server struct { + PassConfig + Database string + Listen []string + MOTD string + Name string + } + + Operator map[string]*PassConfig + + Debug struct { + Net bool + Client bool + Channel bool + Server bool + } } -func (conf *Config) Database() string { - return filepath.Join(conf.directory, "ergonomadic.db") -} - -func (conf *Config) PasswordBytes() []byte { - return decodePassword(conf.Password) -} - -func (conf *Config) OperatorsMap() map[string][]byte { +func (conf *Config) Operators() map[string][]byte { operators := make(map[string][]byte) - for _, opConf := range conf.Operators { - operators[opConf.Name] = opConf.PasswordBytes() + for name, opConf := range conf.Operator { + operators[name] = opConf.PasswordBytes() } return operators } -type OperatorConfig struct { - Name string - Password string -} - -func (conf *OperatorConfig) PasswordBytes() []byte { - return decodePassword(conf.Password) -} - -type ListenerConfig struct { - Net string - Address string - Key string - Certificate string -} - -func (config *ListenerConfig) IsTLS() bool { - return (config.Key != "") && (config.Certificate != "") -} - func LoadConfig(filename string) (config *Config, err error) { config = &Config{} - - file, err := os.Open(filename) + err = gcfg.ReadFileInto(config, filename) if err != nil { return } - defer file.Close() - - decoder := json.NewDecoder(file) - err = decoder.Decode(config) - if err != nil { + if config.Server.Name == "" { + err = errors.New("server.name missing") + return + } + if config.Server.Database == "" { + err = errors.New("server.database missing") + return + } + if len(config.Server.Listen) == 0 { + err = errors.New("server.listen missing") return } - config.directory = filepath.Dir(filename) - config.MOTD = filepath.Join(config.directory, config.MOTD) - for _, lconf := range config.Listeners { - if lconf.Net == "" { - lconf.Net = "tcp" - } + // make + dir := filepath.Dir(filename) + if config.Server.MOTD != "" { + config.Server.MOTD = filepath.Join(dir, config.Server.MOTD) } + config.Server.Database = filepath.Join(dir, config.Server.Database) return } diff --git a/irc/server.go b/irc/server.go index 49c5f4ad..8c9d9ebe 100644 --- a/irc/server.go +++ b/irc/server.go @@ -2,10 +2,7 @@ package irc import ( "bufio" - "crypto/rand" - "crypto/tls" "database/sql" - "encoding/binary" "fmt" _ "github.com/mattn/go-sqlite3" "log" @@ -36,7 +33,7 @@ type Server struct { } func NewServer(config *Config) *Server { - db, err := sql.Open("sqlite3", config.Database()) + db, err := sql.Open("sqlite3", config.Server.Database) if err != nil { log.Fatal(err) } @@ -48,11 +45,11 @@ func NewServer(config *Config) *Server { ctime: time.Now(), db: db, idle: make(chan *Client, 16), - motdFile: config.MOTD, - name: config.Name, + motdFile: config.Server.MOTD, + name: config.Server.Name, newConns: make(chan net.Conn, 16), - operators: config.OperatorsMap(), - password: config.PasswordBytes(), + operators: config.Operators(), + password: config.Server.PasswordBytes(), signals: make(chan os.Signal, 1), timeout: make(chan *Client, 16), } @@ -61,8 +58,8 @@ func NewServer(config *Config) *Server { server.loadChannels() - for _, listenerConf := range config.Listeners { - go server.listen(listenerConf) + for _, addr := range config.Server.Listen { + go server.listen(addr) } return server @@ -169,33 +166,18 @@ func (server *Server) InitPhase() Phase { return Authorization } -func newListener(config ListenerConfig) (net.Listener, error) { - if config.IsTLS() { - certificate, err := tls.LoadX509KeyPair(config.Certificate, config.Key) - if err != nil { - return nil, err - } - return tls.Listen("tcp", config.Address, &tls.Config{ - Certificates: []tls.Certificate{certificate}, - PreferServerCipherSuites: true, - }) - } - - return net.Listen("tcp", config.Address) -} - // // listen goroutine // -func (s *Server) listen(config ListenerConfig) { - listener, err := newListener(config) +func (s *Server) listen(addr string) { + listener, err := net.Listen("tcp", addr) if err != nil { log.Fatal(s, "listen error: ", err) } if DEBUG_SERVER { - log.Printf("%s listening on %s", s, config.Address) + log.Printf("%s listening on %s", s, addr) } for { @@ -214,24 +196,6 @@ func (s *Server) listen(config ListenerConfig) { } } -func (s *Server) GenerateGuestNick() string { - bytes := make([]byte, 8) - for { - _, err := rand.Read(bytes) - if err != nil { - panic(err) - } - randInt, n := binary.Uvarint(bytes) - if n <= 0 { - continue // TODO handle error - } - nick := fmt.Sprintf("guest%d", randInt) - if s.clients.Get(nick) == nil { - return nick - } - } -} - // // server functionality // From 6d194e3d9426a304588314f7e0f6ebc1653d918e Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sat, 1 Mar 2014 14:45:23 -0800 Subject: [PATCH 02/15] update readme --- README.md | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 96ffe620..b69f5470 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,11 @@ and issues are welcome. ## Some Features - follows the RFC where possible -- JSON-based configuration -- server password -- channels with many standard modes -- IRC operators -- TLS support (but better to use stunnel with proxy protocol) -- haproxy PROXY protocol header for hostname setting +- gcfg gitconfig-style configuration +- server password (PASS command) +- channels with most standard modes +- IRC operators (OPER command) +- haproxy [PROXY protocol](http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt) header for hostname setting - passwords stored in bcrypt format - channels that persist between restarts (+P) @@ -23,6 +22,17 @@ I wanted to learn Go. "Ergonomadic" is an anagram of "Go IRC Daemon". +## What about SSL/TLS support? + +Go has a not-yet-verified-as-safe TLS 1.2 implementation. Sadly, many +popular IRC clients will negotiate nothing newer than SSLv2. If you +want to use SSL to protect traffic, I recommend using +[stunnel](https://www.stunnel.org/index.html) version 4.56 with +haproxy's +[PROXY protocol](http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt). This +will allow the server to get the client's original addresses for +hostname lookups. + ## Installation ```sh @@ -33,11 +43,12 @@ ergonomadic -conf '/path/to/config.json' -initdb ## Configuration -See the example `config.json`. Passwords are base64-encoded bcrypted -byte strings. You can generate them with the `genpasswd` subcommand. +See the example `ergonomadic.conf`. Passwords are base64-encoded +bcrypted byte strings. You can generate them with the `genpasswd` +subcommand. ```sh -ergonomadic -genpasswd 'hunter21!' +ergonomadic -genpasswd 'hunter2!' ``` ## Running the Server From b421971b61943e02adf236fbc656be56d7ad19e9 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sat, 1 Mar 2014 15:02:24 -0800 Subject: [PATCH 03/15] move db init/open functions into a single file --- ergonomadic.go | 25 +------------------------ irc/database.go | 32 ++++++++++++++++++++++++++++++++ irc/server.go | 8 +------- 3 files changed, 34 insertions(+), 31 deletions(-) create mode 100644 irc/database.go diff --git a/ergonomadic.go b/ergonomadic.go index e04f9e8a..f47f695d 100644 --- a/ergonomadic.go +++ b/ergonomadic.go @@ -2,12 +2,10 @@ package main import ( "code.google.com/p/go.crypto/bcrypt" - "database/sql" "encoding/base64" "flag" "fmt" "github.com/jlatt/ergonomadic/irc" - _ "github.com/mattn/go-sqlite3" "log" "os" ) @@ -21,27 +19,6 @@ func genPasswd(passwd string) { fmt.Println(encoded) } -func initDB(config *irc.Config) { - os.Remove(config.Database()) - - db, err := sql.Open("sqlite3", config.Database()) - if err != nil { - log.Fatal(err) - } - defer db.Close() - - _, err = db.Exec(` - CREATE TABLE channel ( - name TEXT NOT NULL UNIQUE, - flags TEXT NOT NULL, - key TEXT NOT NULL, - topic TEXT NOT NULL, - user_limit INTEGER DEFAULT 0)`) - if err != nil { - log.Fatal(err) - } -} - func main() { conf := flag.String("conf", "ergonomadic.json", "ergonomadic config file") initdb := flag.Bool("initdb", false, "initialize database") @@ -59,7 +36,7 @@ func main() { } if *initdb { - initDB(config) + irc.InitDB(config.Database()) return } diff --git a/irc/database.go b/irc/database.go new file mode 100644 index 00000000..1ff3bd67 --- /dev/null +++ b/irc/database.go @@ -0,0 +1,32 @@ +package irc + +import ( + "database/sql" + _ "github.com/mattn/go-sqlite3" + "log" + "os" +) + +func InitDB(path string) { + os.Remove(path) + db := OpenDB(path) + defer db.Close() + _, err := db.Exec(` + CREATE TABLE channel ( + name TEXT NOT NULL UNIQUE, + flags TEXT NOT NULL, + key TEXT NOT NULL, + topic TEXT NOT NULL, + user_limit INTEGER DEFAULT 0)`) + if err != nil { + log.Fatal(err) + } +} + +func OpenDB(path string) *sql.DB { + db, err := sql.Open("sqlite3", path) + if err != nil { + log.Fatal(err) + } + return db +} diff --git a/irc/server.go b/irc/server.go index 49c5f4ad..3a4cf69b 100644 --- a/irc/server.go +++ b/irc/server.go @@ -7,7 +7,6 @@ import ( "database/sql" "encoding/binary" "fmt" - _ "github.com/mattn/go-sqlite3" "log" "net" "os" @@ -36,17 +35,12 @@ type Server struct { } func NewServer(config *Config) *Server { - db, err := sql.Open("sqlite3", config.Database()) - if err != nil { - log.Fatal(err) - } - server := &Server{ channels: make(ChannelNameMap), clients: make(ClientNameMap), commands: make(chan Command, 16), ctime: time.Now(), - db: db, + db: OpenDB(config.Database()), idle: make(chan *Client, 16), motdFile: config.MOTD, name: config.Name, From 9aa7debbfeae85d46f1e78d186f6d59a104aad52 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sat, 1 Mar 2014 15:10:04 -0800 Subject: [PATCH 04/15] move password handling into a single file --- ergonomadic.go | 19 ++++++------------- irc/commands.go | 3 +-- irc/config.go | 6 +----- irc/password.go | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 irc/password.go diff --git a/ergonomadic.go b/ergonomadic.go index f47f695d..663d1838 100644 --- a/ergonomadic.go +++ b/ergonomadic.go @@ -1,24 +1,12 @@ package main import ( - "code.google.com/p/go.crypto/bcrypt" - "encoding/base64" "flag" "fmt" "github.com/jlatt/ergonomadic/irc" "log" - "os" ) -func genPasswd(passwd string) { - crypted, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.MinCost) - if err != nil { - log.Fatal(err) - } - encoded := base64.StdEncoding.EncodeToString(crypted) - fmt.Println(encoded) -} - func main() { conf := flag.String("conf", "ergonomadic.json", "ergonomadic config file") initdb := flag.Bool("initdb", false, "initialize database") @@ -26,7 +14,11 @@ func main() { flag.Parse() if *passwd != "" { - genPasswd(*passwd) + encoded, err := irc.GenerateEncodedPassword(*passwd) + if err != nil { + log.Fatal(err) + } + fmt.Println(encoded) return } @@ -37,6 +29,7 @@ func main() { if *initdb { irc.InitDB(config.Database()) + log.Println("database initialized: " + config.Database()) return } diff --git a/irc/commands.go b/irc/commands.go index 8229d217..7f7a14bb 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -1,7 +1,6 @@ package irc import ( - "code.google.com/p/go.crypto/bcrypt" "code.google.com/p/go.text/unicode/norm" "errors" "fmt" @@ -214,7 +213,7 @@ func (cmd *PassCommand) CheckPassword() { if cmd.hash == nil { return } - cmd.err = bcrypt.CompareHashAndPassword(cmd.hash, cmd.password) + cmd.err = ComparePassword(cmd.hash, cmd.password) } func NewPassCommand(args []string) (editableCommand, error) { diff --git a/irc/config.go b/irc/config.go index 07518f00..9aabe12f 100644 --- a/irc/config.go +++ b/irc/config.go @@ -1,7 +1,6 @@ package irc import ( - "encoding/base64" "encoding/json" "log" "os" @@ -9,10 +8,7 @@ import ( ) func decodePassword(password string) []byte { - if password == "" { - return nil - } - bytes, err := base64.StdEncoding.DecodeString(password) + bytes, err := DecodePassword(password) if err != nil { log.Fatal(err) } diff --git a/irc/password.go b/irc/password.go new file mode 100644 index 00000000..1426c402 --- /dev/null +++ b/irc/password.go @@ -0,0 +1,37 @@ +package irc + +import ( + "code.google.com/p/go.crypto/bcrypt" + "encoding/base64" + "errors" +) + +var ( + EmptyPasswordError = errors.New("empty password") +) + +func GenerateEncodedPassword(passwd string) (encoded string, err error) { + if passwd == "" { + err = EmptyPasswordError + return + } + bcrypted, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.MinCost) + if err != nil { + return + } + encoded = base64.StdEncoding.EncodeToString(bcrypted) + return +} + +func DecodePassword(encoded string) (decoded []byte, err error) { + if encoded == "" { + err = EmptyPasswordError + return + } + decoded, err = base64.StdEncoding.DecodeString(encoded) + return +} + +func ComparePassword(hash, password []byte) error { + return bcrypt.CompareHashAndPassword(hash, password) +} From 22c1cfdc3e270a339c17378e2d9c24280987a798 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sat, 1 Mar 2014 21:51:52 -0800 Subject: [PATCH 05/15] chdir instead of relativizing paths in config --- ergonomadic.go | 5 +++++ irc/config.go | 8 -------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/ergonomadic.go b/ergonomadic.go index 6537ff0b..41fe26b3 100644 --- a/ergonomadic.go +++ b/ergonomadic.go @@ -10,6 +10,7 @@ import ( _ "github.com/mattn/go-sqlite3" "log" "os" + "path/filepath" ) func genPasswd(passwd string) { @@ -57,6 +58,10 @@ func main() { if err != nil { log.Fatal(err) } + err = os.Chdir(filepath.Dir(*conf)) + if err != nil { + log.Fatal(err) + } if *initdb { initDB(config) diff --git a/irc/config.go b/irc/config.go index 8fea2974..ba065722 100644 --- a/irc/config.go +++ b/irc/config.go @@ -5,7 +5,6 @@ import ( "encoding/base64" "errors" "log" - "path/filepath" ) type PassConfig struct { @@ -68,12 +67,5 @@ func LoadConfig(filename string) (config *Config, err error) { err = errors.New("server.listen missing") return } - - // make - dir := filepath.Dir(filename) - if config.Server.MOTD != "" { - config.Server.MOTD = filepath.Join(dir, config.Server.MOTD) - } - config.Server.Database = filepath.Join(dir, config.Server.Database) return } From 857907320dcab2a14cc21f492b7e684ac208f391 Mon Sep 17 00:00:00 2001 From: Edmund Huber Date: Sun, 2 Mar 2014 19:56:21 +0100 Subject: [PATCH 06/15] allow underscores in nicks --- irc/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc/constants.go b/irc/constants.go index 83e73efb..eed3d388 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -19,7 +19,7 @@ var ( // regexps ChannelNameExpr = regexp.MustCompile(`^[&!#+][\pL\pN]{1,63}$`) NicknameExpr = regexp.MustCompile( - "^[\\pL\\[\\]{}^`][\\pL\\pN\\[\\]{}^`]{1,31}$") + "^[\\pL\\[\\]{}^`_][\\pL\\pN\\[\\]{}^`_]{1,31}$") ) const ( From 8f0d2b89ee226533b19374599ecc1cbabee622d4 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sun, 2 Mar 2014 11:06:02 -0800 Subject: [PATCH 07/15] v.1.2.12 --- irc/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc/constants.go b/irc/constants.go index eed3d388..f68c0c9e 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -23,7 +23,7 @@ var ( ) const ( - SEM_VER = "ergonomadic-1.2.11" + SEM_VER = "ergonomadic-1.2.12" CRLF = "\r\n" MAX_REPLY_LEN = 512 - len(CRLF) From 6aa6f0aeaae8673f81eb14544fcad132a2811fd0 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sun, 2 Mar 2014 11:36:00 -0800 Subject: [PATCH 08/15] notify clients when the server shuts down --- irc/server.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/irc/server.go b/irc/server.go index 49c5f4ad..896c4ac4 100644 --- a/irc/server.go +++ b/irc/server.go @@ -16,6 +16,7 @@ import ( "runtime/debug" "runtime/pprof" "strings" + "syscall" "time" ) @@ -57,7 +58,7 @@ func NewServer(config *Config) *Server { timeout: make(chan *Client, 16), } - signal.Notify(server.signals, os.Interrupt, os.Kill) + signal.Notify(server.signals, syscall.SIGINT, syscall.SIGHUP) server.loadChannels() @@ -138,14 +139,20 @@ func (server *Server) processCommand(cmd Command) { } } +func (server *Server) Shutdown() { + server.db.Close() + for _, client := range server.clients { + client.Reply(RplNotice(server, client, "shutting down")) + } +} + func (server *Server) Run() { done := false for !done { select { case <-server.signals: - server.db.Close() + server.Shutdown() done = true - continue case conn := <-server.newConns: NewClient(server, conn) From 92e7a7961fc2906f003eafff461a5a0c20a0a14b Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sun, 2 Mar 2014 11:51:29 -0800 Subject: [PATCH 09/15] notify on more signals --- irc/server.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/irc/server.go b/irc/server.go index 3d51192e..ebd5d3f7 100644 --- a/irc/server.go +++ b/irc/server.go @@ -52,14 +52,15 @@ func NewServer(config *Config) *Server { timeout: make(chan *Client, 16), } - signal.Notify(server.signals, syscall.SIGINT, syscall.SIGHUP) - server.loadChannels() for _, listenerConf := range config.Listeners { go server.listen(listenerConf) } + signal.Notify(server.signals, syscall.SIGINT, syscall.SIGHUP, + syscall.SIGTERM, syscall.SIGQUIT) + return server } From d6688a3047e36617c6c2e5c2e66f58effc1cf9b5 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Sun, 2 Mar 2014 13:10:47 -0800 Subject: [PATCH 10/15] allow | in nicks --- irc/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc/constants.go b/irc/constants.go index f68c0c9e..02a78ab2 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -19,7 +19,7 @@ var ( // regexps ChannelNameExpr = regexp.MustCompile(`^[&!#+][\pL\pN]{1,63}$`) NicknameExpr = regexp.MustCompile( - "^[\\pL\\[\\]{}^`_][\\pL\\pN\\[\\]{}^`_]{1,31}$") + "^[\\pL\\[\\]{}^`_][\\pL\\pN\\[\\]{}^`_|]{1,31}$") ) const ( From 1479dbb92e91dff8beb59a8e362bbdb4f6e1bb2e Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Wed, 5 Mar 2014 14:02:57 -0800 Subject: [PATCH 11/15] document example config passwords --- ergonomadic.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ergonomadic.conf b/ergonomadic.conf index 6181e9a7..2f360024 100644 --- a/ergonomadic.conf +++ b/ergonomadic.conf @@ -4,10 +4,10 @@ database = "ergonomadic.db" ; path relative to this file listen = "localhost:6667" ; see `net.Listen` for examples listen = "[::1]:6667" ; multiple `listen`s are allowed. motd = "motd.txt" ; path relative to this file -password = "JDJhJDA0JHJzVFFlNXdOUXNhLmtkSGRUQVVEVHVYWXRKUmdNQ3FKVTRrczRSMTlSWGRPZHRSMVRzQmtt" +password = "JDJhJDA0JHJzVFFlNXdOUXNhLmtkSGRUQVVEVHVYWXRKUmdNQ3FKVTRrczRSMTlSWGRPZHRSMVRzQmtt" ; 'test' [operator "root"] -password = "JDJhJDA0JEhkcm10UlNFRkRXb25iOHZuSDVLZXVBWlpyY0xyNkQ4dlBVc1VMWVk1LlFjWFpQbGxZNUtl" +password = "JDJhJDA0JEhkcm10UlNFRkRXb25iOHZuSDVLZXVBWlpyY0xyNkQ4dlBVc1VMWVk1LlFjWFpQbGxZNUtl" ; 'toor' [debug] net = true From 0d7677e34111dc22cff79da2ffd7956fb6cab55e Mon Sep 17 00:00:00 2001 From: Edmund Huber Date: Wed, 5 Mar 2014 23:40:49 +0100 Subject: [PATCH 12/15] fix up README some more and helpful comment in config --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b69f5470..f88d3754 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ hostname lookups. ```sh go get go install -ergonomadic -conf '/path/to/config.json' -initdb +ergonomadic -conf ergonomadic.conf -initdb ``` ## Configuration @@ -54,7 +54,7 @@ ergonomadic -genpasswd 'hunter2!' ## Running the Server ```sh -ergonomadic -conf '/path/to/config.json' +ergonomadic -conf ergonomadic.conf ``` ## Helpful Documentation From 04bb79a610a32dbd0a247d35b6221ed070533e3c Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Wed, 5 Mar 2014 22:54:50 -0800 Subject: [PATCH 13/15] fix invite message to clients --- irc/channel.go | 3 +-- irc/constants.go | 2 +- irc/reply.go | 4 ++-- irc/server.go | 5 ++--- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/irc/channel.go b/irc/channel.go index a9b45861..bab45747 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -456,9 +456,8 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) { return } - // TODO Modify channel masks inviter.RplInviting(invitee, channel.name) - invitee.Reply(RplInviteMsg(inviter, channel.name)) + invitee.Reply(RplInviteMsg(inviter, invitee, channel.name)) if invitee.flags[Away] { inviter.RplAway(invitee) } diff --git a/irc/constants.go b/irc/constants.go index 02a78ab2..6b4a89ec 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -23,7 +23,7 @@ var ( ) const ( - SEM_VER = "ergonomadic-1.2.12" + SEM_VER = "ergonomadic-1.2.13" CRLF = "\r\n" MAX_REPLY_LEN = 512 - len(CRLF) diff --git a/irc/reply.go b/irc/reply.go index 970876fb..3e69b746 100644 --- a/irc/reply.go +++ b/irc/reply.go @@ -128,8 +128,8 @@ func RplError(message string) string { return NewStringReply(nil, ERROR, ":%s", message) } -func RplInviteMsg(inviter *Client, channel string) string { - return NewStringReply(inviter, INVITE, channel) +func RplInviteMsg(inviter *Client, invitee *Client, channel string) string { + return NewStringReply(inviter, INVITE, "%s :%s", invitee.Nick(), channel) } func RplKick(channel *Channel, client *Client, target *Client, comment string) string { diff --git a/irc/server.go b/irc/server.go index ebd5d3f7..6b770be3 100644 --- a/irc/server.go +++ b/irc/server.go @@ -853,9 +853,8 @@ func (msg *InviteCommand) HandleServer(server *Server) { channel := server.channels.Get(msg.channel) if channel == nil { - name := strings.ToLower(msg.channel) - client.RplInviting(target, name) - target.Reply(RplInviteMsg(client, name)) + client.RplInviting(target, msg.channel) + target.Reply(RplInviteMsg(client, target, msg.channel)) return } From 5c6f8ff92abdd017624aa5787951665bd6377417 Mon Sep 17 00:00:00 2001 From: Jeremy Latt Date: Wed, 5 Mar 2014 23:07:55 -0800 Subject: [PATCH 14/15] improve logging --- ergonomadic.go | 6 +++--- irc/config.go | 2 +- irc/database.go | 4 ++-- irc/server.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ergonomadic.go b/ergonomadic.go index 525e38ac..15504e55 100644 --- a/ergonomadic.go +++ b/ergonomadic.go @@ -18,7 +18,7 @@ func main() { if *passwd != "" { encoded, err := irc.GenerateEncodedPassword(*passwd) if err != nil { - log.Fatal(err) + log.Fatal("encoding error: ", err) } fmt.Println(encoded) return @@ -26,11 +26,11 @@ func main() { config, err := irc.LoadConfig(*conf) if err != nil { - log.Fatal(err) + log.Fatal("error loading config: ", err) } err = os.Chdir(filepath.Dir(*conf)) if err != nil { - log.Fatal(err) + log.Fatal("chdir error: ", err) } if *initdb { diff --git a/irc/config.go b/irc/config.go index 34181acd..6c5aa14d 100644 --- a/irc/config.go +++ b/irc/config.go @@ -13,7 +13,7 @@ type PassConfig struct { func (conf *PassConfig) PasswordBytes() []byte { bytes, err := DecodePassword(conf.Password) if err != nil { - log.Fatal(err) + log.Fatal("decode password error: ", err) } return bytes } diff --git a/irc/database.go b/irc/database.go index 1ff3bd67..c7f9264a 100644 --- a/irc/database.go +++ b/irc/database.go @@ -19,14 +19,14 @@ func InitDB(path string) { topic TEXT NOT NULL, user_limit INTEGER DEFAULT 0)`) if err != nil { - log.Fatal(err) + log.Fatal("initdb error: ", err) } } func OpenDB(path string) *sql.DB { db, err := sql.Open("sqlite3", path) if err != nil { - log.Fatal(err) + log.Fatal("open db error: ", err) } return db } diff --git a/irc/server.go b/irc/server.go index 129a312b..ef2302ff 100644 --- a/irc/server.go +++ b/irc/server.go @@ -66,7 +66,7 @@ func (server *Server) loadChannels() { SELECT name, flags, key, topic, user_limit FROM channel`) if err != nil { - log.Fatal(err) + log.Fatal("error loading channels: ", err) } for rows.Next() { var name, flags, key, topic string From af2c7a0b807e7ca07799eae8e1bd64e4f25ef25d Mon Sep 17 00:00:00 2001 From: Edmund Huber Date: Thu, 6 Mar 2014 08:51:32 +0100 Subject: [PATCH 15/15] change default config file --- ergonomadic.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ergonomadic.go b/ergonomadic.go index 525e38ac..e1b27a1e 100644 --- a/ergonomadic.go +++ b/ergonomadic.go @@ -10,7 +10,7 @@ import ( ) func main() { - conf := flag.String("conf", "ergonomadic.json", "ergonomadic config file") + conf := flag.String("conf", "ergonomadic.conf", "ergonomadic config file") initdb := flag.Bool("initdb", false, "initialize database") passwd := flag.String("genpasswd", "", "bcrypt a password") flag.Parse()