2016-06-15 13:50:56 +02:00
// Copyright (c) 2012-2014 Jeremy Latt
// Copyright (c) 2014-2015 Edmund Huber
2017-03-27 14:15:02 +02:00
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
2016-06-15 13:50:56 +02:00
// released under the MIT license
2014-02-08 22:18:11 +01:00
package main
import (
2019-02-02 22:21:46 +01:00
"bufio"
2022-02-20 04:03:30 +01:00
"encoding/json"
2014-02-24 07:21:39 +01:00
"fmt"
2014-02-09 16:53:42 +01:00
"log"
2019-02-02 22:21:46 +01:00
"os"
2017-06-30 23:07:48 +02:00
"strings"
2016-04-12 15:00:09 +02:00
"syscall"
2014-03-13 02:57:00 +01:00
2018-08-06 04:51:39 +02:00
"golang.org/x/crypto/bcrypt"
2016-04-12 15:00:09 +02:00
"golang.org/x/crypto/ssh/terminal"
2021-05-25 06:34:38 +02:00
"github.com/docopt/docopt-go"
"github.com/ergochat/ergo/irc"
"github.com/ergochat/ergo/irc/logger"
"github.com/ergochat/ergo/irc/mkcerts"
2022-02-20 04:03:30 +01:00
"github.com/ergochat/ergo/irc/passwd"
2016-04-12 15:00:09 +02:00
)
2014-03-13 02:57:00 +01:00
2020-05-21 17:25:30 +02:00
// set via linker flags, either by make or by goreleaser:
var commit = "" // git hash
var version = "" // tagged version
2018-04-09 02:08:54 +02:00
2018-04-10 19:21:51 +02:00
// get a password from stdin from the user
func getPassword ( ) string {
2019-02-02 22:21:46 +01:00
fd := int ( os . Stdin . Fd ( ) )
if terminal . IsTerminal ( fd ) {
bytePassword , err := terminal . ReadPassword ( int ( syscall . Stdin ) )
if err != nil {
log . Fatal ( "Error reading password:" , err . Error ( ) )
}
return string ( bytePassword )
2018-04-10 19:21:51 +02:00
}
2019-02-02 22:21:46 +01:00
reader := bufio . NewReader ( os . Stdin )
text , _ := reader . ReadString ( '\n' )
2019-02-02 23:08:12 +01:00
return strings . TrimSpace ( text )
2018-04-10 19:21:51 +02:00
}
2019-09-12 02:15:34 +02:00
func fileDoesNotExist ( file string ) bool {
if _ , err := os . Stat ( file ) ; os . IsNotExist ( err ) {
return true
}
return false
}
2021-05-25 06:34:38 +02:00
// implements the `ergo mkcerts` command
2019-06-28 16:45:34 +02:00
func doMkcerts ( configFile string , quiet bool ) {
config , err := irc . LoadRawConfig ( configFile )
if err != nil {
log . Fatal ( err )
}
if ! quiet {
log . Println ( "making self-signed certificates" )
}
certToKey := make ( map [ string ] string )
for name , conf := range config . Server . Listeners {
if conf . TLS . Cert == "" {
continue
}
existingKey , ok := certToKey [ conf . TLS . Cert ]
if ok {
if existingKey == conf . TLS . Key {
continue
} else {
2019-09-12 02:15:34 +02:00
log . Fatal ( "Conflicting TLS key files for " , conf . TLS . Cert )
2019-06-28 16:45:34 +02:00
}
}
if ! quiet {
log . Printf ( " making cert for %s listener\n" , name )
}
host := config . Server . Name
cert , key := conf . TLS . Cert , conf . TLS . Key
2019-09-12 02:15:34 +02:00
if ! ( fileDoesNotExist ( cert ) && fileDoesNotExist ( key ) ) {
log . Fatalf ( "Preexisting TLS cert and/or key files: %s %s" , cert , key )
}
2021-05-25 06:34:38 +02:00
err := mkcerts . CreateCert ( "Ergo" , host , cert , key )
2019-06-28 16:45:34 +02:00
if err == nil {
if ! quiet {
log . Printf ( " Certificate created at %s : %s\n" , cert , key )
}
certToKey [ cert ] = key
} else {
log . Fatal ( " Could not create certificate:" , err . Error ( ) )
}
}
}
2022-02-20 04:03:30 +01:00
func doCheckPasswd ( ) ( returnCode int ) {
reader := bufio . NewReader ( os . Stdin )
text , err := reader . ReadBytes ( '\n' )
if err != nil {
log . Fatal ( err )
}
var hashAndPassword [ 2 ] string
err = json . Unmarshal ( text , & hashAndPassword )
if err != nil {
log . Fatal ( err )
}
if passwd . CompareHashAndPassword ( [ ] byte ( hashAndPassword [ 0 ] ) , [ ] byte ( hashAndPassword [ 1 ] ) ) != nil {
return 1
}
return 0
}
2016-04-12 15:00:09 +02:00
func main ( ) {
2020-05-21 17:25:30 +02:00
irc . SetVersionString ( version , commit )
2021-05-25 06:34:38 +02:00
usage := ` ergo .
2016-04-12 15:00:09 +02:00
Usage :
2021-05-25 06:34:38 +02:00
ergo initdb [ -- conf < filename > ] [ -- quiet ]
ergo upgradedb [ -- conf < filename > ] [ -- quiet ]
ergo importdb < database . json > [ -- conf < filename > ] [ -- quiet ]
ergo genpasswd [ -- conf < filename > ] [ -- quiet ]
ergo mkcerts [ -- conf < filename > ] [ -- quiet ]
2022-02-20 04:03:30 +01:00
ergo checkpasswd
2021-05-25 06:34:38 +02:00
ergo run [ -- conf < filename > ] [ -- quiet ] [ -- smoke ]
ergo - h | -- help
ergo -- version
2016-04-12 15:00:09 +02:00
Options :
-- conf < filename > Configuration file to use [ default : ircd . yaml ] .
2016-09-19 14:30:45 +02:00
-- quiet Don ' t show startup / shutdown lines .
2016-04-12 15:00:09 +02:00
- h -- help Show this screen .
-- version Show version . `
2020-05-21 17:25:30 +02:00
arguments , _ := docopt . ParseArgs ( usage , nil , irc . Ver )
2016-04-12 15:00:09 +02:00
2020-05-08 07:16:49 +02:00
// don't require a config file for genpasswd
2016-04-12 15:00:09 +02:00
if arguments [ "genpasswd" ] . ( bool ) {
2019-02-02 22:21:46 +01:00
var password string
fd := int ( os . Stdin . Fd ( ) )
if terminal . IsTerminal ( fd ) {
fmt . Print ( "Enter Password: " )
password = getPassword ( )
fmt . Print ( "\n" )
fmt . Print ( "Reenter Password: " )
confirm := getPassword ( )
fmt . Print ( "\n" )
if confirm != password {
log . Fatal ( "passwords do not match" )
}
} else {
password = getPassword ( )
2016-04-12 15:00:09 +02:00
}
2021-10-29 01:29:55 +02:00
if err := irc . ValidatePassphrase ( password ) ; err != nil {
log . Printf ( "WARNING: this password contains characters that may cause problems with your IRC client software.\n" )
log . Printf ( "We strongly recommend choosing a different password.\n" )
}
2018-08-06 04:51:39 +02:00
hash , err := bcrypt . GenerateFromPassword ( [ ] byte ( password ) , bcrypt . MinCost )
2014-03-02 00:10:04 +01:00
if err != nil {
2017-03-06 06:50:23 +01:00
log . Fatal ( "encoding error:" , err . Error ( ) )
2014-03-02 00:10:04 +01:00
}
2019-02-02 22:21:46 +01:00
fmt . Print ( string ( hash ) )
if terminal . IsTerminal ( fd ) {
fmt . Println ( )
}
2018-12-28 19:45:55 +01:00
return
2019-06-28 16:45:34 +02:00
} else if arguments [ "mkcerts" ] . ( bool ) {
doMkcerts ( arguments [ "--conf" ] . ( string ) , arguments [ "--quiet" ] . ( bool ) )
return
2022-02-20 04:03:30 +01:00
} else if arguments [ "checkpasswd" ] . ( bool ) {
os . Exit ( doCheckPasswd ( ) )
2018-12-28 19:45:55 +01:00
}
configfile := arguments [ "--conf" ] . ( string )
config , err := irc . LoadConfig ( configfile )
2020-05-06 11:53:45 +02:00
if err != nil {
_ , isCertError := err . ( * irc . CertKeyError )
if ! ( isCertError && arguments [ "mkcerts" ] . ( bool ) ) {
log . Fatal ( "Config file did not load successfully: " , err . Error ( ) )
}
2018-12-28 19:45:55 +01:00
}
logman , err := logger . NewManager ( config . Logging )
if err != nil {
log . Fatal ( "Logger did not load successfully:" , err . Error ( ) )
}
if arguments [ "initdb" ] . ( bool ) {
2020-10-02 22:48:37 +02:00
err = irc . InitDB ( config . Datastore . Path )
if err != nil {
log . Fatal ( "Error while initializing db:" , err . Error ( ) )
}
2016-09-19 14:30:45 +02:00
if ! arguments [ "--quiet" ] . ( bool ) {
log . Println ( "database initialized: " , config . Datastore . Path )
}
2016-04-12 15:00:09 +02:00
} else if arguments [ "upgradedb" ] . ( bool ) {
2018-04-16 22:28:31 +02:00
err = irc . UpgradeDB ( config )
if err != nil {
log . Fatal ( "Error while upgrading db:" , err . Error ( ) )
}
2016-09-19 14:30:45 +02:00
if ! arguments [ "--quiet" ] . ( bool ) {
log . Println ( "database upgraded: " , config . Datastore . Path )
}
2020-10-02 22:48:37 +02:00
} else if arguments [ "importdb" ] . ( bool ) {
err = irc . ImportDB ( config , arguments [ "<database.json>" ] . ( string ) )
if err != nil {
log . Fatal ( "Error while importing db:" , err . Error ( ) )
}
2016-04-12 15:00:09 +02:00
} else if arguments [ "run" ] . ( bool ) {
2017-03-06 11:15:28 +01:00
if ! arguments [ "--quiet" ] . ( bool ) {
2020-05-21 17:25:30 +02:00
logman . Info ( "server" , fmt . Sprintf ( "%s starting" , irc . Ver ) )
2017-03-06 11:15:28 +01:00
}
2017-04-30 04:35:07 +02:00
2017-06-30 23:07:48 +02:00
// warning if running a non-final version
2020-05-21 17:25:30 +02:00
if strings . Contains ( irc . Ver , "unreleased" ) {
2021-05-25 06:34:38 +02:00
logman . Warning ( "server" , "You are currently running an unreleased beta version of Ergo that may be unstable and could corrupt your database.\nIf you are running a production network, please download the latest build from https://ergo.chat/downloads.html and run that instead." )
2017-06-30 23:07:48 +02:00
}
2017-11-19 01:42:40 +01:00
server , err := irc . NewServer ( config , logman )
2017-03-06 06:50:23 +01:00
if err != nil {
2019-02-03 03:12:17 +01:00
logman . Error ( "server" , fmt . Sprintf ( "Could not load server: %s" , err . Error ( ) ) )
2019-02-03 03:15:41 +01:00
os . Exit ( 1 )
2016-11-06 04:47:13 +01:00
}
2020-03-24 14:53:04 +01:00
if ! arguments [ "--smoke" ] . ( bool ) {
server . Run ( )
}
2014-03-13 02:57:00 +01:00
}
2014-02-08 22:18:11 +01:00
}