Bläddra i källkod

Merge remote-tracking branch 'origin/master' into user-mask

Conflicts:
	irc/reply.go
	irc/server.go
	irc/types.go
tags/v0.1.0
Jeremy Latt 10 år sedan
förälder
incheckning
5d46e7d7fa
7 ändrade filer med 224 tillägg och 101 borttagningar
  1. 16
    8
      irc/channel.go
  2. 40
    34
      irc/client.go
  3. 20
    6
      irc/commands.go
  4. 30
    3
      irc/constants.go
  5. 19
    6
      irc/reply.go
  6. 73
    42
      irc/server.go
  7. 26
    2
      irc/types.go

+ 16
- 8
irc/channel.go Visa fil

54
 	return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator)
54
 	return client.flags[Operator] || channel.members.HasMode(client, ChannelOperator)
55
 }
55
 }
56
 
56
 
57
-func (channel *Channel) Nicks() []string {
57
+func (channel *Channel) Nicks(target *Client) []string {
58
+	isMultiPrefix := (target != nil) && target.capabilities[MultiPrefix]
58
 	nicks := make([]string, len(channel.members))
59
 	nicks := make([]string, len(channel.members))
59
 	i := 0
60
 	i := 0
60
 	for client, modes := range channel.members {
61
 	for client, modes := range channel.members {
61
-		switch {
62
-		case modes[ChannelOperator]:
63
-			nicks[i] = "@" + client.Nick()
64
-		case modes[Voice]:
65
-			nicks[i] = "+" + client.Nick()
66
-		default:
67
-			nicks[i] = client.Nick()
62
+		if isMultiPrefix {
63
+			if modes[ChannelOperator] {
64
+				nicks[i] += "@"
65
+			}
66
+			if modes[Voice] {
67
+				nicks[i] += "+"
68
+			}
69
+		} else {
70
+			if modes[ChannelOperator] {
71
+				nicks[i] += "@"
72
+			} else if modes[Voice] {
73
+				nicks[i] += "+"
74
+			}
68
 		}
75
 		}
76
+		nicks[i] += client.Nick()
69
 		i += 1
77
 		i += 1
70
 	}
78
 	}
71
 	return nicks
79
 	return nicks

+ 40
- 34
irc/client.go Visa fil

12
 }
12
 }
13
 
13
 
14
 type Client struct {
14
 type Client struct {
15
-	atime       time.Time
16
-	awayMessage string
17
-	channels    ChannelSet
18
-	commands    chan editableCommand
19
-	ctime       time.Time
20
-	flags       map[UserMode]bool
21
-	hasQuit     bool
22
-	hops        uint
23
-	hostname    string
24
-	idleTimer   *time.Timer
25
-	loginTimer  *time.Timer
26
-	nick        string
27
-	phase       Phase
28
-	quitTimer   *time.Timer
29
-	realname    string
30
-	server      *Server
31
-	socket      *Socket
32
-	username    string
15
+	atime        time.Time
16
+	authorized   bool
17
+	awayMessage  string
18
+	capabilities CapabilitySet
19
+	capState     CapState
20
+	channels     ChannelSet
21
+	commands     chan editableCommand
22
+	ctime        time.Time
23
+	flags        map[UserMode]bool
24
+	hasQuit      bool
25
+	hops         uint
26
+	hostname     string
27
+	idleTimer    *time.Timer
28
+	loginTimer   *time.Timer
29
+	nick         string
30
+	phase        Phase
31
+	quitTimer    *time.Timer
32
+	realname     string
33
+	server       *Server
34
+	socket       *Socket
35
+	username     string
33
 }
36
 }
34
 
37
 
35
 func NewClient(server *Server, conn net.Conn) *Client {
38
 func NewClient(server *Server, conn net.Conn) *Client {
36
 	now := time.Now()
39
 	now := time.Now()
37
 	client := &Client{
40
 	client := &Client{
38
-		atime:    now,
39
-		channels: make(ChannelSet),
40
-		commands: make(chan editableCommand),
41
-		ctime:    now,
42
-		flags:    make(map[UserMode]bool),
43
-		phase:    server.InitPhase(),
44
-		server:   server,
41
+		atime:        now,
42
+		capState:     CapNone,
43
+		capabilities: make(CapabilitySet),
44
+		channels:     make(ChannelSet),
45
+		commands:     make(chan editableCommand),
46
+		ctime:        now,
47
+		flags:        make(map[UserMode]bool),
48
+		phase:        Registration,
49
+		server:       server,
45
 	}
50
 	}
46
 	client.socket = NewSocket(conn, client.commands)
51
 	client.socket = NewSocket(conn, client.commands)
47
 	client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
52
 	client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.connectionTimeout)
68
 	}
73
 	}
69
 }
74
 }
70
 
75
 
76
+func (client *Client) connectionTimeout() {
77
+	client.commands <- &QuitCommand{
78
+		message: "connection timeout",
79
+	}
80
+}
81
+
71
 //
82
 //
72
 // idle timer goroutine
83
 // idle timer goroutine
73
 //
84
 //
76
 	client.server.idle <- client
87
 	client.server.idle <- client
77
 }
88
 }
78
 
89
 
79
-//
80
-// quit timer goroutine
81
-//
82
-
83
-func (client *Client) connectionTimeout() {
84
-	client.server.timeout <- client
85
-}
86
-
87
 //
90
 //
88
 // server goroutine
91
 // server goroutine
89
 //
92
 //
233
 	}
236
 	}
234
 }
237
 }
235
 
238
 
236
-func (client *Client) Reply(reply string) {
239
+func (client *Client) Reply(reply string, args ...interface{}) {
240
+	if len(args) > 0 {
241
+		reply = fmt.Sprintf(reply, args...)
242
+	}
237
 	client.socket.Write(reply)
243
 	client.socket.Write(reply)
238
 }
244
 }
239
 
245
 

+ 20
- 6
irc/commands.go Visa fil

706
 	return cmd, nil
706
 	return cmd, nil
707
 }
707
 }
708
 
708
 
709
-// TODO
710
 type CapCommand struct {
709
 type CapCommand struct {
711
 	BaseCommand
710
 	BaseCommand
712
-	args []string
711
+	subCommand   CapSubCommand
712
+	capabilities CapabilitySet
713
 }
713
 }
714
 
714
 
715
 func (msg *CapCommand) String() string {
715
 func (msg *CapCommand) String() string {
716
-	return fmt.Sprintf("CAP(args=%s)", msg.args)
716
+	return fmt.Sprintf("CAP(subCommand=%s, capabilities=%s)",
717
+		msg.subCommand, msg.capabilities)
717
 }
718
 }
718
 
719
 
719
 func NewCapCommand(args []string) (editableCommand, error) {
720
 func NewCapCommand(args []string) (editableCommand, error) {
720
-	return &CapCommand{
721
-		args: args,
722
-	}, nil
721
+	if len(args) < 1 {
722
+		return nil, NotEnoughArgsError
723
+	}
724
+
725
+	cmd := &CapCommand{
726
+		subCommand:   CapSubCommand(strings.ToUpper(args[0])),
727
+		capabilities: make(CapabilitySet),
728
+	}
729
+
730
+	if len(args) > 1 {
731
+		strs := spacesExpr.Split(args[1], -1)
732
+		for _, str := range strs {
733
+			cmd.capabilities[Capability(str)] = true
734
+		}
735
+	}
736
+	return cmd, nil
723
 }
737
 }
724
 
738
 
725
 // HAPROXY support
739
 // HAPROXY support

+ 30
- 3
irc/constants.go Visa fil

156
 	ERR_TOOMANYTARGETS    NumericCode = 407
156
 	ERR_TOOMANYTARGETS    NumericCode = 407
157
 	ERR_NOSUCHSERVICE     NumericCode = 408
157
 	ERR_NOSUCHSERVICE     NumericCode = 408
158
 	ERR_NOORIGIN          NumericCode = 409
158
 	ERR_NOORIGIN          NumericCode = 409
159
+	ERR_INVALIDCAPCMD     NumericCode = 410
159
 	ERR_NORECIPIENT       NumericCode = 411
160
 	ERR_NORECIPIENT       NumericCode = 411
160
 	ERR_NOTEXTTOSEND      NumericCode = 412
161
 	ERR_NOTEXTTOSEND      NumericCode = 412
161
 	ERR_NOTOPLEVEL        NumericCode = 413
162
 	ERR_NOTOPLEVEL        NumericCode = 413
201
 	ERR_UMODEUNKNOWNFLAG  NumericCode = 501
202
 	ERR_UMODEUNKNOWNFLAG  NumericCode = 501
202
 	ERR_USERSDONTMATCH    NumericCode = 502
203
 	ERR_USERSDONTMATCH    NumericCode = 502
203
 
204
 
205
+	CAP_LS    CapSubCommand = "LS"
206
+	CAP_LIST  CapSubCommand = "LIST"
207
+	CAP_REQ   CapSubCommand = "REQ"
208
+	CAP_ACK   CapSubCommand = "ACK"
209
+	CAP_NAK   CapSubCommand = "NAK"
210
+	CAP_CLEAR CapSubCommand = "CLEAR"
211
+	CAP_END   CapSubCommand = "END"
212
+
204
 	Add    ModeOp = '+'
213
 	Add    ModeOp = '+'
205
 	List   ModeOp = '='
214
 	List   ModeOp = '='
206
 	Remove ModeOp = '-'
215
 	Remove ModeOp = '-'
231
 	Secret          ChannelMode = 's' // flag, deprecated
240
 	Secret          ChannelMode = 's' // flag, deprecated
232
 	UserLimit       ChannelMode = 'l' // flag arg
241
 	UserLimit       ChannelMode = 'l' // flag arg
233
 	Voice           ChannelMode = 'v' // arg
242
 	Voice           ChannelMode = 'v' // arg
243
+
244
+	MultiPrefix Capability = "multi-prefix"
245
+	SASL        Capability = "sasl"
246
+
247
+	Disable CapModifier = '-'
248
+	Ack     CapModifier = '~'
249
+	Sticky  CapModifier = '='
250
+)
251
+
252
+var (
253
+	SupportedCapabilities = CapabilitySet{
254
+		MultiPrefix: true,
255
+	}
256
+)
257
+
258
+const (
259
+	Registration Phase = iota
260
+	Normal       Phase = iota
234
 )
261
 )
235
 
262
 
236
 const (
263
 const (
237
-	Authorization Phase = iota
238
-	Registration  Phase = iota
239
-	Normal        Phase = iota
264
+	CapNone        CapState = iota
265
+	CapNegotiating CapState = iota
266
+	CapNegotiated  CapState = iota
240
 )
267
 )

+ 19
- 6
irc/reply.go Visa fil

250
 
250
 
251
 	if channel != nil {
251
 	if channel != nil {
252
 		channelName = channel.name
252
 		channelName = channel.name
253
-
254
-		if channel.members[client][ChannelOperator] {
255
-			flags += "@"
256
-		} else if channel.members[client][Voice] {
257
-			flags += "+"
253
+		if target.capabilities[MultiPrefix] {
254
+			if channel.members[client][ChannelOperator] {
255
+				flags += "@"
256
+			}
257
+			if channel.members[client][Voice] {
258
+				flags += "+"
259
+			}
260
+		} else {
261
+			if channel.members[client][ChannelOperator] {
262
+				flags += "@"
263
+			} else if channel.members[client][Voice] {
264
+				flags += "+"
265
+			}
258
 		}
266
 		}
259
 	}
267
 	}
260
 	target.NumericReply(RPL_WHOREPLY,
268
 	target.NumericReply(RPL_WHOREPLY,
370
 }
378
 }
371
 
379
 
372
 func (target *Client) RplNamReply(channel *Channel) {
380
 func (target *Client) RplNamReply(channel *Channel) {
373
-	target.MultilineReply(channel.Nicks(), RPL_NAMREPLY,
381
+	target.MultilineReply(channel.Nicks(target), RPL_NAMREPLY,
374
 		"= %s :%s", channel)
382
 		"= %s :%s", channel)
375
 }
383
 }
376
 
384
 
528
 	target.NumericReply(ERR_WASNOSUCHNICK,
536
 	target.NumericReply(ERR_WASNOSUCHNICK,
529
 		"%s :There was no such nickname", nickname)
537
 		"%s :There was no such nickname", nickname)
530
 }
538
 }
539
+
540
+func (target *Client) ErrInvalidCapCmd(subCommand CapSubCommand) {
541
+	target.NumericReply(ERR_INVALIDCAPCMD,
542
+		"%s :Invalid CAP subcommand", subCommand)
543
+}

+ 73
- 42
irc/server.go Visa fil

29
 	operators map[string][]byte
29
 	operators map[string][]byte
30
 	password  []byte
30
 	password  []byte
31
 	signals   chan os.Signal
31
 	signals   chan os.Signal
32
-	timeout   chan *Client
33
 	whoWas    *WhoWasList
32
 	whoWas    *WhoWasList
34
 }
33
 }
35
 
34
 
46
 		newConns:  make(chan net.Conn, 16),
45
 		newConns:  make(chan net.Conn, 16),
47
 		operators: config.Operators(),
46
 		operators: config.Operators(),
48
 		signals:   make(chan os.Signal, 1),
47
 		signals:   make(chan os.Signal, 1),
49
-		timeout:   make(chan *Client, 16),
50
 		whoWas:    NewWhoWasList(100),
48
 		whoWas:    NewWhoWasList(100),
51
 	}
49
 	}
52
 
50
 
99
 	}
97
 	}
100
 
98
 
101
 	switch client.phase {
99
 	switch client.phase {
102
-	case Authorization:
103
-		authCmd, ok := cmd.(AuthServerCommand)
104
-		if !ok {
105
-			client.Quit("unexpected command")
106
-			return
107
-		}
108
-		authCmd.HandleAuthServer(server)
109
-
110
 	case Registration:
100
 	case Registration:
111
 		regCmd, ok := cmd.(RegServerCommand)
101
 		regCmd, ok := cmd.(RegServerCommand)
112
 		if !ok {
102
 		if !ok {
115
 		}
105
 		}
116
 		regCmd.HandleRegServer(server)
106
 		regCmd.HandleRegServer(server)
117
 
107
 
118
-	default:
108
+	case Normal:
119
 		srvCmd, ok := cmd.(ServerCommand)
109
 		srvCmd, ok := cmd.(ServerCommand)
120
 		if !ok {
110
 		if !ok {
121
 			client.ErrUnknownCommand(cmd.Code())
111
 			client.ErrUnknownCommand(cmd.Code())
159
 
149
 
160
 		case client := <-server.idle:
150
 		case client := <-server.idle:
161
 			client.Idle()
151
 			client.Idle()
162
-
163
-		case client := <-server.timeout:
164
-			client.Quit("connection timeout")
165
 		}
152
 		}
166
 	}
153
 	}
167
 }
154
 }
168
 
155
 
169
-func (server *Server) InitPhase() Phase {
170
-	if server.password == nil {
171
-		return Registration
172
-	}
173
-	return Authorization
174
-}
175
-
176
 //
156
 //
177
 // listen goroutine
157
 // listen goroutine
178
 //
158
 //
208
 //
188
 //
209
 
189
 
210
 func (s *Server) tryRegister(c *Client) {
190
 func (s *Server) tryRegister(c *Client) {
211
-	if c.HasNick() && c.HasUsername() {
191
+	if c.HasNick() && c.HasUsername() && (c.capState != CapNegotiating) {
212
 		c.Register()
192
 		c.Register()
213
 		c.RplWelcome()
193
 		c.RplWelcome()
214
 		c.RplYourHost()
194
 		c.RplYourHost()
268
 }
248
 }
269
 
249
 
270
 //
250
 //
271
-// authorization commands
251
+// registration commands
272
 //
252
 //
273
 
253
 
274
-func (msg *ProxyCommand) HandleAuthServer(server *Server) {
275
-	msg.Client().hostname = msg.hostname
276
-}
277
-
278
-func (msg *CapCommand) HandleAuthServer(server *Server) {
279
-	// TODO
280
-}
281
-
282
-func (msg *PassCommand) HandleAuthServer(server *Server) {
254
+func (msg *PassCommand) HandleRegServer(server *Server) {
283
 	client := msg.Client()
255
 	client := msg.Client()
284
 	if msg.err != nil {
256
 	if msg.err != nil {
285
 		client.ErrPasswdMismatch()
257
 		client.ErrPasswdMismatch()
287
 		return
259
 		return
288
 	}
260
 	}
289
 
261
 
290
-	client.phase = Registration
262
+	client.authorized = true
291
 }
263
 }
292
 
264
 
293
-func (msg *QuitCommand) HandleAuthServer(server *Server) {
294
-	msg.Client().Quit(msg.message)
295
-}
296
-
297
-//
298
-// registration commands
299
-//
300
-
301
 func (msg *ProxyCommand) HandleRegServer(server *Server) {
265
 func (msg *ProxyCommand) HandleRegServer(server *Server) {
302
 	msg.Client().hostname = msg.hostname
266
 	msg.Client().hostname = msg.hostname
303
 }
267
 }
304
 
268
 
305
 func (msg *CapCommand) HandleRegServer(server *Server) {
269
 func (msg *CapCommand) HandleRegServer(server *Server) {
306
-	// TODO
270
+	client := msg.Client()
271
+
272
+	switch msg.subCommand {
273
+	case CAP_LS:
274
+		client.capState = CapNegotiating
275
+		client.Reply("CAP LS * :%s", SupportedCapabilities)
276
+
277
+	case CAP_LIST:
278
+		client.Reply("CAP LIST * :%s", client.capabilities)
279
+
280
+	case CAP_REQ:
281
+		client.capState = CapNegotiating
282
+		for capability := range msg.capabilities {
283
+			if !SupportedCapabilities[capability] {
284
+				client.Reply("CAP NAK * :%s", msg.capabilities)
285
+				return
286
+			}
287
+		}
288
+		for capability := range msg.capabilities {
289
+			client.capabilities[capability] = true
290
+		}
291
+		client.Reply("CAP ACK * :%s", msg.capabilities)
292
+
293
+	case CAP_CLEAR:
294
+		format := strings.TrimRight(
295
+			strings.Repeat("%s%s ", len(client.capabilities)), " ")
296
+		args := make([]interface{}, len(client.capabilities))
297
+		index := 0
298
+		for capability := range client.capabilities {
299
+			args[index] = Disable
300
+			args[index+1] = capability
301
+			index += 2
302
+			delete(client.capabilities, capability)
303
+		}
304
+		client.Reply("CAP ACK * :"+format, args...)
305
+
306
+	case CAP_END:
307
+		client.capState = CapNegotiated
308
+		server.tryRegister(client)
309
+
310
+	default:
311
+		client.ErrInvalidCapCmd(msg.subCommand)
312
+	}
307
 }
313
 }
308
 
314
 
309
 func (m *NickCommand) HandleRegServer(s *Server) {
315
 func (m *NickCommand) HandleRegServer(s *Server) {
310
 	client := m.Client()
316
 	client := m.Client()
317
+	if !client.authorized {
318
+		client.ErrPasswdMismatch()
319
+		client.Quit("bad password")
320
+		return
321
+	}
322
+
323
+	if client.capState == CapNegotiating {
324
+		client.capState = CapNegotiated
325
+	}
311
 
326
 
312
 	if m.nickname == "" {
327
 	if m.nickname == "" {
313
 		client.ErrNoNicknameGiven()
328
 		client.ErrNoNicknameGiven()
329
 }
344
 }
330
 
345
 
331
 func (msg *RFC1459UserCommand) HandleRegServer(server *Server) {
346
 func (msg *RFC1459UserCommand) HandleRegServer(server *Server) {
347
+	client := msg.Client()
348
+	if !client.authorized {
349
+		client.ErrPasswdMismatch()
350
+		client.Quit("bad password")
351
+		return
352
+	}
332
 	msg.setUserInfo(server)
353
 	msg.setUserInfo(server)
333
 }
354
 }
334
 
355
 
335
 func (msg *RFC2812UserCommand) HandleRegServer(server *Server) {
356
 func (msg *RFC2812UserCommand) HandleRegServer(server *Server) {
336
 	client := msg.Client()
357
 	client := msg.Client()
358
+	if !client.authorized {
359
+		client.ErrPasswdMismatch()
360
+		client.Quit("bad password")
361
+		return
362
+	}
337
 	flags := msg.Flags()
363
 	flags := msg.Flags()
338
 	if len(flags) > 0 {
364
 	if len(flags) > 0 {
339
 		for _, mode := range msg.Flags() {
365
 		for _, mode := range msg.Flags() {
346
 
372
 
347
 func (msg *UserCommand) setUserInfo(server *Server) {
373
 func (msg *UserCommand) setUserInfo(server *Server) {
348
 	client := msg.Client()
374
 	client := msg.Client()
375
+	if client.capState == CapNegotiating {
376
+		client.capState = CapNegotiated
377
+	}
378
+
349
 	server.clients.Remove(client)
379
 	server.clients.Remove(client)
350
 	client.username, client.realname = msg.username, msg.realname
380
 	client.username, client.realname = msg.username, msg.realname
351
 	server.clients.Add(client)
381
 	server.clients.Add(client)
382
+
352
 	server.tryRegister(client)
383
 	server.tryRegister(client)
353
 }
384
 }
354
 
385
 

+ 26
- 2
irc/types.go Visa fil

11
 
11
 
12
 type UserMaskSet map[string]bool
12
 type UserMaskSet map[string]bool
13
 
13
 
14
+type CapSubCommand string
15
+
16
+type Capability string
17
+
18
+type CapModifier rune
19
+
20
+func (mod CapModifier) String() string {
21
+	return string(mod)
22
+}
23
+
24
+type CapState uint
25
+
26
+type CapabilitySet map[Capability]bool
27
+
28
+func (set CapabilitySet) String() string {
29
+	strs := make([]string, len(set))
30
+	index := 0
31
+	for capability := range set {
32
+		strs[index] = string(capability)
33
+		index += 1
34
+	}
35
+	return strings.Join(strs, " ")
36
+}
37
+
14
 // add, remove, list modes
38
 // add, remove, list modes
15
 type ModeOp rune
39
 type ModeOp rune
16
 
40
 
22
 type UserMode rune
46
 type UserMode rune
23
 
47
 
24
 func (mode UserMode) String() string {
48
 func (mode UserMode) String() string {
25
-	return fmt.Sprintf("%c", mode)
49
+	return string(mode)
26
 }
50
 }
27
 
51
 
28
 type Phase uint
52
 type Phase uint
47
 type ChannelMode rune
71
 type ChannelMode rune
48
 
72
 
49
 func (mode ChannelMode) String() string {
73
 func (mode ChannelMode) String() string {
50
-	return fmt.Sprintf("%c", mode)
74
+	return string(mode)
51
 }
75
 }
52
 
76
 
53
 type ChannelNameMap map[string]*Channel
77
 type ChannelNameMap map[string]*Channel

Laddar…
Avbryt
Spara