2017-03-11 13:01:40 +01:00
// Copyright (c) 2017 Daniel Oaks <daniel@danieloaks.net>
// released under the MIT license
package irc
import (
2018-02-02 14:44:52 +01:00
"fmt"
2017-03-11 13:01:40 +01:00
"strings"
2018-02-02 14:44:52 +01:00
"github.com/goshuirc/irc-go/ircfmt"
"github.com/oragono/oragono/irc/sno"
2017-03-11 13:01:40 +01:00
)
2018-02-02 14:44:52 +01:00
const nickservHelp = ` NickServ lets you register and log into a user account .
To register an account :
/ NS REGISTER username [ password ]
Leave out [ password ] if you ' re registering using your client certificate fingerprint .
To login to an account :
/ NS IDENTIFY [ username password ]
Leave out [ username password ] to use your client certificate fingerprint . Otherwise ,
the given username and password will be used . `
// extractParam extracts a parameter from the given string, returning the param and the rest of the string.
func extractParam ( line string ) ( string , string ) {
rawParams := strings . SplitN ( strings . TrimSpace ( line ) , " " , 2 )
param0 := rawParams [ 0 ]
var param1 string
if 1 < len ( rawParams ) {
param1 = strings . TrimSpace ( rawParams [ 1 ] )
}
return param0 , param1
}
2018-02-03 12:15:07 +01:00
// nickservNoticeHandler handles NOTICEs that NickServ receives.
2018-02-05 15:21:08 +01:00
func ( server * Server ) nickservNoticeHandler ( client * Client , message string , rb * ResponseBuffer ) {
2018-02-03 12:15:07 +01:00
// do nothing
}
// nickservPrivmsgHandler handles PRIVMSGs that NickServ receives.
2018-02-05 15:21:08 +01:00
func ( server * Server ) nickservPrivmsgHandler ( client * Client , message string , rb * ResponseBuffer ) {
2018-02-02 14:44:52 +01:00
command , params := extractParam ( message )
command = strings . ToLower ( command )
if command == "help" {
for _ , line := range strings . Split ( nickservHelp , "\n" ) {
2018-02-05 15:21:08 +01:00
rb . Notice ( line )
2018-02-02 14:44:52 +01:00
}
} else if command == "register" {
// get params
username , passphrase := extractParam ( params )
// fail out if we need to
if username == "" {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "No username supplied" ) )
2018-02-02 14:44:52 +01:00
return
}
2018-02-05 15:21:08 +01:00
server . nickservRegisterHandler ( client , username , passphrase , rb )
2018-02-03 12:38:28 +01:00
} else if command == "identify" {
// get params
username , passphrase := extractParam ( params )
2018-02-02 14:44:52 +01:00
2018-02-05 15:21:08 +01:00
server . nickservIdentifyHandler ( client , username , passphrase , rb )
2018-02-03 12:38:28 +01:00
} else {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "Command not recognised. To see the available commands, run /NS HELP" ) )
2018-02-03 12:38:28 +01:00
}
}
2018-02-05 15:21:08 +01:00
func ( server * Server ) nickservRegisterHandler ( client * Client , username , passphrase string , rb * ResponseBuffer ) {
2018-02-03 12:38:28 +01:00
certfp := client . certfp
if passphrase == "" && certfp == "" {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "You need to either supply a passphrase or be connected via TLS with a client cert" ) )
2018-02-03 12:38:28 +01:00
return
}
2018-02-11 11:30:40 +01:00
if ! server . AccountConfig ( ) . Registration . Enabled {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "Account registration has been disabled" ) )
2018-02-03 12:38:28 +01:00
return
}
if client . LoggedIntoAccount ( ) {
2018-02-11 11:30:40 +01:00
if server . AccountConfig ( ) . Registration . AllowMultiplePerConnection {
server . accounts . Logout ( client )
2018-02-03 12:38:28 +01:00
} else {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "You're already logged into an account" ) )
2018-02-02 14:44:52 +01:00
return
}
2018-02-03 12:38:28 +01:00
}
2018-02-02 14:44:52 +01:00
2018-02-03 12:38:28 +01:00
// get and sanitise account name
account := strings . TrimSpace ( username )
casefoldedAccount , err := CasefoldName ( account )
// probably don't need explicit check for "*" here... but let's do it anyway just to make sure
if err != nil || username == "*" {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "Account name is not valid" ) )
2018-02-03 12:38:28 +01:00
return
}
// account could not be created and relevant numerics have been dispatched, abort
if err != nil {
if err != errAccountCreation {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "Account registration failed" ) )
2018-02-02 14:44:52 +01:00
}
2018-02-03 12:38:28 +01:00
return
}
2018-02-02 14:44:52 +01:00
2018-02-11 11:30:40 +01:00
err = server . accounts . Register ( client , account , "" , "" , passphrase , client . certfp )
if err == nil {
err = server . accounts . Verify ( client , casefoldedAccount , "" )
}
2018-02-02 14:44:52 +01:00
2018-02-03 12:38:28 +01:00
// details could not be stored and relevant numerics have been dispatched, abort
if err != nil {
errMsg := "Could not register"
if err == errCertfpAlreadyExists {
errMsg = "An account already exists for your certificate fingerprint"
2018-02-11 11:30:40 +01:00
} else if err == errAccountAlreadyRegistered {
errMsg = "Account already exists"
2018-02-03 12:38:28 +01:00
}
2018-02-11 11:30:40 +01:00
rb . Notice ( client . t ( errMsg ) )
2018-02-03 12:38:28 +01:00
return
}
2018-02-02 14:44:52 +01:00
2018-02-11 11:30:40 +01:00
rb . Notice ( client . t ( "Account created" ) )
rb . Add ( nil , server . name , RPL_LOGGEDIN , client . nick , client . nickMaskString , casefoldedAccount , fmt . Sprintf ( client . t ( "You are now logged in as %s" ) , casefoldedAccount ) )
rb . Add ( nil , server . name , RPL_SASLSUCCESS , client . nick , client . t ( "Authentication successful" ) )
server . snomasks . Send ( sno . LocalAccounts , fmt . Sprintf ( ircfmt . Unescape ( "Account registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]" ) , casefoldedAccount , client . nickMaskString ) )
2018-02-03 12:38:28 +01:00
}
2018-02-02 14:44:52 +01:00
2018-02-05 15:21:08 +01:00
func ( server * Server ) nickservIdentifyHandler ( client * Client , username , passphrase string , rb * ResponseBuffer ) {
2018-02-03 12:38:28 +01:00
// fail out if we need to
2018-02-11 11:30:40 +01:00
if ! server . AccountConfig ( ) . AuthenticationEnabled {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "Login has been disabled" ) )
2018-02-03 12:38:28 +01:00
return
}
// try passphrase
if username != "" && passphrase != "" {
// keep it the same as in the ACC CREATE stage
2018-02-11 11:30:40 +01:00
accountName , err := CasefoldName ( username )
2018-02-03 12:38:28 +01:00
if err != nil {
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "Could not login with your username/password" ) )
2018-02-03 12:38:28 +01:00
return
}
2018-02-11 11:30:40 +01:00
err = server . accounts . AuthenticateByPassphrase ( client , accountName , passphrase )
2018-02-03 12:38:28 +01:00
if err == nil {
2018-02-05 15:21:08 +01:00
rb . Notice ( fmt . Sprintf ( client . t ( "You're now logged in as %s" ) , accountName ) )
2018-02-02 14:44:52 +01:00
return
}
2018-02-03 12:38:28 +01:00
}
2018-02-02 14:44:52 +01:00
2018-02-03 12:38:28 +01:00
// try certfp
2018-02-11 11:30:40 +01:00
if client . certfp != "" {
err := server . accounts . AuthenticateByCertFP ( client )
2018-02-03 12:38:28 +01:00
if err == nil {
2018-02-11 11:30:40 +01:00
rb . Notice ( fmt . Sprintf ( client . t ( "You're now logged in as %s" ) , client . AccountName ( ) ) )
// TODO more notices?
2018-02-03 12:38:28 +01:00
return
}
2018-02-02 14:44:52 +01:00
}
2018-02-03 12:38:28 +01:00
2018-02-05 15:21:08 +01:00
rb . Notice ( client . t ( "Could not login with your TLS certificate or supplied username/password" ) )
2017-03-11 13:01:40 +01:00
}