mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 18:52:41 +01:00
more channel mode parsing and bad listing
This commit is contained in:
parent
d370abcd4c
commit
93f4b6859a
@ -5,6 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Channel struct {
|
type Channel struct {
|
||||||
|
banList []UserMask
|
||||||
commands chan<- ChannelCommand
|
commands chan<- ChannelCommand
|
||||||
key string
|
key string
|
||||||
members ClientSet
|
members ClientSet
|
||||||
@ -47,11 +48,12 @@ func NewChannel(s *Server, name string) *Channel {
|
|||||||
commands := make(chan ChannelCommand)
|
commands := make(chan ChannelCommand)
|
||||||
replies := make(chan Reply)
|
replies := make(chan Reply)
|
||||||
channel := &Channel{
|
channel := &Channel{
|
||||||
name: name,
|
banList: make([]UserMask, 0),
|
||||||
members: make(ClientSet),
|
|
||||||
server: s,
|
|
||||||
commands: commands,
|
commands: commands,
|
||||||
|
members: make(ClientSet),
|
||||||
|
name: name,
|
||||||
replies: replies,
|
replies: replies,
|
||||||
|
server: s,
|
||||||
}
|
}
|
||||||
go channel.receiveCommands(commands)
|
go channel.receiveCommands(commands)
|
||||||
go channel.receiveReplies(replies)
|
go channel.receiveReplies(replies)
|
||||||
@ -199,3 +201,20 @@ func (m *TopicCommand) HandleChannel(channel *Channel) {
|
|||||||
func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
|
func (m *PrivMsgCommand) HandleChannel(channel *Channel) {
|
||||||
channel.replies <- RplPrivMsg(m.Client(), channel, m.message)
|
channel.replies <- RplPrivMsg(m.Client(), channel, m.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *ChannelModeCommand) HandleChannel(channel *Channel) {
|
||||||
|
client := msg.Client()
|
||||||
|
|
||||||
|
for _, modeOp := range msg.modeOps {
|
||||||
|
switch modeOp.mode {
|
||||||
|
case BanMask:
|
||||||
|
// TODO add/remove
|
||||||
|
for _, banMask := range channel.banList {
|
||||||
|
client.replies <- RplBanList(channel, banMask)
|
||||||
|
}
|
||||||
|
client.replies <- RplEndOfBanList(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.replies <- RplChannelModeIs(channel)
|
||||||
|
}
|
||||||
|
117
irc/commands.go
117
irc/commands.go
@ -8,22 +8,17 @@ import (
|
|||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Command interface {
|
type editableCommand interface {
|
||||||
Client() *Client
|
|
||||||
Source() Identifier
|
|
||||||
Reply(Reply)
|
|
||||||
HandleServer(*Server)
|
|
||||||
}
|
|
||||||
|
|
||||||
type EditableCommand interface {
|
|
||||||
Command
|
Command
|
||||||
SetBase(*Client)
|
SetBase(*Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type parseCommandFunc func([]string) (editableCommand, error)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
NotEnoughArgsError = errors.New("not enough arguments")
|
NotEnoughArgsError = errors.New("not enough arguments")
|
||||||
ErrParseCommand = errors.New("failed to parse message")
|
ErrParseCommand = errors.New("failed to parse message")
|
||||||
parseCommandFuncs = map[string]func([]string) (EditableCommand, error){
|
parseCommandFuncs = map[string]parseCommandFunc{
|
||||||
"JOIN": NewJoinCommand,
|
"JOIN": NewJoinCommand,
|
||||||
"MODE": NewModeCommand,
|
"MODE": NewModeCommand,
|
||||||
"NICK": NewNickCommand,
|
"NICK": NewNickCommand,
|
||||||
@ -60,7 +55,7 @@ func (command *BaseCommand) Reply(reply Reply) {
|
|||||||
command.client.Replies() <- reply
|
command.client.Replies() <- reply
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseCommand(line string) (EditableCommand, error) {
|
func ParseCommand(line string) (editableCommand, error) {
|
||||||
command, args := parseLine(line)
|
command, args := parseLine(line)
|
||||||
constructor := parseCommandFuncs[command]
|
constructor := parseCommandFuncs[command]
|
||||||
if constructor == nil {
|
if constructor == nil {
|
||||||
@ -126,7 +121,7 @@ func (cmd *PingCommand) String() string {
|
|||||||
return fmt.Sprintf("PING(server=%s, server2=%s)", cmd.server, cmd.server2)
|
return fmt.Sprintf("PING(server=%s, server2=%s)", cmd.server, cmd.server2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPingCommand(args []string) (EditableCommand, error) {
|
func NewPingCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -151,7 +146,7 @@ func (cmd *PongCommand) String() string {
|
|||||||
return fmt.Sprintf("PONG(server1=%s, server2=%s)", cmd.server1, cmd.server2)
|
return fmt.Sprintf("PONG(server1=%s, server2=%s)", cmd.server1, cmd.server2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPongCommand(args []string) (EditableCommand, error) {
|
func NewPongCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -175,7 +170,7 @@ func (cmd *PassCommand) String() string {
|
|||||||
return fmt.Sprintf("PASS(password=%s)", cmd.password)
|
return fmt.Sprintf("PASS(password=%s)", cmd.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPassCommand(args []string) (EditableCommand, error) {
|
func NewPassCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -195,7 +190,7 @@ func (m *NickCommand) String() string {
|
|||||||
return fmt.Sprintf("NICK(nickname=%s)", m.nickname)
|
return fmt.Sprintf("NICK(nickname=%s)", m.nickname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNickCommand(args []string) (EditableCommand, error) {
|
func NewNickCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -219,7 +214,7 @@ func (cmd *UserMsgCommand) String() string {
|
|||||||
cmd.user, cmd.mode, cmd.unused, cmd.realname)
|
cmd.user, cmd.mode, cmd.unused, cmd.realname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUserMsgCommand(args []string) (EditableCommand, error) {
|
func NewUserMsgCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) != 4 {
|
if len(args) != 4 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -246,7 +241,7 @@ func (cmd *QuitCommand) String() string {
|
|||||||
return fmt.Sprintf("QUIT(message=%s)", cmd.message)
|
return fmt.Sprintf("QUIT(message=%s)", cmd.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQuitCommand(args []string) (EditableCommand, error) {
|
func NewQuitCommand(args []string) (editableCommand, error) {
|
||||||
msg := &QuitCommand{}
|
msg := &QuitCommand{}
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
msg.message = args[0]
|
msg.message = args[0]
|
||||||
@ -266,7 +261,7 @@ func (cmd *JoinCommand) String() string {
|
|||||||
return fmt.Sprintf("JOIN(channels=%s, zero=%t)", cmd.channels, cmd.zero)
|
return fmt.Sprintf("JOIN(channels=%s, zero=%t)", cmd.channels, cmd.zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewJoinCommand(args []string) (EditableCommand, error) {
|
func NewJoinCommand(args []string) (editableCommand, error) {
|
||||||
msg := &JoinCommand{
|
msg := &JoinCommand{
|
||||||
channels: make(map[string]string),
|
channels: make(map[string]string),
|
||||||
}
|
}
|
||||||
@ -313,7 +308,7 @@ func (cmd *PartCommand) String() string {
|
|||||||
return fmt.Sprintf("PART(channels=%s, message=%s)", cmd.channels, cmd.message)
|
return fmt.Sprintf("PART(channels=%s, message=%s)", cmd.channels, cmd.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPartCommand(args []string) (EditableCommand, error) {
|
func NewPartCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -338,7 +333,7 @@ func (cmd *PrivMsgCommand) String() string {
|
|||||||
return fmt.Sprintf("PRIVMSG(target=%s, message=%s)", cmd.target, cmd.message)
|
return fmt.Sprintf("PRIVMSG(target=%s, message=%s)", cmd.target, cmd.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrivMsgCommand(args []string) (EditableCommand, error) {
|
func NewPrivMsgCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -364,7 +359,7 @@ func (cmd *TopicCommand) String() string {
|
|||||||
return fmt.Sprintf("TOPIC(channel=%s, topic=%s)", cmd.channel, cmd.topic)
|
return fmt.Sprintf("TOPIC(channel=%s, topic=%s)", cmd.channel, cmd.topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTopicCommand(args []string) (EditableCommand, error) {
|
func NewTopicCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -377,20 +372,8 @@ func NewTopicCommand(args []string) (EditableCommand, error) {
|
|||||||
return msg, nil
|
return msg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mode rune
|
|
||||||
|
|
||||||
const (
|
|
||||||
Away Mode = 'a'
|
|
||||||
Invisible Mode = 'i'
|
|
||||||
WallOps Mode = 'w'
|
|
||||||
Restricted Mode = 'r'
|
|
||||||
Operator Mode = 'o'
|
|
||||||
LocalOperator Mode = 'O'
|
|
||||||
ServerNotice Mode = 's'
|
|
||||||
)
|
|
||||||
|
|
||||||
type ModeChange struct {
|
type ModeChange struct {
|
||||||
mode Mode
|
mode UserMode
|
||||||
add bool // false => remove
|
add bool // false => remove
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -425,22 +408,68 @@ func stringToRunes(str string) <-chan rune {
|
|||||||
return runes
|
return runes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChannelModeOp struct {
|
||||||
|
mode ChannelMode
|
||||||
|
op ModeOp
|
||||||
|
arg string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *ChannelModeOp) String() string {
|
||||||
|
return fmt.Sprintf("{%s %s %s}", op.op, op.mode, op.arg)
|
||||||
|
}
|
||||||
|
|
||||||
type ChannelModeCommand struct {
|
type ChannelModeCommand struct {
|
||||||
BaseCommand
|
BaseCommand
|
||||||
channel string
|
channel string
|
||||||
|
modeOps []ChannelModeOp
|
||||||
}
|
}
|
||||||
|
|
||||||
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
|
// MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
|
||||||
func NewChannelModeCommand(args []string) (EditableCommand, error) {
|
func NewChannelModeCommand(args []string) (editableCommand, error) {
|
||||||
cmd := &ChannelModeCommand{
|
cmd := &ChannelModeCommand{
|
||||||
channel: args[0],
|
channel: args[0],
|
||||||
|
modeOps: make([]ChannelModeOp, 0),
|
||||||
}
|
}
|
||||||
// TODO implement channel mode changes
|
args = args[1:]
|
||||||
|
|
||||||
|
for len(args) > 0 {
|
||||||
|
modeArg := args[0]
|
||||||
|
op := List
|
||||||
|
switch modeArg[0] {
|
||||||
|
case '+':
|
||||||
|
op = Add
|
||||||
|
modeArg = modeArg[1:]
|
||||||
|
case '-':
|
||||||
|
op = Remove
|
||||||
|
modeArg = modeArg[1:]
|
||||||
|
}
|
||||||
|
skipArgs := 1
|
||||||
|
for mode := range stringToRunes(modeArg) {
|
||||||
|
modeOp := ChannelModeOp{
|
||||||
|
mode: ChannelMode(mode),
|
||||||
|
op: op,
|
||||||
|
}
|
||||||
|
switch modeOp.mode {
|
||||||
|
case Key, BanMask, ExceptionMask, InviteMask:
|
||||||
|
if len(args) > skipArgs {
|
||||||
|
modeOp.arg = args[skipArgs]
|
||||||
|
skipArgs += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cmd.modeOps = append(cmd.modeOps, modeOp)
|
||||||
|
}
|
||||||
|
args = args[skipArgs:]
|
||||||
|
}
|
||||||
|
|
||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *ChannelModeCommand) String() string {
|
||||||
|
return fmt.Sprintf("MODE(channel=%s, modeOps=%s)", msg.channel, msg.modeOps)
|
||||||
|
}
|
||||||
|
|
||||||
// MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
|
// MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
|
||||||
func NewUserModeCommand(args []string) (EditableCommand, error) {
|
func NewUserModeCommand(args []string) (editableCommand, error) {
|
||||||
cmd := &ModeCommand{
|
cmd := &ModeCommand{
|
||||||
nickname: args[0],
|
nickname: args[0],
|
||||||
changes: make([]ModeChange,
|
changes: make([]ModeChange,
|
||||||
@ -458,7 +487,7 @@ func NewUserModeCommand(args []string) (EditableCommand, error) {
|
|||||||
add := sig == '+'
|
add := sig == '+'
|
||||||
for mode := range modeChange {
|
for mode := range modeChange {
|
||||||
cmd.changes[index] = ModeChange{
|
cmd.changes[index] = ModeChange{
|
||||||
mode: Mode(mode),
|
mode: UserMode(mode),
|
||||||
add: add,
|
add: add,
|
||||||
}
|
}
|
||||||
index += 1
|
index += 1
|
||||||
@ -468,7 +497,7 @@ func NewUserModeCommand(args []string) (EditableCommand, error) {
|
|||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModeCommand(args []string) (EditableCommand, error) {
|
func NewModeCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -487,7 +516,7 @@ type WhoisCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WHOIS [ <target> ] <mask> *( "," <mask> )
|
// WHOIS [ <target> ] <mask> *( "," <mask> )
|
||||||
func NewWhoisCommand(args []string) (EditableCommand, error) {
|
func NewWhoisCommand(args []string) (editableCommand, error) {
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
return nil, NotEnoughArgsError
|
return nil, NotEnoughArgsError
|
||||||
}
|
}
|
||||||
@ -508,6 +537,10 @@ func NewWhoisCommand(args []string) (EditableCommand, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *WhoisCommand) String() string {
|
||||||
|
return fmt.Sprintf("WHOIS(target=%s, masks=%s)", msg.target, msg.masks)
|
||||||
|
}
|
||||||
|
|
||||||
type WhoCommand struct {
|
type WhoCommand struct {
|
||||||
BaseCommand
|
BaseCommand
|
||||||
mask string
|
mask string
|
||||||
@ -515,7 +548,7 @@ type WhoCommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WHO [ <mask> [ "o" ] ]
|
// WHO [ <mask> [ "o" ] ]
|
||||||
func NewWhoCommand(args []string) (EditableCommand, error) {
|
func NewWhoCommand(args []string) (editableCommand, error) {
|
||||||
cmd := &WhoCommand{}
|
cmd := &WhoCommand{}
|
||||||
|
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
@ -528,3 +561,7 @@ func NewWhoCommand(args []string) (EditableCommand, error) {
|
|||||||
|
|
||||||
return cmd, nil
|
return cmd, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (msg *WhoCommand) String() string {
|
||||||
|
return fmt.Sprintf("WHO(mask=%s, operatorOnly=%s)", msg.mask, msg.operatorOnly)
|
||||||
|
}
|
||||||
|
@ -9,9 +9,7 @@ var (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
VERSION = "ergonomadic-1"
|
VERSION = "ergonomadic-1"
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// numeric codes
|
// numeric codes
|
||||||
RPL_WELCOME = 1
|
RPL_WELCOME = 1
|
||||||
RPL_YOURHOST = 2
|
RPL_YOURHOST = 2
|
||||||
@ -149,6 +147,7 @@ const (
|
|||||||
ERR_NOOPERHOST = 491
|
ERR_NOOPERHOST = 491
|
||||||
ERR_UMODEUNKNOWNFLAG = 501
|
ERR_UMODEUNKNOWNFLAG = 501
|
||||||
ERR_USERSDONTMATCH = 502
|
ERR_USERSDONTMATCH = 502
|
||||||
|
|
||||||
// message codes
|
// message codes
|
||||||
RPL_ERROR = "ERROR"
|
RPL_ERROR = "ERROR"
|
||||||
RPL_INVITE = "INVITE"
|
RPL_INVITE = "INVITE"
|
||||||
@ -158,4 +157,35 @@ const (
|
|||||||
RPL_PONG = "PONG"
|
RPL_PONG = "PONG"
|
||||||
RPL_PRIVMSG = "PRIVMSG"
|
RPL_PRIVMSG = "PRIVMSG"
|
||||||
RPL_QUIT = "QUIT"
|
RPL_QUIT = "QUIT"
|
||||||
|
|
||||||
|
List ModeOp = 'l'
|
||||||
|
Add ModeOp = 'a'
|
||||||
|
Remove ModeOp = 'r'
|
||||||
|
|
||||||
|
Away UserMode = 'a'
|
||||||
|
Invisible UserMode = 'i'
|
||||||
|
WallOps UserMode = 'w'
|
||||||
|
Restricted UserMode = 'r'
|
||||||
|
Operator UserMode = 'o'
|
||||||
|
LocalOperator UserMode = 'O'
|
||||||
|
ServerNotice UserMode = 's'
|
||||||
|
|
||||||
|
Anonymous ChannelMode = 'a'
|
||||||
|
InviteOnly ChannelMode = 'i'
|
||||||
|
Moderated ChannelMode = 'm'
|
||||||
|
NoOutside ChannelMode = 'n'
|
||||||
|
Quiet ChannelMode = 'q'
|
||||||
|
Private ChannelMode = 'p'
|
||||||
|
Secret ChannelMode = 's'
|
||||||
|
ReOp ChannelMode = 'r'
|
||||||
|
OpOnlyTopic ChannelMode = 't'
|
||||||
|
Key ChannelMode = 'k'
|
||||||
|
UserLimit ChannelMode = 'l'
|
||||||
|
BanMask ChannelMode = 'b'
|
||||||
|
ExceptionMask ChannelMode = 'e'
|
||||||
|
InviteMask ChannelMode = 'i'
|
||||||
|
|
||||||
|
ChannelCreator UserChannelMode = 'O'
|
||||||
|
ChannelOperator UserChannelMode = 'o'
|
||||||
|
Voice UserChannelMode = 'v'
|
||||||
)
|
)
|
||||||
|
42
irc/net.go
42
irc/net.go
@ -2,56 +2,68 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func readTrimmedLine(reader *bufio.Reader) (string, error) {
|
|
||||||
line, err := reader.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(line), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapt `net.Conn` to a `chan string`.
|
// Adapt `net.Conn` to a `chan string`.
|
||||||
func StringReadChan(conn net.Conn) <-chan string {
|
func StringReadChan(conn net.Conn) <-chan string {
|
||||||
ch := make(chan string)
|
ch := make(chan string)
|
||||||
reader := bufio.NewReader(conn)
|
reader := bufio.NewReader(conn)
|
||||||
go func() {
|
go func() {
|
||||||
defer conn.Close()
|
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
for {
|
for {
|
||||||
line, err := readTrimmedLine(reader)
|
line, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
log.Printf("%s → %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
log.Printf("%s → %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if DEBUG_NET {
|
if DEBUG_NET {
|
||||||
log.Printf("%s → %s %s", conn.RemoteAddr(), conn.LocalAddr(), line)
|
log.Printf("%s → %s %s", conn.RemoteAddr(), conn.LocalAddr(), line)
|
||||||
}
|
}
|
||||||
ch <- line
|
|
||||||
|
ch <- strings.TrimSpace(line)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
CRLF = "\r\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
func maybeLogWriteError(conn net.Conn, err error) bool {
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
log.Printf("%s ← %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func StringWriteChan(conn net.Conn) chan<- string {
|
func StringWriteChan(conn net.Conn) chan<- string {
|
||||||
ch := make(chan string)
|
ch := make(chan string)
|
||||||
writer := bufio.NewWriter(conn)
|
writer := bufio.NewWriter(conn)
|
||||||
go func() {
|
go func() {
|
||||||
defer conn.Close()
|
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
for str := range ch {
|
for str := range ch {
|
||||||
if DEBUG_NET {
|
if DEBUG_NET {
|
||||||
log.Printf("%s ← %s %s", conn.RemoteAddr(), conn.LocalAddr(), str)
|
log.Printf("%s ← %s %s", conn.RemoteAddr(), conn.LocalAddr(), str)
|
||||||
}
|
}
|
||||||
if _, err := writer.WriteString(str + "\r\n"); err != nil {
|
if _, err := writer.WriteString(str); maybeLogWriteError(conn, err) {
|
||||||
log.Printf("%s ← %s error: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
break
|
||||||
|
}
|
||||||
|
if _, err := writer.WriteString(CRLF); maybeLogWriteError(conn, err) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err := writer.Flush(); maybeLogWriteError(conn, err) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
writer.Flush()
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return ch
|
return ch
|
||||||
|
13
irc/reply.go
13
irc/reply.go
@ -234,8 +234,8 @@ func RplEndOfWhois(server *Server) Reply {
|
|||||||
return NewNumericReply(server, RPL_ENDOFWHOIS, ":End of WHOIS list")
|
return NewNumericReply(server, RPL_ENDOFWHOIS, ":End of WHOIS list")
|
||||||
}
|
}
|
||||||
|
|
||||||
func RplChannelModeIs(server *Server, channel *Channel) Reply {
|
func RplChannelModeIs(channel *Channel) Reply {
|
||||||
return NewNumericReply(server, RPL_CHANNELMODEIS, "%s %s",
|
return NewNumericReply(channel.server, RPL_CHANNELMODEIS, "%s %s",
|
||||||
channel.name, channel.ModeString())
|
channel.name, channel.ModeString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,6 +252,15 @@ func RplEndOfWho(server *Server, name string) Reply {
|
|||||||
return NewNumericReply(server, RPL_ENDOFWHO, "%s :End of WHO list", name)
|
return NewNumericReply(server, RPL_ENDOFWHO, "%s :End of WHO list", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RplBanList(channel *Channel, ban UserMask) Reply {
|
||||||
|
return NewNumericReply(channel.server, RPL_BANLIST, "%s %s", channel.name, ban)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RplEndOfBanList(channel *Channel) Reply {
|
||||||
|
return NewNumericReply(channel.server, RPL_ENDOFBANLIST, "%s :End of channel ban list",
|
||||||
|
channel.name)
|
||||||
|
}
|
||||||
|
|
||||||
// errors (also numeric)
|
// errors (also numeric)
|
||||||
|
|
||||||
func ErrAlreadyRegistered(source Identifier) Reply {
|
func ErrAlreadyRegistered(source Identifier) Reply {
|
||||||
|
@ -307,8 +307,7 @@ func (msg *ChannelModeCommand) HandleServer(server *Server) {
|
|||||||
client.replies <- ErrNoSuchChannel(server, msg.channel)
|
client.replies <- ErrNoSuchChannel(server, msg.channel)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
channel.commands <- msg
|
||||||
client.replies <- RplChannelModeIs(server, channel)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func whoChannel(client *Client, server *Server, channel *Channel) {
|
func whoChannel(client *Client, server *Server, channel *Channel) {
|
||||||
|
34
irc/types.go
Normal file
34
irc/types.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// simple types
|
||||||
|
|
||||||
|
type ModeOp rune
|
||||||
|
type UserMode rune
|
||||||
|
type ChannelMode rune
|
||||||
|
type UserChannelMode rune
|
||||||
|
type Mask string
|
||||||
|
|
||||||
|
// interfaces
|
||||||
|
|
||||||
|
type Command interface {
|
||||||
|
Client() *Client
|
||||||
|
Source() Identifier
|
||||||
|
Reply(Reply)
|
||||||
|
HandleServer(*Server)
|
||||||
|
}
|
||||||
|
|
||||||
|
// structs
|
||||||
|
|
||||||
|
type UserMask struct {
|
||||||
|
nickname Mask
|
||||||
|
username Mask
|
||||||
|
hostname Mask
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mask *UserMask) String() string {
|
||||||
|
return fmt.Sprintf("%s!%s@%s", mask.nickname, mask.username, mask.hostname)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user