Browse Source

Implement mode [+-]i.

tags/v0.1.0
Jeremy Latt 12 years ago
parent
commit
99364e8b5f
7 changed files with 94 additions and 29 deletions
  1. 10
    2
      src/irc/client.go
  2. 20
    0
      src/irc/commands.go
  3. 1
    0
      src/irc/constants.go
  4. 5
    5
      src/irc/message.go
  5. 4
    2
      src/irc/net.go
  6. 45
    19
      src/irc/parse.go
  7. 9
    1
      src/irc/responses.go

+ 10
- 2
src/irc/client.go View File

@@ -5,18 +5,19 @@ import (
5 5
 )
6 6
 
7 7
 type Client struct {
8
-	addr       net.Addr
8
+	conn       net.Conn
9 9
 	send       chan<- string
10 10
 	recv       <-chan string
11 11
 	username   string
12 12
 	realname   string
13 13
 	nick       string
14 14
 	registered bool
15
+	invisible  bool
15 16
 }
16 17
 
17 18
 func NewClient(conn net.Conn) *Client {
18 19
 	client := new(Client)
19
-	client.addr = conn.RemoteAddr()
20
+	client.conn = conn
20 21
 	client.send = StringWriteChan(conn)
21 22
 	client.recv = StringReadChan(conn)
22 23
 	return client
@@ -38,3 +39,10 @@ func (c *Client) Nick() string {
38 39
 	}
39 40
 	return "<guest>"
40 41
 }
42
+
43
+func (c *Client) UModeString() string {
44
+	if c.invisible {
45
+		return "+i"
46
+	}
47
+	return ""
48
+}

+ 20
- 0
src/irc/commands.go View File

@@ -1,5 +1,9 @@
1 1
 package irc
2 2
 
3
+type Message interface {
4
+	Handle(s *Server, c *Client)
5
+}
6
+
3 7
 func (m *NickMessage) Handle(s *Server, c *Client) {
4 8
 	if s.nicks[m.nickname] != nil {
5 9
 		c.send <- ErrNickNameInUse(m.nickname)
@@ -24,6 +28,7 @@ func (m *UserMessage) Handle(s *Server, c *Client) {
24 28
 
25 29
 func (m *QuitMessage) Handle(s *Server, c *Client) {
26 30
 	c.send <- MessageError()
31
+	c.conn.Close()
27 32
 	delete(s.nicks, c.nick)
28 33
 }
29 34
 
@@ -35,6 +40,21 @@ func (m *PingMessage) Handle(s *Server, c *Client) {
35 40
 	c.send <- MessagePong()
36 41
 }
37 42
 
43
+func (m *ModeMessage) Handle(s *Server, c *Client) {
44
+	if m.nickname != c.nick {
45
+		c.send <- ErrUsersDontMatch(c.Nick())
46
+		return
47
+	}
48
+	for _, mode := range m.modes {
49
+		if mode == "+i" {
50
+			c.invisible = true
51
+		} else if mode == "-i" {
52
+			c.invisible = false
53
+		}
54
+	}
55
+	c.send <- ReplyUModeIs(c)
56
+}
57
+
38 58
 func tryRegister(s *Server, c *Client) {
39 59
 	if (!c.registered && c.nick != "" && c.username != "") {
40 60
 		c.registered = true

+ 1
- 0
src/irc/constants.go View File

@@ -10,6 +10,7 @@ const (
10 10
 	RPL_YOURHOST = "002"
11 11
 	RPL_CREATED  = "003"
12 12
 	RPL_MYINFO   = "004"
13
+	RPL_UMODEIS  = "221"
13 14
 	RPL_NONE     = "300"
14 15
 )
15 16
 

+ 5
- 5
src/irc/message.go View File

@@ -1,9 +1,5 @@
1 1
 package irc
2 2
 
3
-type Message interface {
4
-	Handle(s *Server, c *Client)
5
-}
6
-
7 3
 type NickMessage struct {
8 4
 	nickname string
9 5
 }
@@ -15,7 +11,6 @@ type UserMessage struct {
15 11
 	realname string
16 12
 }
17 13
 
18
-
19 14
 type QuitMessage struct {
20 15
 	message string
21 16
 }
@@ -25,3 +20,8 @@ type UnknownMessage struct {
25 20
 }
26 21
 
27 22
 type PingMessage struct {}
23
+
24
+type ModeMessage struct {
25
+	nickname string
26
+	modes []string
27
+}

+ 4
- 2
src/irc/net.go View File

@@ -16,12 +16,13 @@ func readTrimmedLine(reader *bufio.Reader) (string, error) {
16 16
 func StringReadChan(conn net.Conn) <-chan string {
17 17
 	ch := make(chan string)
18 18
 	reader := bufio.NewReader(conn)
19
+	addr := conn.RemoteAddr()
19 20
 	go func() {
20 21
 		for {
21 22
 			line, err := readTrimmedLine(reader)
22 23
 			if (line != "") {
23 24
 				ch <- line
24
-				log.Printf("%s -> %s", conn.RemoteAddr(), line)
25
+				log.Printf("%s -> %s", addr, line)
25 26
 			}
26 27
 			if err != nil {
27 28
 				break
@@ -35,13 +36,14 @@ func StringReadChan(conn net.Conn) <-chan string {
35 36
 func StringWriteChan(conn net.Conn) chan<- string {
36 37
 	ch := make(chan string)
37 38
 	writer := bufio.NewWriter(conn)
39
+	addr := conn.RemoteAddr()
38 40
 	go func() {
39 41
 		for str := range ch {
40 42
 			if _, err := writer.WriteString(str + "\r\n"); err != nil {
41 43
 				break
42 44
 			}
43 45
 			writer.Flush()
44
-			log.Printf("%s <- %s", conn.RemoteAddr(), str)
46
+			log.Printf("%s <- %s", addr, str)
45 47
 		}
46 48
 		close(ch)
47 49
 	}()

+ 45
- 19
src/irc/parse.go View File

@@ -1,10 +1,33 @@
1 1
 package irc
2 2
 
3 3
 import (
4
+	"fmt"
5
+	"regexp"
4 6
 	"strconv"
5 7
 	"strings"
6 8
 )
7 9
 
10
+var commands = map[string]func([]string) Message {
11
+	"MODE": NewModeMessage,
12
+	"NICK": NewNickMessage,
13
+	"PING": NewPingMessage,
14
+	"QUIT": NewQuitMessage,
15
+	"USER": NewUserMessage,
16
+}
17
+
18
+func ParseMessage(line string) Message {
19
+	command, args := parseLine(line)
20
+	constructor, ok := commands[command]
21
+	var msg Message
22
+	if ok {
23
+		msg = constructor(args)
24
+	}
25
+	if msg == nil {
26
+		msg = &UnknownMessage{command}
27
+	}
28
+	return msg
29
+}
30
+
8 31
 func parseArg(line string) (string, string) {
9 32
 	if line == "" {
10 33
 		return "", ""
@@ -31,25 +54,6 @@ func parseLine(line string) (string, []string) {
31 54
 	return args[0], args[1:]
32 55
 }
33 56
 
34
-var commands = map[string]func([]string) Message {
35
-	"NICK": NewNickMessage,
36
-	"PING": NewPingMessage,
37
-	"QUIT": NewQuitMessage,
38
-	"USER": NewUserMessage,
39
-}
40
-
41
-func ParseMessage(line string) Message {
42
-	command, args := parseLine(line)
43
-	constructor, ok := commands[command]
44
-	var msg Message
45
-	if ok {
46
-		msg = constructor(args)
47
-	}
48
-	if msg == nil {
49
-		msg = &UnknownMessage{command}
50
-	}
51
-	return msg
52
-}
53 57
 
54 58
 // []string => Message constructors
55 59
 
@@ -86,3 +90,25 @@ func NewUserMessage(args []string) Message {
86 90
 	msg.realname = args[3]
87 91
 	return msg
88 92
 }
93
+
94
+var MODE_RE = regexp.MustCompile("^[-+][a-zA-Z]+$")
95
+
96
+func NewModeMessage(args []string) Message {
97
+	if len(args) < 1 {
98
+		return nil
99
+	}
100
+	msg := new(ModeMessage)
101
+	msg.nickname = args[0]
102
+	for _, arg := range args[1:] {
103
+		if !MODE_RE.MatchString(arg) {
104
+			// TODO invalid args
105
+			return nil
106
+		}
107
+		prefix := arg[0]
108
+		for _, c := range arg[1:] {
109
+			mode := fmt.Sprintf("%c%c", prefix, c)
110
+			msg.modes = append(msg.modes, mode)
111
+		}
112
+	}
113
+	return msg
114
+}

+ 9
- 1
src/irc/responses.go View File

@@ -17,7 +17,11 @@ func ReplyCreated(nick string, created string) string {
17 17
 }
18 18
 
19 19
 func ReplyMyInfo(nick string, servername string) string {
20
-	return fmt.Sprintf("%s %s %s %s <user modes> <channel modes>", RPL_MYINFO, nick, servername, VERSION)
20
+	return fmt.Sprintf("%s %s %s %s i <channel modes>", RPL_MYINFO, nick, servername, VERSION)
21
+}
22
+
23
+func ReplyUModeIs(c *Client) string {
24
+	return fmt.Sprintf("%s %s %s", RPL_UMODEIS, c.Nick(), c.UModeString())
21 25
 }
22 26
 
23 27
 func ErrAlreadyRegistered(nick string) string {
@@ -32,6 +36,10 @@ func ErrUnknownCommand(nick string, command string) string {
32 36
 	return fmt.Sprintf("%s %s %s :Unknown command", ERR_UNKNOWNCOMMAND, nick, command)
33 37
 }
34 38
 
39
+func ErrUsersDontMatch(nick string) string {
40
+	return fmt.Sprintf("%s %s :Cannot change mode for other users", ERR_USERSDONTMATCH, nick)
41
+}
42
+
35 43
 func MessagePong() string {
36 44
 	return "PONG"
37 45
 }

Loading…
Cancel
Save