From 04442ddef14d503b4dd03ed658f7bc35e6fd8230 Mon Sep 17 00:00:00 2001 From: Daniel Oaks Date: Mon, 20 Jun 2016 22:53:45 +1000 Subject: [PATCH] Stuff is no longer completely broken. Just mostly. --- CHANGELOG.md | 1 + irc/capability.go | 4 +- irc/client.go | 8 +-- irc/commandhandlers.go | 1 + irc/commands.go | 7 ++- irc/isupport.go | 3 +- irc/modes.go | 112 +++++++++++++++++++++++++++++------------ irc/numerics.go | 1 + irc/server.go | 57 ++++++++++++--------- 9 files changed, 125 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 908e5a53..88d804a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Initial release of Oragono! * Default channel modes are now (`+nt`), matching most other IRCds. * CLI argument names made more consistent with typical software. * ONICK: Renamed to SANICK to be more consistent with other IRCds. +* USER: Prepend usernames set by the USER command with `"~"`. ### Removed * Gitconfig config format completely removed and replaced with YAML. diff --git a/irc/capability.go b/irc/capability.go index 503e91f6..2079f72d 100644 --- a/irc/capability.go +++ b/irc/capability.go @@ -105,14 +105,14 @@ func capHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // make sure all capabilities actually exist for capability := range capabilities { if !SupportedCapabilities[capability] { - client.Send(nil, server.nameString, "CAP", client.nickString, subCommand, capString) + client.Send(nil, server.nameString, "CAP", client.nickString, "NAK", capString) return false } } for capability := range capabilities { client.capabilities[capability] = true } - client.Send(nil, server.nameString, "CAP", client.nickString, subCommand, capString) + client.Send(nil, server.nameString, "CAP", client.nickString, "ACK", capString) case "END": if !client.registered { diff --git a/irc/client.go b/irc/client.go index 996a07f2..e2bbc4a6 100644 --- a/irc/client.go +++ b/irc/client.go @@ -56,6 +56,7 @@ func NewClient(server *Server, conn net.Conn) *Client { flags: make(map[UserMode]bool), server: server, socket: &socket, + nickString: "*", // * is used until actual nick is given } client.Touch() go client.run() @@ -68,7 +69,6 @@ func NewClient(server *Server, conn net.Conn) *Client { // func (client *Client) run() { - var command Command var err error var isExiting bool var line string @@ -88,14 +88,14 @@ func (client *Client) run() { msg, err = ircmsg.ParseLine(line) if err != nil { - client.Quit("received malformed command") + client.Quit("received malformed line") break } cmd, exists := Commands[msg.Command] if !exists { - //TODO(dan): Reply with 400 or whatever unknown cmd is - client.Quit("Received unknown command") + client.Send(nil, client.server.nameString, ERR_UNKNOWNCOMMAND, client.nickString, msg.Command, "Unknown command") + continue } isExiting = cmd.Run(client.server, client, msg) diff --git a/irc/commandhandlers.go b/irc/commandhandlers.go index e8f8fb13..51fdc761 100644 --- a/irc/commandhandlers.go +++ b/irc/commandhandlers.go @@ -127,6 +127,7 @@ type ChannelModeCommand struct { changes ChannelModeChanges } +/* // MODE ( "+" / "-" )? *( "+" / "-" / ) * func ParseChannelModeCommand(channel Name, args []string) (Command, error) { cmd := &ChannelModeCommand{ diff --git a/irc/commands.go b/irc/commands.go index 8aa30a87..a74fbfa7 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -23,7 +23,7 @@ func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) b // command silently ignored return false } - if (!cmd.oper) && (!client.flags[Operator]) { + if cmd.oper && !client.flags[Operator] { client.Send(nil, server.nameString, ERR_NOPRIVILEGES, client.nickString, "Permission Denied - You're not an IRC operator") return false } @@ -87,11 +87,10 @@ var Commands = map[string]Command{ handler: listHandler, minParams: 0, }, - /*TODO(dan): ADD THIS BACK. "MODE": Command{ handler: modeHandler, minParams: 1, - },*/ + }, "MOTD": Command{ handler: motdHandler, minParams: 0, @@ -153,7 +152,7 @@ var Commands = map[string]Command{ usablePreReg: true, minParams: 0, }, - /*TODO(dan): ADD THIS BACK IN + /*TODO(dan): Add this back in "THEATRE": Command{ handler: theatreHandler, minParams: 1, diff --git a/irc/isupport.go b/irc/isupport.go index 41e158c7..852a7a9c 100644 --- a/irc/isupport.go +++ b/irc/isupport.go @@ -69,6 +69,7 @@ func (il *ISupportList) RegenerateCachedReply() { func (client *Client) RplISupport() { for _, tokenline := range client.server.isupport.CachedReply { - client.Send(nil, client.server.nameString, RPL_ISUPPORT, tokenline...) + // ugly trickery ahead + client.Send(nil, client.server.nameString, RPL_ISUPPORT, append([]string{client.nickString}, tokenline...)...) } } diff --git a/irc/modes.go b/irc/modes.go index a315d51f..08515c11 100644 --- a/irc/modes.go +++ b/irc/modes.go @@ -7,6 +7,8 @@ package irc import ( "strings" + + "github.com/DanielOaks/girc-go/ircmsg" ) // user mode flags @@ -126,60 +128,104 @@ var ( // commands // -/* -func (m *ModeCommand) HandleServer(s *Server) { - client := m.Client() - target := s.clients.Get(m.nickname) +// MODE [ [...]] +func modeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { + name := NewName(msg.Params[0]) + if name.IsChannel() { + // return cmodeHandler(server, client, msg) + client.Notice("CMODEs are not yet supported!") + return false + } else { + return umodeHandler(server, client, msg) + } +} + +// MODE [ [...]] +func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { + nickname := NewName(msg.Params[0]) + + target := server.clients.Get(nickname) if target == nil { - client.ErrNoSuchNick(m.nickname) - return + client.Send(nil, server.nameString, ERR_NOSUCHNICK, client.nickString, msg.Params[0], "No such nick") + return false } + //TODO(dan): restricting to Operator here should be done with SAMODE only + // point SAMODE at this handler too, if they are operator and SAMODE was called then fine if client != target && !client.flags[Operator] { - client.ErrUsersDontMatch() - return + if len(msg.Params) > 1 { + client.Send(nil, server.nameString, ERR_USERSDONTMATCH, client.nickString, "Can't change modes for other users") + } else { + client.Send(nil, server.nameString, ERR_USERSDONTMATCH, client.nickString, "Can't view modes for other users") + } + return false } - changes := make(ModeChanges, 0, len(m.changes)) + // assemble changes + changes := make(ModeChanges, 0) - for _, change := range m.changes { - switch change.mode { - case Invisible, ServerNotice, WallOps: - switch change.op { - case Add: - if target.flags[change.mode] { - continue - } - target.flags[change.mode] = true - changes = append(changes, change) + if len(msg.Params) > 1 { + modeArg := msg.Params[0] + op := ModeOp(modeArg[0]) + if (op == Add) || (op == Remove) { + modeArg = modeArg[1:] + } else { + client.Send(nil, server.nameString, ERR_UNKNOWNERROR, client.nickString, "MODE", "Mode string could not be parsed correctly") + return false + } - case Remove: - if !target.flags[change.mode] { - continue - } - delete(target.flags, change.mode) - changes = append(changes, change) + for _, mode := range modeArg { + if mode == '-' || mode == '+' { + op = ModeOp(mode) + continue } + changes = append(changes, &ModeChange{ + mode: UserMode(mode), + op: op, + }) + } - case Operator, LocalOperator: - if change.op == Remove { - if !target.flags[change.mode] { - continue + for _, change := range changes { + switch change.mode { + case Invisible, ServerNotice, WallOps: + switch change.op { + case Add: + if target.flags[change.mode] { + continue + } + target.flags[change.mode] = true + changes = append(changes, change) + + case Remove: + if !target.flags[change.mode] { + continue + } + delete(target.flags, change.mode) + changes = append(changes, change) + } + + case Operator, LocalOperator: + if change.op == Remove { + if !target.flags[change.mode] { + continue + } + delete(target.flags, change.mode) + changes = append(changes, change) } - delete(target.flags, change.mode) - changes = append(changes, change) } } } if len(changes) > 0 { - client.Reply(RplModeChanges(client, target, changes)) + client.Send(nil, client.nickMaskString, "MODE", target.nickString, changes.String()) } else if client == target { - client.RplUModeIs(client) + client.Send(nil, target.nickMaskString, RPL_UMODEIS, target.nickString, target.ModeString()) } + return false } +/* func (msg *ChannelModeCommand) HandleServer(server *Server) { client := msg.Client() channel := server.channels.Get(msg.channel) diff --git a/irc/numerics.go b/irc/numerics.go index 1fca0632..3f4ce6cb 100644 --- a/irc/numerics.go +++ b/irc/numerics.go @@ -90,6 +90,7 @@ const ( RPL_USERS = "393" RPL_ENDOFUSERS = "394" RPL_NOUSERS = "395" + ERR_UNKNOWNERROR = "400" ERR_NOSUCHNICK = "401" ERR_NOSUCHSERVER = "402" ERR_NOSUCHCHANNEL = "403" diff --git a/irc/server.go b/irc/server.go index afd8c6dd..44cf94bd 100644 --- a/irc/server.go +++ b/irc/server.go @@ -287,11 +287,11 @@ func (s *Server) tryRegister(c *Client) { // send welcome text //NOTE(dan): we specifically use the NICK here instead of the nickmask // see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask - c.Send(nil, s.nameString, RPL_WELCOME, fmt.Sprintf("Welcome to the Internet Relay Network %s", c.nickString)) - c.Send(nil, s.nameString, RPL_YOURHOST, fmt.Sprintf("Your host is %s, running version %s", s.nameString, SEM_VER)) - c.Send(nil, s.nameString, RPL_CREATED, fmt.Sprintf("This server was created %s", s.ctime.Format(time.RFC1123))) + c.Send(nil, s.nameString, RPL_WELCOME, c.nickString, fmt.Sprintf("Welcome to the Internet Relay Network %s", c.nickString)) + c.Send(nil, s.nameString, RPL_YOURHOST, c.nickString, fmt.Sprintf("Your host is %s, running version %s", s.nameString, SEM_VER)) + c.Send(nil, s.nameString, RPL_CREATED, c.nickString, fmt.Sprintf("This server was created %s", s.ctime.Format(time.RFC1123))) //TODO(dan): Look at adding last optional [] parameter - c.Send(nil, s.nameString, RPL_MYINFO, s.nameString, SEM_VER, supportedUserModesString, supportedChannelModesString) + c.Send(nil, s.nameString, RPL_MYINFO, c.nickString, s.nameString, SEM_VER, supportedUserModesString, supportedChannelModesString) c.RplISupport() s.MOTD(c) } @@ -332,6 +332,12 @@ func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { return false } + // if no password exists, skip checking + if len(server.password) == 0 { + client.authorized = true + return false + } + // check the provided password password := []byte(msg.Params[0]) if ComparePassword(server.password, password) != nil { @@ -383,8 +389,8 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // we do it this way to ONLY replace what hasn't already been set server.clients.Remove(client) - if client.username != "" { - client.username = Name(msg.Params[0]) + if !client.HasUsername() { + client.username = Name("~" + msg.Params[0]) client.updateNickMask() } if client.realname != "" { @@ -446,6 +452,7 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { for i, nameString := range channels { name = Name(nameString) if !name.IsChannel() { + fmt.Println("ISN'T CHANNEL NAME:", nameString) client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, nameString, "No such channel") continue } @@ -551,10 +558,10 @@ func (client *Client) WhoisChannelsNames(target *Client) []string { // WHOIS [ ] *( "," ) func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { var masksString string - var target string + //var target string if len(msg.Params) > 1 { - target = msg.Params[0] + //target = msg.Params[0] masksString = msg.Params[1] } else { masksString = msg.Params[0] @@ -639,10 +646,10 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { //TODO(dan): is this used and would I put this param in the Modern doc? // if not, can we remove it? - var operatorOnly bool - if len(msg.Params) > 1 && msg.Params[1] == "o" { - operatorOnly = true - } + //var operatorOnly bool + //if len(msg.Params) > 1 && msg.Params[1] == "o" { + // operatorOnly = true + //} if mask == "" { for _, channel := range server.channels { @@ -679,7 +686,7 @@ func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { } client.flags[Operator] = true - client.Send(nil, server.nameString, RPL_YOUREOPER, client.nickString, "You are not an IRC operator") + client.Send(nil, server.nameString, RPL_YOUREOPER, client.nickString, "You are now an IRC operator") //TODO(dan): Should this be sent automagically as part of setting the flag/mode? modech := ModeChanges{&ModeChange{ mode: Operator, @@ -740,10 +747,10 @@ func isonHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { // MOTD [] func motdHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { //TODO(dan): hook this up when we have multiple servers I guess??? - var target string - if len(msg.Params) > 0 { - target = msg.Params[0] - } + //var target string + //if len(msg.Params) > 0 { + // target = msg.Params[0] + //} server.MOTD(client) return false @@ -902,10 +909,10 @@ func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { if len(msg.Params) > 0 { channels = strings.Split(msg.Params[0], ",") } - var target string - if len(msg.Params) > 1 { - target = msg.Params[1] - } + //var target string + //if len(msg.Params) > 1 { + // target = msg.Params[1] + //} if len(channels) == 0 { for _, channel := range server.channels { @@ -1002,10 +1009,10 @@ func whowasHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool { if len(msg.Params) > 1 { count, _ = strconv.ParseInt(msg.Params[1], 10, 64) } - var target string - if len(msg.Params) > 2 { - target = msg.Params[2] - } + //var target string + //if len(msg.Params) > 2 { + // target = msg.Params[2] + //} for _, nickname := range nicknames { results := server.whoWas.Find(Name(nickname), count) if len(results) == 0 {