hid: another round of general code cleanups

This commit is contained in:
Přemysl Eric Janouch 2018-07-31 23:37:54 +02:00
parent e77495f316
commit 3610f98d67
1 changed files with 92 additions and 91 deletions

View File

@ -307,7 +307,6 @@ func callSimpleConfigWriteDefault(pathHint string, table []simpleConfigItem) {
// --- Configuration ----------------------------------------------------------- // --- Configuration -----------------------------------------------------------
var configTable = []simpleConfigItem{ var configTable = []simpleConfigItem{
// TODO: Default to the result from os.Hostname (if successful).
{"server_name", "", "Server name"}, {"server_name", "", "Server name"},
{"server_info", "My server", "Brief server description"}, {"server_info", "My server", "Brief server description"},
{"motd", "", "MOTD filename"}, {"motd", "", "MOTD filename"},
@ -488,6 +487,8 @@ func ircParseMessage(line string) *message {
} }
// --- IRC token validation ----------------------------------------------------
// Everything as per RFC 2812 // Everything as per RFC 2812
const ( const (
ircMaxNickname = 9 ircMaxNickname = 9
@ -1028,21 +1029,20 @@ func (c *client) getTLSCertFingerprint() string {
// --- IRC command handling ---------------------------------------------------- // --- IRC command handling ----------------------------------------------------
// XXX: ap doesn't really need to be a slice. func (c *client) makeReply(id int, ap ...interface{}) string {
func (c *client) makeReply(id int, ap []interface{}) string {
s := fmt.Sprintf(":%s %03d %s ", serverName, id, c.nicknameOrStar()) s := fmt.Sprintf(":%s %03d %s ", serverName, id, c.nicknameOrStar())
a := fmt.Sprintf(defaultReplies[id], ap...) a := fmt.Sprintf(defaultReplies[id], ap...)
return s + a return s + a
} }
// XXX: This way we cannot typecheck the arguments, so we must be careful. // XXX: This way we cannot typecheck the arguments, so we should be careful.
func (c *client) sendReply(id int, args ...interface{}) { func (c *client) sendReply(id int, args ...interface{}) {
c.send(c.makeReply(id, args)) c.send(c.makeReply(id, args...))
} }
/// Send a space-separated list of words across as many replies as needed. /// Send a space-separated list of words across as many replies as needed.
func (c *client) sendReplyVector(id int, items []string, args ...interface{}) { func (c *client) sendReplyVector(id int, items []string, args ...interface{}) {
common := c.makeReply(id, args) common := c.makeReply(id, args...)
// We always send at least one message (there might be a client that // We always send at least one message (there might be a client that
// expects us to send this message at least once). // expects us to send this message at least once).
@ -1114,8 +1114,7 @@ func (c *client) sendLUSERS() {
c.sendReply(RPL_LUSERME, nUsers+nServices+nUnknown, 0 /* peer servers */) c.sendReply(RPL_LUSERME, nUsers+nServices+nUnknown, 0 /* peer servers */)
} }
// TODO: Rename back to ircIsThisMe for consistency with kike. func ircIsThisMe(target string) bool {
func isThisMe(target string) bool {
// Target servers can also be matched by their users // Target servers can also be matched by their users
if ircFnmatch(target, serverName) { if ircFnmatch(target, serverName) {
return true return true
@ -1275,7 +1274,7 @@ var ircCapHandlers = map[string]func(*client, *ircCapArgs){
"END": (*client).handleCAPEND, "END": (*client).handleCAPEND,
} }
// XXX: Maybe these also deserve to be methods for client? They operato on // XXX: Maybe these also deserve to be methods for client? They operate on
// global state, though. // global state, though.
func ircHandleCAP(msg *message, c *client) { func ircHandleCAP(msg *message, c *client) {
@ -1418,7 +1417,7 @@ func ircHandleUSERHOST(msg *message, c *client) {
} }
func ircHandleLUSERS(msg *message, c *client) { func ircHandleLUSERS(msg *message, c *client) {
if len(msg.params) > 1 && !isThisMe(msg.params[1]) { if len(msg.params) > 1 && !ircIsThisMe(msg.params[1]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[1]) c.sendReply(ERR_NOSUCHSERVER, msg.params[1])
return return
} }
@ -1426,7 +1425,7 @@ func ircHandleLUSERS(msg *message, c *client) {
} }
func ircHandleMOTD(msg *message, c *client) { func ircHandleMOTD(msg *message, c *client) {
if len(msg.params) > 0 && !isThisMe(msg.params[0]) { if len(msg.params) > 0 && !ircIsThisMe(msg.params[0]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[1]) c.sendReply(ERR_NOSUCHSERVER, msg.params[1])
return return
} }
@ -1435,7 +1434,7 @@ func ircHandleMOTD(msg *message, c *client) {
func ircHandlePING(msg *message, c *client) { func ircHandlePING(msg *message, c *client) {
// XXX: The RFC is pretty incomprehensible about the exact usage. // XXX: The RFC is pretty incomprehensible about the exact usage.
if len(msg.params) > 1 && !isThisMe(msg.params[1]) { if len(msg.params) > 1 && !ircIsThisMe(msg.params[1]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[1]) c.sendReply(ERR_NOSUCHSERVER, msg.params[1])
} else if len(msg.params) < 1 { } else if len(msg.params) < 1 {
c.sendReply(ERR_NOORIGIN) c.sendReply(ERR_NOORIGIN)
@ -1465,7 +1464,7 @@ func ircHandleQUIT(msg *message, c *client) {
} }
func ircHandleTIME(msg *message, c *client) { func ircHandleTIME(msg *message, c *client) {
if len(msg.params) > 0 && !isThisMe(msg.params[0]) { if len(msg.params) > 0 && !ircIsThisMe(msg.params[0]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[0]) c.sendReply(ERR_NOSUCHSERVER, msg.params[0])
return return
} }
@ -1475,7 +1474,7 @@ func ircHandleTIME(msg *message, c *client) {
} }
func ircHandleVERSION(msg *message, c *client) { func ircHandleVERSION(msg *message, c *client) {
if len(msg.params) > 0 && !isThisMe(msg.params[0]) { if len(msg.params) > 0 && !ircIsThisMe(msg.params[0]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[0]) c.sendReply(ERR_NOSUCHSERVER, msg.params[0])
return return
} }
@ -1962,7 +1961,7 @@ func ircHandleNOTICE(msg *message, c *client) {
} }
func ircHandleLIST(msg *message, c *client) { func ircHandleLIST(msg *message, c *client) {
if len(msg.params) > 1 && !isThisMe(msg.params[1]) { if len(msg.params) > 1 && !ircIsThisMe(msg.params[1]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[1]) c.sendReply(ERR_NOSUCHSERVER, msg.params[1])
return return
} }
@ -2013,8 +2012,7 @@ func ircMakeRPLNAMREPLYItem(c, target *client, modes uint) string {
return result return result
} }
// TODO: Consider using *client instead of string as the map key. func ircSendRPLNAMREPLY(c *client, ch *channel, usedNicks map[*client]bool) {
func ircSendRPLNAMREPLY(c *client, ch *channel, usedNicks map[string]bool) {
kind := '=' kind := '='
if 0 != ch.modes&ircChanModeSecret { if 0 != ch.modes&ircChanModeSecret {
kind = '@' kind = '@'
@ -2030,17 +2028,17 @@ func ircSendRPLNAMREPLY(c *client, ch *channel, usedNicks map[string]bool) {
continue continue
} }
if usedNicks != nil { if usedNicks != nil {
usedNicks[ircToCanon(client.nickname)] = true usedNicks[client] = true
} }
nicks = append(nicks, ircMakeRPLNAMREPLYItem(c, client, modes)) nicks = append(nicks, ircMakeRPLNAMREPLYItem(c, client, modes))
} }
c.sendReplyVector(RPL_NAMREPLY, nicks, kind, ch.name, "") c.sendReplyVector(RPL_NAMREPLY, nicks, kind, ch.name, "")
} }
func ircSendDisassociatedNames(c *client, usedNicks map[string]bool) { func ircSendDisassociatedNames(c *client, usedNicks map[*client]bool) {
var nicks []string var nicks []string
for canonNickname, client := range users { for _, client := range users {
if 0 == client.mode&ircUserModeInvisible && !usedNicks[canonNickname] { if 0 == client.mode&ircUserModeInvisible && !usedNicks[client] {
nicks = append(nicks, ircMakeRPLNAMREPLYItem(c, client, 0)) nicks = append(nicks, ircMakeRPLNAMREPLYItem(c, client, 0))
} }
} }
@ -2050,14 +2048,13 @@ func ircSendDisassociatedNames(c *client, usedNicks map[string]bool) {
} }
func ircHandleNAMES(msg *message, c *client) { func ircHandleNAMES(msg *message, c *client) {
if len(msg.params) > 1 && !isThisMe(msg.params[1]) { if len(msg.params) > 1 && !ircIsThisMe(msg.params[1]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[1]) c.sendReply(ERR_NOSUCHSERVER, msg.params[1])
return return
} }
if len(msg.params) == 0 { if len(msg.params) == 0 {
usedNicks := make(map[string]bool) usedNicks := make(map[*client]bool)
for _, ch := range channels { for _, ch := range channels {
if _, present := ch.userModes[c]; present || if _, present := ch.userModes[c]; present ||
0 == ch.modes&(ircChanModePrivate|ircChanModeSecret) { 0 == ch.modes&(ircChanModePrivate|ircChanModeSecret) {
@ -2211,7 +2208,7 @@ func ircHandleWHOIS(msg *message, c *client) {
c.sendReply(ERR_NEEDMOREPARAMS, msg.command) c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
return return
} }
if len(msg.params) > 1 && !isThisMe(msg.params[0]) { if len(msg.params) > 1 && !ircIsThisMe(msg.params[0]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[0]) c.sendReply(ERR_NOSUCHSERVER, msg.params[0])
return return
} }
@ -2248,7 +2245,7 @@ func ircHandleWHOWAS(msg *message, c *client) {
c.sendReply(ERR_NEEDMOREPARAMS, msg.command) c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
return return
} }
if len(msg.params) > 2 && !isThisMe(msg.params[2]) { if len(msg.params) > 2 && !ircIsThisMe(msg.params[2]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[2]) c.sendReply(ERR_NOSUCHSERVER, msg.params[2])
return return
} }
@ -2283,10 +2280,10 @@ func ircHandleTOPIC(msg *message, c *client) {
return return
} }
target := msg.params[0] channelName := msg.params[0]
ch := channels[ircToCanon(target)] ch := channels[ircToCanon(channelName)]
if ch == nil { if ch == nil {
c.sendReply(ERR_NOSUCHCHANNEL, target) c.sendReply(ERR_NOSUCHCHANNEL, channelName)
return return
} }
@ -2297,13 +2294,13 @@ func ircHandleTOPIC(msg *message, c *client) {
modes, present := ch.userModes[c] modes, present := ch.userModes[c]
if !present { if !present {
c.sendReply(ERR_NOTONCHANNEL, target) c.sendReply(ERR_NOTONCHANNEL, channelName)
return return
} }
if 0 != ch.modes&ircChanModeProtectedTopic && if 0 != ch.modes&ircChanModeProtectedTopic &&
0 == modes&ircChanModeOperator { 0 == modes&ircChanModeOperator {
c.sendReply(ERR_CHANOPRIVSNEEDED, target) c.sendReply(ERR_CHANOPRIVSNEEDED, channelName)
return return
} }
@ -2312,28 +2309,28 @@ func ircHandleTOPIC(msg *message, c *client) {
ch.topicTime = time.Now() ch.topicTime = time.Now()
message := fmt.Sprintf(":%s!%s@%s TOPIC %s :%s", message := fmt.Sprintf(":%s!%s@%s TOPIC %s :%s",
c.nickname, c.username, c.hostname, target, ch.topic) c.nickname, c.username, c.hostname, channelName, ch.topic)
ircChannelMulticast(ch, message, nil) ircChannelMulticast(ch, message, nil)
} }
func ircTryPart(c *client, target string, reason string) { func ircTryPart(c *client, channelName string, reason string) {
if reason == "" { if reason == "" {
reason = c.nickname reason = c.nickname
} }
ch := channels[ircToCanon(target)] ch := channels[ircToCanon(channelName)]
if ch == nil { if ch == nil {
c.sendReply(ERR_NOSUCHCHANNEL, target) c.sendReply(ERR_NOSUCHCHANNEL, channelName)
return return
} }
if _, present := ch.userModes[c]; !present { if _, present := ch.userModes[c]; !present {
c.sendReply(ERR_NOTONCHANNEL, target) c.sendReply(ERR_NOTONCHANNEL, channelName)
return return
} }
message := fmt.Sprintf(":%s@%s@%s PART %s :%s", message := fmt.Sprintf(":%s@%s@%s PART %s :%s",
c.nickname, c.username, c.hostname, target, reason) c.nickname, c.username, c.hostname, channelName, reason)
if 0 == ch.modes&ircChanModeQuiet { if 0 == ch.modes&ircChanModeQuiet {
ircChannelMulticast(ch, message, nil) ircChannelMulticast(ch, message, nil)
} else { } else {
@ -2363,35 +2360,34 @@ func ircHandlePART(msg *message, c *client) {
reason = msg.params[1] reason = msg.params[1]
} }
for _, target := range splitString(msg.params[0], ",", true) { for _, channelName := range splitString(msg.params[0], ",", true) {
ircTryPart(c, target, reason) ircTryPart(c, channelName, reason)
} }
} }
// TODO: Undo the rename from channelName to target, also in ircTryPart. func ircTryKick(c *client, channelName, nick, reason string) {
func ircTryKick(c *client, target, nick, reason string) { ch := channels[ircToCanon(channelName)]
ch := channels[ircToCanon(target)]
if ch == nil { if ch == nil {
c.sendReply(ERR_NOSUCHCHANNEL, target) c.sendReply(ERR_NOSUCHCHANNEL, channelName)
return return
} }
if modes, present := ch.userModes[c]; !present { if modes, present := ch.userModes[c]; !present {
c.sendReply(ERR_NOTONCHANNEL, target) c.sendReply(ERR_NOTONCHANNEL, channelName)
return return
} else if 0 == modes&ircChanModeOperator { } else if 0 == modes&ircChanModeOperator {
c.sendReply(ERR_CHANOPRIVSNEEDED, target) c.sendReply(ERR_CHANOPRIVSNEEDED, channelName)
return return
} }
client := users[ircToCanon(nick)] client := users[ircToCanon(nick)]
if _, present := ch.userModes[client]; client == nil || !present { if _, present := ch.userModes[client]; client == nil || !present {
c.sendReply(ERR_USERNOTINCHANNEL, nick, target) c.sendReply(ERR_USERNOTINCHANNEL, nick, channelName)
return return
} }
message := fmt.Sprintf(":%s@%s@%s KICK %s %s :%s", message := fmt.Sprintf(":%s@%s@%s KICK %s %s :%s",
c.nickname, c.username, c.hostname, target, nick, reason) c.nickname, c.username, c.hostname, channelName, nick, reason)
if 0 == ch.modes&ircChanModeQuiet { if 0 == ch.modes&ircChanModeQuiet {
ircChannelMulticast(ch, message, nil) ircChannelMulticast(ch, message, nil)
} else { } else {
@ -2594,7 +2590,7 @@ func ircHandleISON(msg *message, c *client) {
} }
func ircHandleADMIN(msg *message, c *client) { func ircHandleADMIN(msg *message, c *client) {
if len(msg.params) > 0 && !isThisMe(msg.params[0]) { if len(msg.params) > 0 && !ircIsThisMe(msg.params[0]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[0]) c.sendReply(ERR_NOSUCHSERVER, msg.params[0])
return return
} }
@ -2632,7 +2628,7 @@ func ircHandleStatsCommands(c *client) {
} }
// We need to do it this way because of an initialization loop concerning // We need to do it this way because of an initialization loop concerning
// ircHandlers. Workaround proposed by rsc in #1817. // ircHandlers. Workaround proposed by rsc in go #1817.
var ircHandleStatsCommandsIndirect func(c *client) var ircHandleStatsCommandsIndirect func(c *client)
func init() { func init() {
@ -2656,7 +2652,7 @@ func ircHandleSTATS(msg *message, c *client) {
query = msg.params[0][0] query = msg.params[0][0]
} }
if len(msg.params) > 1 && !isThisMe(msg.params[1]) { if len(msg.params) > 1 && !ircIsThisMe(msg.params[1]) {
c.sendReply(ERR_NOSUCHSERVER, msg.params[0]) c.sendReply(ERR_NOSUCHSERVER, msg.params[0])
return return
} }
@ -2677,7 +2673,7 @@ func ircHandleSTATS(msg *message, c *client) {
} }
func ircHandleLINKS(msg *message, c *client) { func ircHandleLINKS(msg *message, c *client) {
if len(msg.params) > 1 && !isThisMe(msg.params[0]) { if len(msg.params) > 1 && !ircIsThisMe(msg.params[0]) {
c.sendReply(ERR_NEEDMOREPARAMS, msg.command) c.sendReply(ERR_NEEDMOREPARAMS, msg.command)
return return
} }
@ -2729,7 +2725,7 @@ func ircHandleDIE(msg *message, c *client) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// TODO: Add an index for IRC_ERR_NOSUCHSERVER validation? // TODO: Add an index for ERR_NOSUCHSERVER validation?
// TODO: Add a minimal parameter count? // TODO: Add a minimal parameter count?
// TODO: Add a field for oper-only commands? Use flags? // TODO: Add a field for oper-only commands? Use flags?
var ircHandlers = map[string]*ircCommand{ var ircHandlers = map[string]*ircCommand{
@ -2818,7 +2814,7 @@ func (c *client) onPrepared(host string, isTLS bool) {
c.hostname = host c.hostname = host
c.address = net.JoinHostPort(host, c.port) c.address = net.JoinHostPort(host, c.port)
// TODO: If we've tried to send any data before now, we need to flushSendQ. // If we tried to send any data before now, we would need to flushSendQ.
go read(c) go read(c)
c.reading = true c.reading = true
} }
@ -2961,7 +2957,6 @@ func prepare(client *client) {
isTLS = detectTLS(sysconn) isTLS = detectTLS(sysconn)
} }
// FIXME: When the client sends no data, we still initialize its conn.
prepared <- preparedEvent{client, host, isTLS} prepared <- preparedEvent{client, host, isTLS}
} }
@ -3126,52 +3121,58 @@ func ircInitializeMOTD() error {
return scanner.Err() return scanner.Err()
} }
type configError struct { type configProcessor struct {
name string // configuration key err error // any error that has occurred so far
err error // description of the issue
} }
func (e *configError) Error() string { func (cp *configProcessor) read(name string, process func(string) string) {
return fmt.Sprintf("invalid configuration value for `%s': %s", if cp.err != nil {
e.name, e.err) return
}
if err := process(config[name]); err != "" {
cp.err = fmt.Errorf("invalid configuration value for `%s': %s",
name, err)
}
} }
// This function handles values that require validation before their first use, // This function handles values that require validation before their first use,
// or some kind of a transformation (such as conversion to an integer) needs // or some kind of a transformation (such as conversion to an integer) needs
// to be done before they can be used directly. // to be done before they can be used directly.
func ircParseConfig() error { func ircParseConfig() error {
// TODO: I think we could further shorten this with lambdas, doing away cp := &configProcessor{}
// with the custom error type completely and at the same time getting rid of cp.read("ping_interval", func(value string) string {
// the key stuttering.
if u, err := strconv.ParseUint( if u, err := strconv.ParseUint(
config["ping_interval"], 10, 32); err != nil { config["ping_interval"], 10, 32); err != nil {
return &configError{"ping_interval", err} return err.Error()
} else if u < 1 { } else if u < 1 {
return &configError{"ping_interval", return "the value is out of range"
errors.New("the value is out of range")}
} else { } else {
pingInterval = uint(u) pingInterval = uint(u)
} }
return ""
})
cp.read("max_connections", func(value string) string {
if i, err := strconv.ParseInt( if i, err := strconv.ParseInt(
config["max_connections"], 10, 32); err != nil { value, 10, 32); err != nil {
return &configError{"max_connections", err} return err.Error()
} else if i < 0 { } else if i < 0 {
return &configError{"max_connections", return "the value is out of range"
errors.New("the value is out of range")}
} else { } else {
maxConnections = int(i) maxConnections = int(i)
} }
return ""
})
cp.read("operators", func(value string) string {
operators = make(map[string]bool) operators = make(map[string]bool)
for _, fp := range splitString(config["operators"], ",", true) { for _, fp := range splitString(value, ",", true) {
if !ircIsValidFingerprint(fp) { if !ircIsValidFingerprint(fp) {
return &configError{"operators", return "invalid fingerprint value"
errors.New("invalid fingerprint valeu")}
} }
operators[strings.ToLower(fp)] = true operators[strings.ToLower(fp)] = true
} }
return nil return ""
})
return cp.err
} }
func ircInitializeServerName() error { func ircInitializeServerName() error {