mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-26 05:49:25 +01:00
urgh this should not even be commited yet, this will all be squashed out
This commit is contained in:
parent
dbca03e948
commit
e83283e7fd
111
irc/client.go
111
irc/client.go
@ -9,6 +9,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -65,51 +67,44 @@ func NewClient(server *Server, conn net.Conn) *Client {
|
|||||||
func (client *Client) run() {
|
func (client *Client) run() {
|
||||||
var command Command
|
var command Command
|
||||||
var err error
|
var err error
|
||||||
|
var isExiting bool
|
||||||
var line string
|
var line string
|
||||||
|
var msg ircmsg.IrcMessage
|
||||||
|
|
||||||
// Set the hostname for this client. The client may later send a PROXY
|
// Set the hostname for this client. The client may later send a PROXY
|
||||||
// command from stunnel that sets the hostname to something more accurate.
|
// command from stunnel that sets the hostname to something more accurate.
|
||||||
client.hostname = AddrLookupHostname(client.socket.conn.RemoteAddr())
|
client.hostname = AddrLookupHostname(client.socket.conn.RemoteAddr())
|
||||||
|
|
||||||
for err == nil {
|
//TODO(dan): Make this a socketreactor from ircbnc
|
||||||
//TODO(dan): does this read sockets correctly and split lines properly? (think that ZNC bug that kept happening with mammon)
|
for {
|
||||||
if line, err = client.socket.Read(); err != nil {
|
line, err = client.socket.Read()
|
||||||
command = NewQuitCommand("connection closed")
|
if err != nil {
|
||||||
|
client.Quit("connection closed")
|
||||||
} else if command, err = ParseCommand(line); err != nil {
|
break
|
||||||
switch err {
|
|
||||||
case ErrParseCommand:
|
|
||||||
//TODO(dan): why is this a notice? there's a proper numeric for this I swear
|
|
||||||
client.Reply(RplNotice(client.server, client,
|
|
||||||
NewText("failed to parse command")))
|
|
||||||
}
|
|
||||||
// so the read loop will continue
|
|
||||||
err = nil
|
|
||||||
continue
|
|
||||||
|
|
||||||
} else if checkPass, ok := command.(checkPasswordCommand); ok {
|
|
||||||
checkPass.LoadPassword(client.server)
|
|
||||||
// Block the client thread while handling a potentially expensive
|
|
||||||
// password bcrypt operation. Since the server is single-threaded
|
|
||||||
// for commands, we don't want the server to perform the bcrypt,
|
|
||||||
// blocking anyone else from sending commands until it
|
|
||||||
// completes. This could be a form of DoS if handled naively.
|
|
||||||
checkPass.CheckPassword()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client.send(command)
|
msg, err = ParseLine(line)
|
||||||
|
if err != nil {
|
||||||
|
client.Quit("received malformed command")
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isExiting = Run(client.server, client, msg)
|
||||||
|
if isExiting {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure client connection gets closed
|
||||||
|
client.Destroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) send(command Command) {
|
//
|
||||||
command.SetClient(client)
|
|
||||||
client.server.commands <- command
|
|
||||||
}
|
|
||||||
|
|
||||||
// quit timer goroutine
|
// quit timer goroutine
|
||||||
|
//
|
||||||
|
|
||||||
func (client *Client) connectionTimeout() {
|
func (client *Client) connectionTimeout() {
|
||||||
client.send(NewQuitCommand("connection timeout"))
|
client.Quit("connection timeout")
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -158,31 +153,6 @@ func (client *Client) Register() {
|
|||||||
client.Touch()
|
client.Touch()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) destroy() {
|
|
||||||
// clean up channels
|
|
||||||
|
|
||||||
for channel := range client.channels {
|
|
||||||
channel.Quit(client)
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up server
|
|
||||||
|
|
||||||
client.server.clients.Remove(client)
|
|
||||||
|
|
||||||
// clean up self
|
|
||||||
|
|
||||||
if client.idleTimer != nil {
|
|
||||||
client.idleTimer.Stop()
|
|
||||||
}
|
|
||||||
if client.quitTimer != nil {
|
|
||||||
client.quitTimer.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
client.socket.Close()
|
|
||||||
|
|
||||||
Log.debug.Printf("%s: destroyed", client)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (client *Client) IdleTime() time.Duration {
|
func (client *Client) IdleTime() time.Duration {
|
||||||
return time.Since(client.atime)
|
return time.Since(client.atime)
|
||||||
}
|
}
|
||||||
@ -238,6 +208,7 @@ func (c *Client) String() string {
|
|||||||
return c.Id().String()
|
return c.Id().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Friends refers to clients that share a channel with this client.
|
||||||
func (client *Client) Friends() ClientSet {
|
func (client *Client) Friends() ClientSet {
|
||||||
friends := make(ClientSet)
|
friends := make(ClientSet)
|
||||||
friends.Add(client)
|
friends.Add(client)
|
||||||
@ -276,16 +247,36 @@ func (client *Client) Reply(reply string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Quit(message Text) {
|
func (client *Client) Quit(message Text) {
|
||||||
if client.hasQuit {
|
client.Send("QUIT", message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (client *Client) destroy() {
|
||||||
|
if client.isDestroyed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client.hasQuit = true
|
client.isDestroyed = true
|
||||||
client.Reply(RplError("quit"))
|
|
||||||
client.server.whoWas.Append(client)
|
client.server.whoWas.Append(client)
|
||||||
friends := client.Friends()
|
friends := client.Friends()
|
||||||
friends.Remove(client)
|
friends.Remove(client)
|
||||||
client.destroy()
|
|
||||||
|
// clean up channels
|
||||||
|
for channel := range client.channels {
|
||||||
|
channel.Quit(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up server
|
||||||
|
client.server.clients.Remove(client)
|
||||||
|
|
||||||
|
// clean up self
|
||||||
|
if client.idleTimer != nil {
|
||||||
|
client.idleTimer.Stop()
|
||||||
|
}
|
||||||
|
if client.quitTimer != nil {
|
||||||
|
client.quitTimer.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
client.socket.Close()
|
||||||
|
|
||||||
if len(friends) > 0 {
|
if len(friends) > 0 {
|
||||||
reply := RplQuit(client, message)
|
reply := RplQuit(client, message)
|
||||||
|
550
irc/commandhandlers.go
Normal file
550
irc/commandhandlers.go
Normal file
@ -0,0 +1,550 @@
|
|||||||
|
// Copyright (c) 2012-2014 Jeremy Latt
|
||||||
|
// Copyright (c) 2014-2015 Edmund Huber
|
||||||
|
// Copyright (c) 2016- Daniel Oaks <daniel@danieloaks.net>
|
||||||
|
// released under the MIT license
|
||||||
|
|
||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NICK <nickname>
|
||||||
|
func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
// check NICK validity
|
||||||
|
// send NICK change to primary server thread for processing
|
||||||
|
// |-> ensure no other client exists with that nickname
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeChange struct {
|
||||||
|
mode UserMode
|
||||||
|
op ModeOp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (change *ModeChange) String() string {
|
||||||
|
return fmt.Sprintf("%s%s", change.op, change.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModeChanges []*ModeChange
|
||||||
|
|
||||||
|
func (changes ModeChanges) String() string {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
op := changes[0].op
|
||||||
|
str := changes[0].op.String()
|
||||||
|
for _, change := range changes {
|
||||||
|
if change.op != op {
|
||||||
|
op = change.op
|
||||||
|
str += change.op.String()
|
||||||
|
}
|
||||||
|
str += change.mode.String()
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
type ModeCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
nickname Name
|
||||||
|
changes ModeChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODE <nickname> ( "+" / "-" )? *( "+" / "-" / <mode character> )
|
||||||
|
func ParseUserModeCommand(nickname Name, args []string) (Command, error) {
|
||||||
|
cmd := &ModeCommand{
|
||||||
|
nickname: nickname,
|
||||||
|
changes: make(ModeChanges, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
// account for MODE command with no args to list things
|
||||||
|
if len(args) < 1 {
|
||||||
|
// don't do any further processing
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
modeArg := args[0]
|
||||||
|
op := ModeOp(modeArg[0])
|
||||||
|
if (op == Add) || (op == Remove) {
|
||||||
|
modeArg = modeArg[1:]
|
||||||
|
} else {
|
||||||
|
return nil, ErrParseCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, mode := range modeArg {
|
||||||
|
if mode == '-' || mode == '+' {
|
||||||
|
op = ModeOp(mode)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cmd.changes = append(cmd.changes, &ModeChange{
|
||||||
|
mode: UserMode(mode),
|
||||||
|
op: op,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
type ChannelModeChange struct {
|
||||||
|
mode ChannelMode
|
||||||
|
op ModeOp
|
||||||
|
arg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (change *ChannelModeChange) String() (str string) {
|
||||||
|
if (change.op == Add) || (change.op == Remove) {
|
||||||
|
str = change.op.String()
|
||||||
|
}
|
||||||
|
str += change.mode.String()
|
||||||
|
if change.arg != "" {
|
||||||
|
str += " " + change.arg
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelModeChanges []*ChannelModeChange
|
||||||
|
|
||||||
|
func (changes ChannelModeChanges) String() string {
|
||||||
|
if len(changes) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
op := changes[0].op
|
||||||
|
str := changes[0].op.String()
|
||||||
|
|
||||||
|
for _, change := range changes {
|
||||||
|
if change.op != op {
|
||||||
|
op = change.op
|
||||||
|
str += change.op.String()
|
||||||
|
}
|
||||||
|
str += change.mode.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, change := range changes {
|
||||||
|
if change.arg == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
str += " " + change.arg
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChannelModeCommand struct {
|
||||||
|
channel Name
|
||||||
|
changes ChannelModeChanges
|
||||||
|
}
|
||||||
|
|
||||||
|
// MODE <channel> ( "+" / "-" )? *( "+" / "-" / <mode character> ) *<modeparams>
|
||||||
|
func ParseChannelModeCommand(channel Name, args []string) (Command, error) {
|
||||||
|
cmd := &ChannelModeCommand{
|
||||||
|
channel: channel,
|
||||||
|
changes: make(ChannelModeChanges, 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
// account for MODE command with no args to list things
|
||||||
|
if len(args) < 1 {
|
||||||
|
// don't do any further processing
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
modeArg := args[0]
|
||||||
|
op := ModeOp(modeArg[0])
|
||||||
|
if (op == Add) || (op == Remove) {
|
||||||
|
modeArg = modeArg[1:]
|
||||||
|
} else {
|
||||||
|
return nil, ErrParseCommand
|
||||||
|
}
|
||||||
|
|
||||||
|
currentArgIndex := 1
|
||||||
|
|
||||||
|
for _, mode := range modeArg {
|
||||||
|
if mode == '-' || mode == '+' {
|
||||||
|
op = ModeOp(mode)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
change := &ChannelModeChange{
|
||||||
|
mode: ChannelMode(mode),
|
||||||
|
op: op,
|
||||||
|
}
|
||||||
|
switch change.mode {
|
||||||
|
// TODO(dan): separate this into the type A/B/C/D args and use those lists here
|
||||||
|
case Key, BanMask, ExceptMask, InviteMask, UserLimit,
|
||||||
|
ChannelOperator, ChannelFounder, ChannelAdmin, Halfop, Voice:
|
||||||
|
if len(args) > currentArgIndex {
|
||||||
|
change.arg = args[currentArgIndex]
|
||||||
|
currentArgIndex++
|
||||||
|
} else {
|
||||||
|
// silently skip this mode
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.changes = append(cmd.changes, change)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
func ParseModeCommand(args []string) (Command, error) {
|
||||||
|
name := NewName(args[0])
|
||||||
|
if name.IsChannel() {
|
||||||
|
return ParseChannelModeCommand(name, args[1:])
|
||||||
|
} else {
|
||||||
|
return ParseUserModeCommand(name, args[1:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhoisCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
target Name
|
||||||
|
masks []Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// WHOIS [ <target> ] <mask> *( "," <mask> )
|
||||||
|
func ParseWhoisCommand(args []string) (Command, error) {
|
||||||
|
var masks string
|
||||||
|
var target string
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
target = args[0]
|
||||||
|
masks = args[1]
|
||||||
|
} else {
|
||||||
|
masks = args[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return &WhoisCommand{
|
||||||
|
target: NewName(target),
|
||||||
|
masks: NewNames(strings.Split(masks, ",")),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhoCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
mask Name
|
||||||
|
operatorOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// WHO [ <mask> [ "o" ] ]
|
||||||
|
func ParseWhoCommand(args []string) (Command, error) {
|
||||||
|
cmd := &WhoCommand{}
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd.mask = NewName(args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len(args) > 1) && (args[1] == "o") {
|
||||||
|
cmd.operatorOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type OperCommand struct {
|
||||||
|
PassCommand
|
||||||
|
name Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *OperCommand) LoadPassword(server *Server) {
|
||||||
|
msg.hash = server.operators[msg.name]
|
||||||
|
}
|
||||||
|
|
||||||
|
// OPER <name> <password>
|
||||||
|
func ParseOperCommand(args []string) (Command, error) {
|
||||||
|
cmd := &OperCommand{
|
||||||
|
name: NewName(args[0]),
|
||||||
|
}
|
||||||
|
cmd.password = []byte(args[1])
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type CapCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
subCommand CapSubCommand
|
||||||
|
capabilities CapabilitySet
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseCapCommand(args []string) (Command, error) {
|
||||||
|
cmd := &CapCommand{
|
||||||
|
subCommand: CapSubCommand(strings.ToUpper(args[0])),
|
||||||
|
capabilities: make(CapabilitySet),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) > 1 {
|
||||||
|
strs := spacesExpr.Split(args[1], -1)
|
||||||
|
for _, str := range strs {
|
||||||
|
cmd.capabilities[Capability(str)] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HAPROXY support
|
||||||
|
type ProxyCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
net Name
|
||||||
|
sourceIP Name
|
||||||
|
destIP Name
|
||||||
|
sourcePort Name
|
||||||
|
destPort Name
|
||||||
|
hostname Name // looked up in socket thread
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyCommand(hostname Name) *ProxyCommand {
|
||||||
|
cmd := &ProxyCommand{
|
||||||
|
hostname: hostname,
|
||||||
|
}
|
||||||
|
cmd.code = PROXY
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseProxyCommand(args []string) (Command, error) {
|
||||||
|
return &ProxyCommand{
|
||||||
|
net: NewName(args[0]),
|
||||||
|
sourceIP: NewName(args[1]),
|
||||||
|
destIP: NewName(args[2]),
|
||||||
|
sourcePort: NewName(args[3]),
|
||||||
|
destPort: NewName(args[4]),
|
||||||
|
hostname: LookupHostname(NewName(args[1])),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AwayCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
text Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseAwayCommand(args []string) (Command, error) {
|
||||||
|
cmd := &AwayCommand{}
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd.text = NewText(args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type IsOnCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
nicks []Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseIsOnCommand(args []string) (Command, error) {
|
||||||
|
return &IsOnCommand{
|
||||||
|
nicks: NewNames(args),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MOTDCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
target Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseMOTDCommand(args []string) (Command, error) {
|
||||||
|
cmd := &MOTDCommand{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd.target = NewName(args[0])
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoticeCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
target Name
|
||||||
|
message Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseNoticeCommand(args []string) (Command, error) {
|
||||||
|
return &NoticeCommand{
|
||||||
|
target: NewName(args[0]),
|
||||||
|
message: NewText(args[1]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type KickCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
kicks map[Name]Name
|
||||||
|
comment Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (msg *KickCommand) Comment() Text {
|
||||||
|
if msg.comment == "" {
|
||||||
|
return msg.Client().Nick().Text()
|
||||||
|
}
|
||||||
|
return msg.comment
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseKickCommand(args []string) (Command, error) {
|
||||||
|
channels := NewNames(strings.Split(args[0], ","))
|
||||||
|
users := NewNames(strings.Split(args[1], ","))
|
||||||
|
if (len(channels) != len(users)) && (len(users) != 1) {
|
||||||
|
return nil, NotEnoughArgsError
|
||||||
|
}
|
||||||
|
cmd := &KickCommand{
|
||||||
|
kicks: make(map[Name]Name),
|
||||||
|
}
|
||||||
|
for index, channel := range channels {
|
||||||
|
if len(users) == 1 {
|
||||||
|
cmd.kicks[channel] = users[0]
|
||||||
|
} else {
|
||||||
|
cmd.kicks[channel] = users[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(args) > 2 {
|
||||||
|
cmd.comment = NewText(args[2])
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
channels []Name
|
||||||
|
target Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseListCommand(args []string) (Command, error) {
|
||||||
|
cmd := &ListCommand{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd.channels = NewNames(strings.Split(args[0], ","))
|
||||||
|
}
|
||||||
|
if len(args) > 1 {
|
||||||
|
cmd.target = NewName(args[1])
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NamesCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
channels []Name
|
||||||
|
target Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseNamesCommand(args []string) (Command, error) {
|
||||||
|
cmd := &NamesCommand{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd.channels = NewNames(strings.Split(args[0], ","))
|
||||||
|
}
|
||||||
|
if len(args) > 1 {
|
||||||
|
cmd.target = NewName(args[1])
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DebugCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
subCommand Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDebugCommand(args []string) (Command, error) {
|
||||||
|
return &DebugCommand{
|
||||||
|
subCommand: NewName(strings.ToUpper(args[0])),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type VersionCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
target Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseVersionCommand(args []string) (Command, error) {
|
||||||
|
cmd := &VersionCommand{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd.target = NewName(args[0])
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type InviteCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
nickname Name
|
||||||
|
channel Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseInviteCommand(args []string) (Command, error) {
|
||||||
|
return &InviteCommand{
|
||||||
|
nickname: NewName(args[0]),
|
||||||
|
channel: NewName(args[1]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTheaterCommand(args []string) (Command, error) {
|
||||||
|
if upperSubCmd := strings.ToUpper(args[0]); upperSubCmd == "IDENTIFY" && len(args) == 3 {
|
||||||
|
return &TheaterIdentifyCommand{
|
||||||
|
channel: NewName(args[1]),
|
||||||
|
PassCommand: PassCommand{password: []byte(args[2])},
|
||||||
|
}, nil
|
||||||
|
} else if upperSubCmd == "PRIVMSG" && len(args) == 4 {
|
||||||
|
return &TheaterPrivMsgCommand{
|
||||||
|
channel: NewName(args[1]),
|
||||||
|
asNick: NewName(args[2]),
|
||||||
|
message: NewText(args[3]),
|
||||||
|
}, nil
|
||||||
|
} else if upperSubCmd == "ACTION" && len(args) == 4 {
|
||||||
|
return &TheaterActionCommand{
|
||||||
|
channel: NewName(args[1]),
|
||||||
|
asNick: NewName(args[2]),
|
||||||
|
action: NewCTCPText(args[3]),
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
return nil, ErrParseCommand
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimeCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
target Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseTimeCommand(args []string) (Command, error) {
|
||||||
|
cmd := &TimeCommand{}
|
||||||
|
if len(args) > 0 {
|
||||||
|
cmd.target = NewName(args[0])
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type KillCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
nickname Name
|
||||||
|
comment Text
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseKillCommand(args []string) (Command, error) {
|
||||||
|
return &KillCommand{
|
||||||
|
nickname: NewName(args[0]),
|
||||||
|
comment: NewText(args[1]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhoWasCommand struct {
|
||||||
|
BaseCommand
|
||||||
|
nicknames []Name
|
||||||
|
count int64
|
||||||
|
target Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseWhoWasCommand(args []string) (Command, error) {
|
||||||
|
cmd := &WhoWasCommand{
|
||||||
|
nicknames: NewNames(strings.Split(args[0], ",")),
|
||||||
|
}
|
||||||
|
if len(args) > 1 {
|
||||||
|
cmd.count, _ = strconv.ParseInt(args[1], 10, 64)
|
||||||
|
}
|
||||||
|
if len(args) > 2 {
|
||||||
|
cmd.target = NewName(args[2])
|
||||||
|
}
|
||||||
|
return cmd, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseOperNickCommand(args []string) (Command, error) {
|
||||||
|
return &OperNickCommand{
|
||||||
|
target: NewName(args[0]),
|
||||||
|
nick: NewName(args[1]),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
*/
|
1073
irc/commands.go
1073
irc/commands.go
File diff suppressed because it is too large
Load Diff
@ -9,15 +9,16 @@ import (
|
|||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (msg *DebugCommand) HandleServer(server *Server) {
|
func debugHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
client := msg.Client()
|
|
||||||
if !client.flags[Operator] {
|
if !client.flags[Operator] {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch msg.subCommand {
|
switch msg.Params[0] {
|
||||||
case "GCSTATS":
|
case "GCSTATS":
|
||||||
stats := debug.GCStats{
|
stats := debug.GCStats{
|
||||||
Pause: make([]time.Duration, 10),
|
Pause: make([]time.Duration, 10),
|
||||||
|
@ -122,6 +122,7 @@ var (
|
|||||||
// commands
|
// commands
|
||||||
//
|
//
|
||||||
|
|
||||||
|
/*
|
||||||
func (m *ModeCommand) HandleServer(s *Server) {
|
func (m *ModeCommand) HandleServer(s *Server) {
|
||||||
client := m.Client()
|
client := m.Client()
|
||||||
target := s.clients.Get(m.nickname)
|
target := s.clients.Get(m.nickname)
|
||||||
@ -185,3 +186,4 @@ func (msg *ChannelModeCommand) HandleServer(server *Server) {
|
|||||||
|
|
||||||
channel.Mode(client, msg.changes)
|
channel.Mode(client, msg.changes)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package irc
|
package irc
|
||||||
|
|
||||||
|
/*
|
||||||
type NickCommand struct {
|
type NickCommand struct {
|
||||||
BaseCommand
|
BaseCommand
|
||||||
nickname Name
|
nickname Name
|
||||||
@ -98,3 +99,4 @@ func (msg *OperNickCommand) HandleServer(server *Server) {
|
|||||||
|
|
||||||
target.ChangeNickname(msg.nick)
|
target.ChangeNickname(msg.nick)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
496
irc/server.go
496
irc/server.go
@ -15,21 +15,14 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/DanielOaks/girc-go/ircmsg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerCommand interface {
|
|
||||||
Command
|
|
||||||
HandleServer(*Server)
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegServerCommand interface {
|
|
||||||
Command
|
|
||||||
HandleRegServer(*Server)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
channels ChannelNameMap
|
channels ChannelNameMap
|
||||||
clients *ClientLookupSet
|
clients *ClientLookupSet
|
||||||
@ -217,6 +210,7 @@ func (server *Server) processCommand(cmd Command) {
|
|||||||
func (server *Server) Shutdown() {
|
func (server *Server) Shutdown() {
|
||||||
server.db.Close()
|
server.db.Close()
|
||||||
for _, client := range server.clients.byNick {
|
for _, client := range server.clients.byNick {
|
||||||
|
client.Send("notice")
|
||||||
client.Reply(RplNotice(server, client, "shutting down"))
|
client.Reply(RplNotice(server, client, "shutting down"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -331,6 +325,7 @@ func (s *Server) tryRegister(c *Client) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.Send("Intro to the network")
|
||||||
c.Register()
|
c.Register()
|
||||||
c.RplWelcome()
|
c.RplWelcome()
|
||||||
c.RplYourHost()
|
c.RplYourHost()
|
||||||
@ -342,14 +337,17 @@ func (s *Server) tryRegister(c *Client) {
|
|||||||
|
|
||||||
func (server *Server) MOTD(client *Client) {
|
func (server *Server) MOTD(client *Client) {
|
||||||
if len(server.motdLines) < 1 {
|
if len(server.motdLines) < 1 {
|
||||||
|
c.Send("send")
|
||||||
client.ErrNoMOTD()
|
client.ErrNoMOTD()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client.RplMOTDStart()
|
client.RplMOTDStart()
|
||||||
for _, line := range server.motdLines {
|
for _, line := range server.motdLines {
|
||||||
|
c.Send("send")
|
||||||
client.RplMOTD(line)
|
client.RplMOTD(line)
|
||||||
}
|
}
|
||||||
|
c.Send("send")
|
||||||
client.RplMOTDEnd()
|
client.RplMOTDEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,103 +363,132 @@ func (s *Server) Nick() Name {
|
|||||||
return s.Id()
|
return s.Id()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) Reply(target *Client, message string) {
|
|
||||||
target.Reply(RplPrivMsg(server, target, NewText(message)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (server *Server) Replyf(target *Client, format string, args ...interface{}) {
|
|
||||||
server.Reply(target, fmt.Sprintf(format, args...))
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// registration commands
|
// registration commands
|
||||||
//
|
//
|
||||||
|
|
||||||
func (msg *PassCommand) HandleRegServer(server *Server) {
|
// PASS <password>
|
||||||
client := msg.Client()
|
func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
if msg.err != nil {
|
if client.Registered {
|
||||||
client.ErrPasswdMismatch()
|
client.Send("send")
|
||||||
|
client.ErrAlreadyRegistered()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the provided password
|
||||||
|
logger.Fatal("Implement PASS command")
|
||||||
|
password := []byte(args[0])
|
||||||
|
if ComparePassword(server.password, password) != nil {
|
||||||
|
logger.Fatal("SEND BACK REJECTION")
|
||||||
client.Quit("bad password")
|
client.Quit("bad password")
|
||||||
return
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
client.authorized = true
|
client.authorized = true
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *ProxyCommand) HandleRegServer(server *Server) {
|
// PROXY TCP4/6 SOURCEIP DESTIP SOURCEPORT DESTPORT
|
||||||
client := msg.Client()
|
// http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt
|
||||||
|
func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
clientAddress := IPString(client.socket.conn.RemoteAddr()).String()
|
clientAddress := IPString(client.socket.conn.RemoteAddr()).String()
|
||||||
clientHostname := client.hostname.String()
|
clientHostname := client.hostname.String()
|
||||||
|
|
||||||
for _, address := range server.proxyAllowedFrom {
|
for _, address := range server.proxyAllowedFrom {
|
||||||
if clientHostname == address || clientAddress == address {
|
if clientHostname == address || clientAddress == address {
|
||||||
client.hostname = msg.hostname
|
client.hostname = LookupHostname(NewName(msg.Params[1]))
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Quit("PROXY command is not usable from your address")
|
client.Quit("PROXY command is not usable from your address")
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *UserCommand) HandleRegServer(server *Server) {
|
// USER <username> * 0 <realname>
|
||||||
client := msg.Client()
|
func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
if client.Registered {
|
||||||
|
client.Send("send")
|
||||||
|
client.ErrAlreadyRegistered()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if !client.authorized {
|
if !client.authorized {
|
||||||
client.ErrPasswdMismatch()
|
|
||||||
client.Quit("bad password")
|
client.Quit("bad password")
|
||||||
return
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if client.username != "" && client.realname != "" {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// set user info and log client in
|
// set user info and log client in
|
||||||
server.clients.Remove(client)
|
|
||||||
//TODO(dan): Could there be a race condition here with adding/removing the client?
|
//TODO(dan): Could there be a race condition here with adding/removing the client?
|
||||||
client.username, client.realname = msg.username, msg.realname
|
//TODO(dan): we should do something like server.clients.Replace(client) instead
|
||||||
|
|
||||||
|
// we do it this way to ONLY replace what hasn't already been set
|
||||||
|
server.clients.Remove(client)
|
||||||
|
|
||||||
|
if client.username != "" {
|
||||||
|
client.username = msg.username
|
||||||
|
}
|
||||||
|
if client.realname != "" {
|
||||||
|
client.realname = msg.realname
|
||||||
|
}
|
||||||
|
|
||||||
server.clients.Add(client)
|
server.clients.Add(client)
|
||||||
|
|
||||||
server.tryRegister(client)
|
server.tryRegister(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *QuitCommand) HandleRegServer(server *Server) {
|
// QUIT [<reason>]
|
||||||
msg.Client().Quit(msg.message)
|
func quitHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
reason := "Quit"
|
||||||
|
if len(msg.Params) > 0 {
|
||||||
|
reason += ": " + msg.Params[0]
|
||||||
|
}
|
||||||
|
client.Quit(msg.message)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// normal commands
|
// normal commands
|
||||||
//
|
//
|
||||||
|
|
||||||
func (m *PassCommand) HandleServer(s *Server) {
|
// PING <server1> [<server2>]
|
||||||
m.Client().ErrAlreadyRegistered()
|
func pingHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
// client.Socket.Send(response here)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PingCommand) HandleServer(s *Server) {
|
// PONG <server> [ <server2> ]
|
||||||
client := m.Client()
|
func pongHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
client.Reply(RplPong(client, m.server.Text()))
|
//TODO(dan): update client idle timer from this
|
||||||
|
//TODO(dan): use this to affect how often we send pings
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PongCommand) HandleServer(s *Server) {
|
// JOIN <channel>{,<channel>} [<key>{,<key>}]
|
||||||
// no-op
|
// JOIN 0
|
||||||
}
|
func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
// handle JOIN 0
|
||||||
func (m *UserCommand) HandleServer(s *Server) {
|
if msg.Params[0] == "0" {
|
||||||
m.Client().ErrAlreadyRegistered()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (msg *QuitCommand) HandleServer(server *Server) {
|
|
||||||
msg.Client().Quit(msg.message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *JoinCommand) HandleServer(s *Server) {
|
|
||||||
client := m.Client()
|
|
||||||
|
|
||||||
if m.zero {
|
|
||||||
for channel := range client.channels {
|
for channel := range client.channels {
|
||||||
channel.Part(client, client.Nick().Text())
|
channel.Part(client, client.Nick().Text())
|
||||||
}
|
}
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, key := range m.channels {
|
// handle regular JOINs
|
||||||
|
channels := strings.Split(msg.Params[0], ",")
|
||||||
|
var keys []string
|
||||||
|
if len(msg.Params) > 1 {
|
||||||
|
keys = strings.Split(msg.Params[1], ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, name := range channels {
|
||||||
if !name.IsChannel() {
|
if !name.IsChannel() {
|
||||||
client.ErrNoSuchChannel(name)
|
log.Fatal("Implement ErrNoSuchChannel")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,17 +496,29 @@ func (m *JoinCommand) HandleServer(s *Server) {
|
|||||||
if channel == nil {
|
if channel == nil {
|
||||||
channel = NewChannel(s, name, true)
|
channel = NewChannel(s, name, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var key string
|
||||||
|
if len(keys) > i {
|
||||||
|
key = keys[i]
|
||||||
|
}
|
||||||
|
|
||||||
channel.Join(client, key)
|
channel.Join(client, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *PartCommand) HandleServer(server *Server) {
|
// PART <channel>{,<channel>} [<reason>]
|
||||||
client := m.Client()
|
func partHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
for _, chname := range m.channels {
|
channels := strings.Split(msg.Params[0], ",")
|
||||||
|
var reason string //TODO(dan): should this be the user's nickname instead of empty?
|
||||||
|
if len(msg.Params) > 1 {
|
||||||
|
reason = msg.Params[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, chname := range channels {
|
||||||
channel := server.channels.Get(chname)
|
channel := server.channels.Get(chname)
|
||||||
|
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
m.Client().ErrNoSuchChannel(chname)
|
log.Fatal("Implement ErrNoSuchChannel")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,42 +526,47 @@ func (m *PartCommand) HandleServer(server *Server) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *TopicCommand) HandleServer(server *Server) {
|
// TOPIC <channel> [<topic>]
|
||||||
client := msg.Client()
|
func topicHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
channel := server.channels.Get(msg.channel)
|
channel := server.channels.Get(msg.Params[0])
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
client.ErrNoSuchChannel(msg.channel)
|
log.Fatal("Implement ErrNoSuchChannel")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.setTopic {
|
if len(msg.Params) > 1 {
|
||||||
channel.SetTopic(client, msg.topic)
|
channel.SetTopic(client, msg.Params[1])
|
||||||
} else {
|
} else {
|
||||||
channel.GetTopic(client)
|
channel.GetTopic(client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *PrivMsgCommand) HandleServer(server *Server) {
|
// PRIVMSG <target>{,<target>} <message>
|
||||||
client := msg.Client()
|
func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
if msg.target.IsChannel() {
|
targets := strings.Split(msg.Params[0], ",")
|
||||||
channel := server.channels.Get(msg.target)
|
message := msg.Params[1]
|
||||||
|
|
||||||
|
for _, target := range targets {
|
||||||
|
if target.IsChannel() {
|
||||||
|
channel := server.channels.Get(target)
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
client.ErrNoSuchChannel(msg.target)
|
client.Send("send")
|
||||||
|
client.ErrNoSuchChannel(target)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
channel.PrivMsg(client, message)
|
||||||
|
} else {
|
||||||
|
user := server.clients.Get(target)
|
||||||
|
if user == nil {
|
||||||
|
client.Send("send")
|
||||||
|
client.ErrNoSuchNick(target)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
user.Send("content here")
|
||||||
channel.PrivMsg(client, msg.message)
|
if user.flags[Away] {
|
||||||
return
|
client.Send("target is AWAY")
|
||||||
}
|
}
|
||||||
|
|
||||||
target := server.clients.Get(msg.target)
|
|
||||||
if target == nil {
|
|
||||||
client.ErrNoSuchNick(msg.target)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
target.Reply(RplPrivMsg(client, target, msg.message))
|
|
||||||
if target.flags[Away] {
|
|
||||||
client.RplAway(target)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,19 +585,29 @@ func (client *Client) WhoisChannelsNames(target *Client) []string {
|
|||||||
return chstrs
|
return chstrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *WhoisCommand) HandleServer(server *Server) {
|
// WHOIS [ <target> ] <mask> *( "," <mask> )
|
||||||
client := m.Client()
|
func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
var masks string
|
||||||
|
var target string
|
||||||
|
|
||||||
|
if len(msg.Params) > 1 {
|
||||||
|
target = msg.Params[0]
|
||||||
|
masks = msg.Params[1]
|
||||||
|
} else {
|
||||||
|
masks = msg.Params[0]
|
||||||
|
}
|
||||||
|
|
||||||
// TODO implement target query
|
// TODO implement target query
|
||||||
|
for _, mask := range masks {
|
||||||
for _, mask := range m.masks {
|
|
||||||
matches := server.clients.FindAll(mask)
|
matches := server.clients.FindAll(mask)
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
client.ErrNoSuchNick(mask)
|
client.ErrNoSuchNick(mask)
|
||||||
|
client.Send("NOSUCHNICK")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for mclient := range matches {
|
for mclient := range matches {
|
||||||
client.RplWhois(mclient)
|
client.RplWhois(mclient)
|
||||||
|
client.Send("WHOIS")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -561,15 +615,27 @@ func (m *WhoisCommand) HandleServer(server *Server) {
|
|||||||
func whoChannel(client *Client, channel *Channel, friends ClientSet) {
|
func whoChannel(client *Client, channel *Channel, friends ClientSet) {
|
||||||
for member := range channel.members {
|
for member := range channel.members {
|
||||||
if !client.flags[Invisible] || friends[client] {
|
if !client.flags[Invisible] || friends[client] {
|
||||||
|
client.Send("send")
|
||||||
client.RplWhoReply(channel, member)
|
client.RplWhoReply(channel, member)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *WhoCommand) HandleServer(server *Server) {
|
// WHO [ <mask> [ "o" ] ]
|
||||||
client := msg.Client()
|
func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
friends := client.Friends()
|
friends := client.Friends()
|
||||||
mask := msg.mask
|
|
||||||
|
var mask string
|
||||||
|
if len(msg.Params) > 0 {
|
||||||
|
mask = NewName(msg.Params[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
//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 && msr.Params[1] == "o" {
|
||||||
|
operatorOnly = true
|
||||||
|
}
|
||||||
|
|
||||||
if mask == "" {
|
if mask == "" {
|
||||||
for _, channel := range server.channels {
|
for _, channel := range server.channels {
|
||||||
@ -584,101 +650,161 @@ func (msg *WhoCommand) HandleServer(server *Server) {
|
|||||||
} else {
|
} else {
|
||||||
for mclient := range server.clients.FindAll(mask) {
|
for mclient := range server.clients.FindAll(mask) {
|
||||||
client.RplWhoReply(nil, mclient)
|
client.RplWhoReply(nil, mclient)
|
||||||
|
client.Send("REPLY")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.RplEndOfWho(mask)
|
client.RplEndOfWho(mask)
|
||||||
|
client.Send("ENDOFWHO")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *OperCommand) HandleServer(server *Server) {
|
// OPER <name> <password>
|
||||||
client := msg.Client()
|
func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
name = NewName(msg.Params[0])
|
||||||
|
hash = server.operators[name]
|
||||||
|
password = []byte(msg.Params[1])
|
||||||
|
|
||||||
if (msg.hash == nil) || (msg.err != nil) {
|
err = ComparePassword(hash, password)
|
||||||
|
|
||||||
|
if (hash == nil) || (err != nil) {
|
||||||
client.ErrPasswdMismatch()
|
client.ErrPasswdMismatch()
|
||||||
return
|
client.Send("PASSWDBAD")
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO(dan): Split this into client.makeOper() ??
|
||||||
client.flags[Operator] = true
|
client.flags[Operator] = true
|
||||||
client.RplYoureOper()
|
client.RplYoureOper()
|
||||||
|
client.Send("YOUROPER")
|
||||||
client.Reply(RplModeChanges(client, client, ModeChanges{&ModeChange{
|
client.Reply(RplModeChanges(client, client, ModeChanges{&ModeChange{
|
||||||
mode: Operator,
|
mode: Operator,
|
||||||
op: Add,
|
op: Add,
|
||||||
}}))
|
}}))
|
||||||
|
client.Send("OPERMODECHANGE")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *AwayCommand) HandleServer(server *Server) {
|
// AWAY [<message>]
|
||||||
client := msg.Client()
|
func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
if len(msg.text) > 0 {
|
var isAway bool
|
||||||
|
var text string
|
||||||
|
if len(msg.Params) > 0 {
|
||||||
|
isAway = True
|
||||||
|
text = NewText(msg.Params[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
if isAway {
|
||||||
client.flags[Away] = true
|
client.flags[Away] = true
|
||||||
} else {
|
} else {
|
||||||
delete(client.flags, Away)
|
delete(client.flags, Away)
|
||||||
}
|
}
|
||||||
client.awayMessage = msg.text
|
client.awayMessage = text
|
||||||
|
|
||||||
var op ModeOp
|
var op ModeOp
|
||||||
if client.flags[Away] {
|
if client.flags[Away] {
|
||||||
op = Add
|
op = Add
|
||||||
|
client.Send("imaway")
|
||||||
client.RplNowAway()
|
client.RplNowAway()
|
||||||
} else {
|
} else {
|
||||||
op = Remove
|
op = Remove
|
||||||
|
client.Send("unaway")
|
||||||
client.RplUnAway()
|
client.RplUnAway()
|
||||||
}
|
}
|
||||||
|
client.Send("mode changes I guess?")
|
||||||
client.Reply(RplModeChanges(client, client, ModeChanges{&ModeChange{
|
client.Reply(RplModeChanges(client, client, ModeChanges{&ModeChange{
|
||||||
mode: Away,
|
mode: Away,
|
||||||
op: op,
|
op: op,
|
||||||
}}))
|
}}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *IsOnCommand) HandleServer(server *Server) {
|
// ISON <nick>{ <nick>}
|
||||||
client := msg.Client()
|
func isonHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
var nicks = NewNames(msg.Params)
|
||||||
|
|
||||||
ison := make([]string, 0)
|
ison := make([]string, 0)
|
||||||
for _, nick := range msg.nicks {
|
for _, nick := range nicks {
|
||||||
if iclient := server.clients.Get(nick); iclient != nil {
|
if iclient := server.clients.Get(nick); iclient != nil {
|
||||||
ison = append(ison, iclient.Nick().String())
|
ison = append(ison, iclient.Nick().String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client.Send("ISON")
|
||||||
client.RplIsOn(ison)
|
client.RplIsOn(ison)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *MOTDCommand) HandleServer(server *Server) {
|
// MOTD [<target>]
|
||||||
|
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 = NewName(msg.Params[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
client.Send("MOTD")
|
||||||
server.MOTD(msg.Client())
|
server.MOTD(msg.Client())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *NoticeCommand) HandleServer(server *Server) {
|
// NOTICE <target>{,<target>} <message>
|
||||||
client := msg.Client()
|
func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
if msg.target.IsChannel() {
|
targetName := NewName(msg.Params[0])
|
||||||
channel := server.channels.Get(msg.target)
|
message := NewText(msg.Params[1])
|
||||||
|
|
||||||
|
if targetName.IsChannel() {
|
||||||
|
channel := server.channels.Get(targetName)
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
client.ErrNoSuchChannel(msg.target)
|
client.Send("ERRNOSUCHCHAN")
|
||||||
|
client.ErrNoSuchChannel(targetName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Notice(client, msg.message)
|
channel.Notice(client, message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target := server.clients.Get(msg.target)
|
target := server.clients.Get(targetName)
|
||||||
if target == nil {
|
if target == nil {
|
||||||
client.ErrNoSuchNick(msg.target)
|
client.Send("ERRNOSUCHNICK")
|
||||||
|
client.ErrNoSuchNick(targetName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
target.Reply(RplNotice(client, target, msg.message))
|
client.Send("NOTICE")
|
||||||
|
target.Reply(RplNotice(client, target, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *KickCommand) HandleServer(server *Server) {
|
// KICK <channel>{,<channel>} <user>{,<user>} [<comment>]
|
||||||
client := msg.Client()
|
func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
for chname, nickname := range msg.kicks {
|
channels := NewNames(strings.Split(msg.Params[0], ","))
|
||||||
|
users := NewNames(strings.Split(msg.Params[1], ","))
|
||||||
|
if (len(channels) != len(users)) && (len(users) != 1) {
|
||||||
|
client.Send("NotEnoughArgs??")
|
||||||
|
return false
|
||||||
|
// not needed return nil, NotEnoughArgsError
|
||||||
|
}
|
||||||
|
|
||||||
|
kicks := make(map[Name]Name)
|
||||||
|
for index, channel := range channels {
|
||||||
|
if len(users) == 1 {
|
||||||
|
kicks[channel] = users[0]
|
||||||
|
} else {
|
||||||
|
kicks[channel] = users[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var comment string
|
||||||
|
if len(msg.Params) > 2 {
|
||||||
|
comment = msg.Params[2]
|
||||||
|
}
|
||||||
|
for chname, nickname := range kicks {
|
||||||
channel := server.channels.Get(chname)
|
channel := server.channels.Get(chname)
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
client.ErrNoSuchChannel(chname)
|
client.ErrNoSuchChannel(chname)
|
||||||
|
client.Send("send")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
target := server.clients.Get(nickname)
|
target := server.clients.Get(nickname)
|
||||||
if target == nil {
|
if target == nil {
|
||||||
client.ErrNoSuchNick(nickname)
|
client.ErrNoSuchNick(nickname)
|
||||||
|
client.Send("send")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,128 +826,196 @@ func (msg *KickCommand) HandleServer(server *Server) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if hasPrivs {
|
if hasPrivs {
|
||||||
channel.Kick(client, target, msg.Comment())
|
if comment == "" {
|
||||||
|
channel.Kick(client, target, nickname)
|
||||||
|
} else {
|
||||||
|
channel.Kick(client, target, comment)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
client.ErrChanOPrivIsNeeded(channel)
|
client.ErrChanOPrivIsNeeded(channel)
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *ListCommand) HandleServer(server *Server) {
|
// LIST [<channel>{,<channel>} [<server>]]
|
||||||
client := msg.Client()
|
func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
var channels []Name
|
||||||
|
if len(args) > 0 {
|
||||||
|
channels = NewNames(strings.Split(args[0], ","))
|
||||||
|
}
|
||||||
|
var target Name
|
||||||
|
if len(args) > 1 {
|
||||||
|
target = NewName(args[1])
|
||||||
|
}
|
||||||
|
|
||||||
// TODO target server
|
//TODO(dan): target server when we have multiple servers
|
||||||
if msg.target != "" {
|
//TODO(dan): we should continue just fine if it's this current server though
|
||||||
|
if target != "" {
|
||||||
client.ErrNoSuchServer(msg.target)
|
client.ErrNoSuchServer(msg.target)
|
||||||
|
client.Send("send")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(msg.channels) == 0 {
|
if len(channels) == 0 {
|
||||||
for _, channel := range server.channels {
|
for _, channel := range server.channels {
|
||||||
if !client.flags[Operator] && channel.flags[Secret] {
|
if !client.flags[Operator] && channel.flags[Secret] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
client.RplList(channel)
|
client.RplList(channel)
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, chname := range msg.channels {
|
for _, chname := range channels {
|
||||||
channel := server.channels.Get(chname)
|
channel := server.channels.Get(chname)
|
||||||
if channel == nil || (!client.flags[Operator] && channel.flags[Secret]) {
|
if channel == nil || (!client.flags[Operator] && channel.flags[Secret]) {
|
||||||
client.ErrNoSuchChannel(chname)
|
client.ErrNoSuchChannel(chname)
|
||||||
|
client.Send("send")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
client.RplList(channel)
|
client.RplList(channel)
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.RplListEnd(server)
|
client.RplListEnd(server)
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *NamesCommand) HandleServer(server *Server) {
|
// NAMES [<channel>{,<channel>}]
|
||||||
client := msg.Client()
|
func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
if len(server.channels) == 0 {
|
var channels []Name
|
||||||
|
if len(args) > 0 {
|
||||||
|
channels = NewNames(strings.Split(args[0], ","))
|
||||||
|
}
|
||||||
|
var target Name
|
||||||
|
if len(args) > 1 {
|
||||||
|
target = NewName(args[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(channels) == 0 {
|
||||||
for _, channel := range server.channels {
|
for _, channel := range server.channels {
|
||||||
channel.Names(client)
|
channel.Names(client)
|
||||||
}
|
}
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, chname := range msg.channels {
|
for _, chname := range channels {
|
||||||
channel := server.channels.Get(chname)
|
channel := server.channels.Get(chname)
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
client.ErrNoSuchChannel(chname)
|
client.ErrNoSuchChannel(chname)
|
||||||
|
client.Send("send")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
channel.Names(client)
|
channel.Names(client)
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *VersionCommand) HandleServer(server *Server) {
|
// VERSION [<server>]
|
||||||
client := msg.Client()
|
func versionHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
if (msg.target != "") && (msg.target != server.name) {
|
var target Name
|
||||||
client.ErrNoSuchServer(msg.target)
|
if len(args) > 0 {
|
||||||
|
target = NewName(args[0])
|
||||||
|
}
|
||||||
|
if (target != "") && (target != server.name) {
|
||||||
|
client.ErrNoSuchServer(target)
|
||||||
|
client.Send("send")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client.RplVersion()
|
client.RplVersion()
|
||||||
|
client.Send("send")
|
||||||
client.RplISupport()
|
client.RplISupport()
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *InviteCommand) HandleServer(server *Server) {
|
// INVITE <nickname> <channel>
|
||||||
client := msg.Client()
|
func inviteHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
nickname := NewName(msg.Params[0])
|
||||||
|
channelName := NewName(msg.Params[1])
|
||||||
|
|
||||||
target := server.clients.Get(msg.nickname)
|
target := server.clients.Get(nickname)
|
||||||
if target == nil {
|
if target == nil {
|
||||||
client.ErrNoSuchNick(msg.nickname)
|
client.ErrNoSuchNick(nickname)
|
||||||
|
client.Send("send")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel := server.channels.Get(msg.channel)
|
channel := server.channels.Get(channelName)
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
client.RplInviting(target, msg.channel)
|
client.RplInviting(target, channelName)
|
||||||
target.Reply(RplInviteMsg(client, target, msg.channel))
|
client.Send("send")
|
||||||
|
target.Reply(RplInviteMsg(client, target, channelName))
|
||||||
|
client.Send("send")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.Invite(target, client)
|
channel.Invite(target, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *TimeCommand) HandleServer(server *Server) {
|
// TIME [<server>]
|
||||||
client := msg.Client()
|
func timeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
if (msg.target != "") && (msg.target != server.name) {
|
var target Name
|
||||||
client.ErrNoSuchServer(msg.target)
|
if len(msg.Params) > 0 {
|
||||||
|
target = NewName(msg.Params[0])
|
||||||
|
}
|
||||||
|
if (target != "") && (target != server.name) {
|
||||||
|
client.ErrNoSuchServer(target)
|
||||||
|
client.Send("send")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
client.RplTime()
|
client.RplTime()
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *KillCommand) HandleServer(server *Server) {
|
// KILL <nickname> <comment>
|
||||||
client := msg.Client()
|
func killHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
|
nickname := NewName(msg.Params[0])
|
||||||
|
comment := NewText(msg.Params[1])
|
||||||
|
|
||||||
if !client.flags[Operator] {
|
if !client.flags[Operator] {
|
||||||
client.ErrNoPrivileges()
|
client.ErrNoPrivileges()
|
||||||
|
client.Send("send")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
target := server.clients.Get(msg.nickname)
|
target := server.clients.Get(nickname)
|
||||||
if target == nil {
|
if target == nil {
|
||||||
client.ErrNoSuchNick(msg.nickname)
|
client.ErrNoSuchNick(nickname)
|
||||||
|
client.Send("send")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
quitMsg := fmt.Sprintf("KILLed by %s: %s", client.Nick(), msg.comment)
|
//TODO(dan): make below format match that from other IRCds
|
||||||
|
quitMsg := fmt.Sprintf("KILLed by %s: %s", client.Nick(), comment)
|
||||||
target.Quit(NewText(quitMsg))
|
target.Quit(NewText(quitMsg))
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *WhoWasCommand) HandleServer(server *Server) {
|
// WHOWAS <nickname> [<count> [<server>]]
|
||||||
client := msg.Client()
|
func whowasHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
||||||
for _, nickname := range msg.nicknames {
|
nicknames := NewNames(strings.Split(msg.Params[0], ","))
|
||||||
|
|
||||||
|
var count int
|
||||||
|
if len(msg.Params) > 1 {
|
||||||
|
count, _ = strconv.ParseInt(msg.Params[1], 10, 64)
|
||||||
|
}
|
||||||
|
var target Name
|
||||||
|
if len(msg.Params) > 2 {
|
||||||
|
target = NewName(msg.Params[2])
|
||||||
|
}
|
||||||
|
for _, nickname := range nicknames {
|
||||||
results := server.whoWas.Find(nickname, msg.count)
|
results := server.whoWas.Find(nickname, msg.count)
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
client.ErrWasNoSuchNick(nickname)
|
client.ErrWasNoSuchNick(nickname)
|
||||||
|
client.Send("send")
|
||||||
} else {
|
} else {
|
||||||
for _, whoWas := range results {
|
for _, whoWas := range results {
|
||||||
client.RplWhoWasUser(whoWas)
|
client.RplWhoWasUser(whoWas)
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.RplEndOfWhoWas(nickname)
|
client.RplEndOfWhoWas(nickname)
|
||||||
|
client.Send("send")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user