소스 검색

Merge pull request #35 from jlatt/fix-goroutine-leak

fix two bugs
tags/v0.1.0
Jeremy Latt 10 년 전
부모
커밋
2e212e3692
9개의 변경된 파일122개의 추가작업 그리고 209개의 파일을 삭제
  1. 4
    2
      irc/channel.go
  2. 4
    9
      irc/client.go
  3. 3
    2
      irc/client_lookup_set.go
  4. 80
    151
      irc/commands.go
  5. 9
    8
      irc/nickname.go
  6. 1
    1
      irc/reply.go
  7. 7
    4
      irc/server.go
  8. 10
    14
      irc/socket.go
  9. 4
    18
      irc/theater.go

+ 4
- 2
irc/channel.go 파일 보기

244
 		client.ErrCannotSendToChan(channel)
244
 		client.ErrCannotSendToChan(channel)
245
 		return
245
 		return
246
 	}
246
 	}
247
+	reply := RplPrivMsg(client, channel, message)
247
 	for member := range channel.members {
248
 	for member := range channel.members {
248
 		if member == client {
249
 		if member == client {
249
 			continue
250
 			continue
250
 		}
251
 		}
251
-		member.Reply(RplPrivMsg(client, channel, message))
252
+		member.Reply(reply)
252
 	}
253
 	}
253
 }
254
 }
254
 
255
 
458
 		client.ErrCannotSendToChan(channel)
459
 		client.ErrCannotSendToChan(channel)
459
 		return
460
 		return
460
 	}
461
 	}
462
+	reply := RplNotice(client, channel, message)
461
 	for member := range channel.members {
463
 	for member := range channel.members {
462
 		if member == client {
464
 		if member == client {
463
 			continue
465
 			continue
464
 		}
466
 		}
465
-		member.Reply(RplNotice(client, channel, message))
467
+		member.Reply(reply)
466
 	}
468
 	}
467
 }
469
 }
468
 
470
 

+ 4
- 9
irc/client.go 파일 보기

62
 
62
 
63
 func (client *Client) run() {
63
 func (client *Client) run() {
64
 	for command := range client.commands {
64
 	for command := range client.commands {
65
-		command.SetClient(client)
66
-
67
-		checkPass, ok := command.(checkPasswordCommand)
68
-		if ok {
65
+		if checkPass, ok := command.(checkPasswordCommand); ok {
69
 			checkPass.LoadPassword(client.server)
66
 			checkPass.LoadPassword(client.server)
70
 			// Block the client thread while handling a potentially expensive
67
 			// Block the client thread while handling a potentially expensive
71
 			// password bcrypt operation. Since the server is single-threaded
68
 			// password bcrypt operation. Since the server is single-threaded
74
 			// completes. This could be a form of DoS if handled naively.
71
 			// completes. This could be a form of DoS if handled naively.
75
 			checkPass.CheckPassword()
72
 			checkPass.CheckPassword()
76
 		}
73
 		}
77
-
74
+		command.SetClient(client)
78
 		client.server.commands <- command
75
 		client.server.commands <- command
79
 	}
76
 	}
80
 }
77
 }
81
 
78
 
82
 func (client *Client) connectionTimeout() {
79
 func (client *Client) connectionTimeout() {
83
-	client.commands <- &QuitCommand{
84
-		message: "connection timeout",
85
-	}
80
+	client.commands <- NewQuitCommand("connection timeout")
86
 }
81
 }
87
 
82
 
88
 //
83
 //
259
 		return
254
 		return
260
 	}
255
 	}
261
 
256
 
262
-	client.Reply(RplError("connection closed"))
263
 	client.hasQuit = true
257
 	client.hasQuit = true
258
+	client.Reply(RplError("quit"))
264
 	client.server.whoWas.Append(client)
259
 	client.server.whoWas.Append(client)
265
 	friends := client.Friends()
260
 	friends := client.Friends()
266
 	friends.Remove(client)
261
 	friends.Remove(client)

+ 3
- 2
irc/client_lookup_set.go 파일 보기

92
 		return
92
 		return
93
 	}
93
 	}
94
 	for rows.Next() {
94
 	for rows.Next() {
95
-		var nickname Name
96
-		err := rows.Scan(&nickname)
95
+		var sqlNickname string
96
+		err := rows.Scan(&sqlNickname)
97
 		if err != nil {
97
 		if err != nil {
98
 			Log.error.Println("ClientLookupSet.FindAll.Scan:", err)
98
 			Log.error.Println("ClientLookupSet.FindAll.Scan:", err)
99
 			return
99
 			return
100
 		}
100
 		}
101
+		nickname := Name(sqlNickname)
101
 		client := clients.Get(nickname)
102
 		client := clients.Get(nickname)
102
 		if client == nil {
103
 		if client == nil {
103
 			Log.error.Println("ClientLookupSet.FindAll: missing client:", nickname)
104
 			Log.error.Println("ClientLookupSet.FindAll: missing client:", nickname)

+ 80
- 151
irc/commands.go 파일 보기

26
 	NotEnoughArgsError = errors.New("not enough arguments")
26
 	NotEnoughArgsError = errors.New("not enough arguments")
27
 	ErrParseCommand    = errors.New("failed to parse message")
27
 	ErrParseCommand    = errors.New("failed to parse message")
28
 	parseCommandFuncs  = map[StringCode]parseCommandFunc{
28
 	parseCommandFuncs  = map[StringCode]parseCommandFunc{
29
-		AWAY:    NewAwayCommand,
30
-		CAP:     NewCapCommand,
31
-		DEBUG:   NewDebugCommand,
32
-		INVITE:  NewInviteCommand,
33
-		ISON:    NewIsOnCommand,
34
-		JOIN:    NewJoinCommand,
35
-		KICK:    NewKickCommand,
36
-		KILL:    NewKillCommand,
37
-		LIST:    NewListCommand,
38
-		MODE:    NewModeCommand,
39
-		MOTD:    NewMOTDCommand,
40
-		NAMES:   NewNamesCommand,
41
-		NICK:    NewNickCommand,
42
-		NOTICE:  NewNoticeCommand,
43
-		ONICK:   NewOperNickCommand,
44
-		OPER:    NewOperCommand,
45
-		PART:    NewPartCommand,
46
-		PASS:    NewPassCommand,
47
-		PING:    NewPingCommand,
48
-		PONG:    NewPongCommand,
49
-		PRIVMSG: NewPrivMsgCommand,
50
-		PROXY:   NewProxyCommand,
51
-		QUIT:    NewQuitCommand,
52
-		THEATER: NewTheaterCommand, // nonstandard
53
-		TIME:    NewTimeCommand,
54
-		TOPIC:   NewTopicCommand,
55
-		USER:    NewUserCommand,
56
-		VERSION: NewVersionCommand,
57
-		WHO:     NewWhoCommand,
58
-		WHOIS:   NewWhoisCommand,
59
-		WHOWAS:  NewWhoWasCommand,
29
+		AWAY:    ParseAwayCommand,
30
+		CAP:     ParseCapCommand,
31
+		DEBUG:   ParseDebugCommand,
32
+		INVITE:  ParseInviteCommand,
33
+		ISON:    ParseIsOnCommand,
34
+		JOIN:    ParseJoinCommand,
35
+		KICK:    ParseKickCommand,
36
+		KILL:    ParseKillCommand,
37
+		LIST:    ParseListCommand,
38
+		MODE:    ParseModeCommand,
39
+		MOTD:    ParseMOTDCommand,
40
+		NAMES:   ParseNamesCommand,
41
+		NICK:    ParseNickCommand,
42
+		NOTICE:  ParseNoticeCommand,
43
+		ONICK:   ParseOperNickCommand,
44
+		OPER:    ParseOperCommand,
45
+		PART:    ParsePartCommand,
46
+		PASS:    ParsePassCommand,
47
+		PING:    ParsePingCommand,
48
+		PONG:    ParsePongCommand,
49
+		PRIVMSG: ParsePrivMsgCommand,
50
+		PROXY:   ParseProxyCommand,
51
+		QUIT:    ParseQuitCommand,
52
+		THEATER: ParseTheaterCommand, // nonstandard
53
+		TIME:    ParseTimeCommand,
54
+		TOPIC:   ParseTopicCommand,
55
+		USER:    ParseUserCommand,
56
+		VERSION: ParseVersionCommand,
57
+		WHO:     ParseWhoCommand,
58
+		WHOIS:   ParseWhoisCommand,
59
+		WHOWAS:  ParseWhoWasCommand,
60
 	}
60
 	}
61
 )
61
 )
62
 
62
 
85
 	code, args := ParseLine(line)
85
 	code, args := ParseLine(line)
86
 	constructor := parseCommandFuncs[code]
86
 	constructor := parseCommandFuncs[code]
87
 	if constructor == nil {
87
 	if constructor == nil {
88
-		cmd = NewUnknownCommand(args)
88
+		cmd = ParseUnknownCommand(args)
89
 	} else {
89
 	} else {
90
 		cmd, err = constructor(args)
90
 		cmd, err = constructor(args)
91
 	}
91
 	}
135
 	args []string
135
 	args []string
136
 }
136
 }
137
 
137
 
138
-func (cmd *UnknownCommand) String() string {
139
-	return fmt.Sprintf("UNKNOWN(command=%s, args=%s)", cmd.Code(), cmd.args)
140
-}
141
-
142
-func NewUnknownCommand(args []string) *UnknownCommand {
138
+func ParseUnknownCommand(args []string) *UnknownCommand {
143
 	return &UnknownCommand{
139
 	return &UnknownCommand{
144
 		args: args,
140
 		args: args,
145
 	}
141
 	}
153
 	server2 Name
149
 	server2 Name
154
 }
150
 }
155
 
151
 
156
-func (cmd *PingCommand) String() string {
157
-	return fmt.Sprintf("PING(server=%s, server2=%s)", cmd.server, cmd.server2)
158
-}
159
-
160
-func NewPingCommand(args []string) (Command, error) {
152
+func ParsePingCommand(args []string) (Command, error) {
161
 	if len(args) < 1 {
153
 	if len(args) < 1 {
162
 		return nil, NotEnoughArgsError
154
 		return nil, NotEnoughArgsError
163
 	}
155
 	}
178
 	server2 Name
170
 	server2 Name
179
 }
171
 }
180
 
172
 
181
-func (cmd *PongCommand) String() string {
182
-	return fmt.Sprintf("PONG(server1=%s, server2=%s)", cmd.server1, cmd.server2)
183
-}
184
-
185
-func NewPongCommand(args []string) (Command, error) {
173
+func ParsePongCommand(args []string) (Command, error) {
186
 	if len(args) < 1 {
174
 	if len(args) < 1 {
187
 		return nil, NotEnoughArgsError
175
 		return nil, NotEnoughArgsError
188
 	}
176
 	}
204
 	err      error
192
 	err      error
205
 }
193
 }
206
 
194
 
207
-func (cmd *PassCommand) String() string {
208
-	return fmt.Sprintf("PASS(password=%s)", cmd.password)
209
-}
210
-
211
 func (cmd *PassCommand) LoadPassword(server *Server) {
195
 func (cmd *PassCommand) LoadPassword(server *Server) {
212
 	cmd.hash = server.password
196
 	cmd.hash = server.password
213
 }
197
 }
219
 	cmd.err = ComparePassword(cmd.hash, cmd.password)
203
 	cmd.err = ComparePassword(cmd.hash, cmd.password)
220
 }
204
 }
221
 
205
 
222
-func NewPassCommand(args []string) (Command, error) {
206
+func ParsePassCommand(args []string) (Command, error) {
223
 	if len(args) < 1 {
207
 	if len(args) < 1 {
224
 		return nil, NotEnoughArgsError
208
 		return nil, NotEnoughArgsError
225
 	}
209
 	}
230
 
214
 
231
 // NICK <nickname>
215
 // NICK <nickname>
232
 
216
 
233
-func NewNickCommand(args []string) (Command, error) {
217
+func ParseNickCommand(args []string) (Command, error) {
234
 	if len(args) != 1 {
218
 	if len(args) != 1 {
235
 		return nil, NotEnoughArgsError
219
 		return nil, NotEnoughArgsError
236
 	}
220
 	}
252
 	servername Name
236
 	servername Name
253
 }
237
 }
254
 
238
 
255
-func (cmd *RFC1459UserCommand) String() string {
256
-	return fmt.Sprintf("USER(username=%s, hostname=%s, servername=%s, realname=%s)",
257
-		cmd.username, cmd.hostname, cmd.servername, cmd.realname)
258
-}
259
-
260
 // USER <user> <mode> <unused> <realname>
239
 // USER <user> <mode> <unused> <realname>
261
 type RFC2812UserCommand struct {
240
 type RFC2812UserCommand struct {
262
 	UserCommand
241
 	UserCommand
264
 	unused string
243
 	unused string
265
 }
244
 }
266
 
245
 
267
-func (cmd *RFC2812UserCommand) String() string {
268
-	return fmt.Sprintf("USER(username=%s, mode=%d, unused=%s, realname=%s)",
269
-		cmd.username, cmd.mode, cmd.unused, cmd.realname)
270
-}
271
-
272
 func (cmd *RFC2812UserCommand) Flags() []UserMode {
246
 func (cmd *RFC2812UserCommand) Flags() []UserMode {
273
 	flags := make([]UserMode, 0)
247
 	flags := make([]UserMode, 0)
274
 	if (cmd.mode & 4) == 4 {
248
 	if (cmd.mode & 4) == 4 {
280
 	return flags
254
 	return flags
281
 }
255
 }
282
 
256
 
283
-func NewUserCommand(args []string) (Command, error) {
257
+func ParseUserCommand(args []string) (Command, error) {
284
 	if len(args) != 4 {
258
 	if len(args) != 4 {
285
 		return nil, NotEnoughArgsError
259
 		return nil, NotEnoughArgsError
286
 	}
260
 	}
311
 	message Text
285
 	message Text
312
 }
286
 }
313
 
287
 
314
-func (cmd *QuitCommand) String() string {
315
-	return fmt.Sprintf("QUIT(message=%s)", cmd.message)
288
+func NewQuitCommand(message Text) *QuitCommand {
289
+	cmd := &QuitCommand{
290
+		message: message,
291
+	}
292
+	cmd.code = QUIT
293
+	return cmd
316
 }
294
 }
317
 
295
 
318
-func NewQuitCommand(args []string) (Command, error) {
296
+func ParseQuitCommand(args []string) (Command, error) {
319
 	msg := &QuitCommand{}
297
 	msg := &QuitCommand{}
320
 	if len(args) > 0 {
298
 	if len(args) > 0 {
321
 		msg.message = NewText(args[0])
299
 		msg.message = NewText(args[0])
331
 	zero     bool
309
 	zero     bool
332
 }
310
 }
333
 
311
 
334
-func (cmd *JoinCommand) String() string {
335
-	return fmt.Sprintf("JOIN(channels=%s, zero=%t)", cmd.channels, cmd.zero)
336
-}
337
-
338
-func NewJoinCommand(args []string) (Command, error) {
312
+func ParseJoinCommand(args []string) (Command, error) {
339
 	msg := &JoinCommand{
313
 	msg := &JoinCommand{
340
 		channels: make(map[Name]Text),
314
 		channels: make(map[Name]Text),
341
 	}
315
 	}
378
 	return cmd.message
352
 	return cmd.message
379
 }
353
 }
380
 
354
 
381
-func (cmd *PartCommand) String() string {
382
-	return fmt.Sprintf("PART(channels=%s, message=%s)", cmd.channels, cmd.message)
383
-}
384
-
385
-func NewPartCommand(args []string) (Command, error) {
355
+func ParsePartCommand(args []string) (Command, error) {
386
 	if len(args) < 1 {
356
 	if len(args) < 1 {
387
 		return nil, NotEnoughArgsError
357
 		return nil, NotEnoughArgsError
388
 	}
358
 	}
403
 	message Text
373
 	message Text
404
 }
374
 }
405
 
375
 
406
-func (cmd *PrivMsgCommand) String() string {
407
-	return fmt.Sprintf("PRIVMSG(target=%s, message=%s)", cmd.target, cmd.message)
408
-}
409
-
410
-func NewPrivMsgCommand(args []string) (Command, error) {
376
+func ParsePrivMsgCommand(args []string) (Command, error) {
411
 	if len(args) < 2 {
377
 	if len(args) < 2 {
412
 		return nil, NotEnoughArgsError
378
 		return nil, NotEnoughArgsError
413
 	}
379
 	}
426
 	topic    Text
392
 	topic    Text
427
 }
393
 }
428
 
394
 
429
-func (cmd *TopicCommand) String() string {
430
-	return fmt.Sprintf("TOPIC(channel=%s, topic=%s)", cmd.channel, cmd.topic)
431
-}
432
-
433
-func NewTopicCommand(args []string) (Command, error) {
395
+func ParseTopicCommand(args []string) (Command, error) {
434
 	if len(args) < 1 {
396
 	if len(args) < 1 {
435
 		return nil, NotEnoughArgsError
397
 		return nil, NotEnoughArgsError
436
 	}
398
 	}
480
 }
442
 }
481
 
443
 
482
 // MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
444
 // MODE <nickname> *( ( "+" / "-" ) *( "i" / "w" / "o" / "O" / "r" ) )
483
-func NewUserModeCommand(nickname Name, args []string) (Command, error) {
445
+func ParseUserModeCommand(nickname Name, args []string) (Command, error) {
484
 	cmd := &ModeCommand{
446
 	cmd := &ModeCommand{
485
 		nickname: nickname,
447
 		nickname: nickname,
486
 		changes:  make(ModeChanges, 0),
448
 		changes:  make(ModeChanges, 0),
506
 	return cmd, nil
468
 	return cmd, nil
507
 }
469
 }
508
 
470
 
509
-func (cmd *ModeCommand) String() string {
510
-	return fmt.Sprintf("MODE(nickname=%s, changes=%s)", cmd.nickname, cmd.changes)
511
-}
512
-
513
 type ChannelModeChange struct {
471
 type ChannelModeChange struct {
514
 	mode ChannelMode
472
 	mode ChannelMode
515
 	op   ModeOp
473
 	op   ModeOp
557
 }
515
 }
558
 
516
 
559
 // MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
517
 // MODE <channel> *( ( "-" / "+" ) *<modes> *<modeparams> )
560
-func NewChannelModeCommand(channel Name, args []string) (Command, error) {
518
+func ParseChannelModeCommand(channel Name, args []string) (Command, error) {
561
 	cmd := &ChannelModeCommand{
519
 	cmd := &ChannelModeCommand{
562
 		channel: channel,
520
 		channel: channel,
563
 		changes: make(ChannelModeChanges, 0),
521
 		changes: make(ChannelModeChanges, 0),
599
 	return cmd, nil
557
 	return cmd, nil
600
 }
558
 }
601
 
559
 
602
-func (msg *ChannelModeCommand) String() string {
603
-	return fmt.Sprintf("MODE(channel=%s, changes=%s)", msg.channel, msg.changes)
604
-}
605
-
606
-func NewModeCommand(args []string) (Command, error) {
560
+func ParseModeCommand(args []string) (Command, error) {
607
 	if len(args) == 0 {
561
 	if len(args) == 0 {
608
 		return nil, NotEnoughArgsError
562
 		return nil, NotEnoughArgsError
609
 	}
563
 	}
610
 
564
 
611
 	name := NewName(args[0])
565
 	name := NewName(args[0])
612
 	if name.IsChannel() {
566
 	if name.IsChannel() {
613
-		return NewChannelModeCommand(name, args[1:])
567
+		return ParseChannelModeCommand(name, args[1:])
614
 	} else {
568
 	} else {
615
-		return NewUserModeCommand(name, args[1:])
569
+		return ParseUserModeCommand(name, args[1:])
616
 	}
570
 	}
617
 }
571
 }
618
 
572
 
623
 }
577
 }
624
 
578
 
625
 // WHOIS [ <target> ] <mask> *( "," <mask> )
579
 // WHOIS [ <target> ] <mask> *( "," <mask> )
626
-func NewWhoisCommand(args []string) (Command, error) {
580
+func ParseWhoisCommand(args []string) (Command, error) {
627
 	if len(args) < 1 {
581
 	if len(args) < 1 {
628
 		return nil, NotEnoughArgsError
582
 		return nil, NotEnoughArgsError
629
 	}
583
 	}
644
 	}, nil
598
 	}, nil
645
 }
599
 }
646
 
600
 
647
-func (msg *WhoisCommand) String() string {
648
-	return fmt.Sprintf("WHOIS(target=%s, masks=%s)", msg.target, msg.masks)
649
-}
650
-
651
 type WhoCommand struct {
601
 type WhoCommand struct {
652
 	BaseCommand
602
 	BaseCommand
653
 	mask         Name
603
 	mask         Name
655
 }
605
 }
656
 
606
 
657
 // WHO [ <mask> [ "o" ] ]
607
 // WHO [ <mask> [ "o" ] ]
658
-func NewWhoCommand(args []string) (Command, error) {
608
+func ParseWhoCommand(args []string) (Command, error) {
659
 	cmd := &WhoCommand{}
609
 	cmd := &WhoCommand{}
660
 
610
 
661
 	if len(args) > 0 {
611
 	if len(args) > 0 {
669
 	return cmd, nil
619
 	return cmd, nil
670
 }
620
 }
671
 
621
 
672
-func (msg *WhoCommand) String() string {
673
-	return fmt.Sprintf("WHO(mask=%s, operatorOnly=%t)", msg.mask, msg.operatorOnly)
674
-}
675
-
676
 type OperCommand struct {
622
 type OperCommand struct {
677
 	PassCommand
623
 	PassCommand
678
 	name Name
624
 	name Name
679
 }
625
 }
680
 
626
 
681
-func (msg *OperCommand) String() string {
682
-	return fmt.Sprintf("OPER(name=%s, password=%s)", msg.name, msg.password)
683
-}
684
-
685
 func (msg *OperCommand) LoadPassword(server *Server) {
627
 func (msg *OperCommand) LoadPassword(server *Server) {
686
 	msg.hash = server.operators[msg.name]
628
 	msg.hash = server.operators[msg.name]
687
 }
629
 }
688
 
630
 
689
 // OPER <name> <password>
631
 // OPER <name> <password>
690
-func NewOperCommand(args []string) (Command, error) {
632
+func ParseOperCommand(args []string) (Command, error) {
691
 	if len(args) < 2 {
633
 	if len(args) < 2 {
692
 		return nil, NotEnoughArgsError
634
 		return nil, NotEnoughArgsError
693
 	}
635
 	}
705
 	capabilities CapabilitySet
647
 	capabilities CapabilitySet
706
 }
648
 }
707
 
649
 
708
-func (msg *CapCommand) String() string {
709
-	return fmt.Sprintf("CAP(subCommand=%s, capabilities=%s)",
710
-		msg.subCommand, msg.capabilities)
711
-}
712
-
713
-func NewCapCommand(args []string) (Command, error) {
650
+func ParseCapCommand(args []string) (Command, error) {
714
 	if len(args) < 1 {
651
 	if len(args) < 1 {
715
 		return nil, NotEnoughArgsError
652
 		return nil, NotEnoughArgsError
716
 	}
653
 	}
740
 	hostname   Name // looked up in socket thread
677
 	hostname   Name // looked up in socket thread
741
 }
678
 }
742
 
679
 
743
-func (msg *ProxyCommand) String() string {
744
-	return fmt.Sprintf("PROXY(sourceIP=%s, sourcePort=%s)", msg.sourceIP, msg.sourcePort)
680
+func NewProxyCommand(hostname Name) *ProxyCommand {
681
+	cmd := &ProxyCommand{
682
+		hostname: hostname,
683
+	}
684
+	cmd.code = PROXY
685
+	return cmd
745
 }
686
 }
746
 
687
 
747
-func NewProxyCommand(args []string) (Command, error) {
688
+func ParseProxyCommand(args []string) (Command, error) {
748
 	if len(args) < 5 {
689
 	if len(args) < 5 {
749
 		return nil, NotEnoughArgsError
690
 		return nil, NotEnoughArgsError
750
 	}
691
 	}
764
 	away bool
705
 	away bool
765
 }
706
 }
766
 
707
 
767
-func (msg *AwayCommand) String() string {
768
-	return fmt.Sprintf("AWAY(%s)", msg.text)
769
-}
770
-
771
-func NewAwayCommand(args []string) (Command, error) {
708
+func ParseAwayCommand(args []string) (Command, error) {
772
 	cmd := &AwayCommand{}
709
 	cmd := &AwayCommand{}
773
 
710
 
774
 	if len(args) > 0 {
711
 	if len(args) > 0 {
784
 	nicks []Name
721
 	nicks []Name
785
 }
722
 }
786
 
723
 
787
-func (msg *IsOnCommand) String() string {
788
-	return fmt.Sprintf("ISON(nicks=%s)", msg.nicks)
789
-}
790
-
791
-func NewIsOnCommand(args []string) (Command, error) {
724
+func ParseIsOnCommand(args []string) (Command, error) {
792
 	if len(args) == 0 {
725
 	if len(args) == 0 {
793
 		return nil, NotEnoughArgsError
726
 		return nil, NotEnoughArgsError
794
 	}
727
 	}
803
 	target Name
736
 	target Name
804
 }
737
 }
805
 
738
 
806
-func NewMOTDCommand(args []string) (Command, error) {
739
+func ParseMOTDCommand(args []string) (Command, error) {
807
 	cmd := &MOTDCommand{}
740
 	cmd := &MOTDCommand{}
808
 	if len(args) > 0 {
741
 	if len(args) > 0 {
809
 		cmd.target = NewName(args[0])
742
 		cmd.target = NewName(args[0])
817
 	message Text
750
 	message Text
818
 }
751
 }
819
 
752
 
820
-func (cmd *NoticeCommand) String() string {
821
-	return fmt.Sprintf("NOTICE(target=%s, message=%s)", cmd.target, cmd.message)
822
-}
823
-
824
-func NewNoticeCommand(args []string) (Command, error) {
753
+func ParseNoticeCommand(args []string) (Command, error) {
825
 	if len(args) < 2 {
754
 	if len(args) < 2 {
826
 		return nil, NotEnoughArgsError
755
 		return nil, NotEnoughArgsError
827
 	}
756
 	}
844
 	return msg.comment
773
 	return msg.comment
845
 }
774
 }
846
 
775
 
847
-func NewKickCommand(args []string) (Command, error) {
776
+func ParseKickCommand(args []string) (Command, error) {
848
 	if len(args) < 2 {
777
 	if len(args) < 2 {
849
 		return nil, NotEnoughArgsError
778
 		return nil, NotEnoughArgsError
850
 	}
779
 	}
875
 	target   Name
804
 	target   Name
876
 }
805
 }
877
 
806
 
878
-func NewListCommand(args []string) (Command, error) {
807
+func ParseListCommand(args []string) (Command, error) {
879
 	cmd := &ListCommand{}
808
 	cmd := &ListCommand{}
880
 	if len(args) > 0 {
809
 	if len(args) > 0 {
881
 		cmd.channels = NewNames(strings.Split(args[0], ","))
810
 		cmd.channels = NewNames(strings.Split(args[0], ","))
892
 	target   Name
821
 	target   Name
893
 }
822
 }
894
 
823
 
895
-func NewNamesCommand(args []string) (Command, error) {
824
+func ParseNamesCommand(args []string) (Command, error) {
896
 	cmd := &NamesCommand{}
825
 	cmd := &NamesCommand{}
897
 	if len(args) > 0 {
826
 	if len(args) > 0 {
898
 		cmd.channels = NewNames(strings.Split(args[0], ","))
827
 		cmd.channels = NewNames(strings.Split(args[0], ","))
908
 	subCommand Name
837
 	subCommand Name
909
 }
838
 }
910
 
839
 
911
-func NewDebugCommand(args []string) (Command, error) {
840
+func ParseDebugCommand(args []string) (Command, error) {
912
 	if len(args) == 0 {
841
 	if len(args) == 0 {
913
 		return nil, NotEnoughArgsError
842
 		return nil, NotEnoughArgsError
914
 	}
843
 	}
923
 	target Name
852
 	target Name
924
 }
853
 }
925
 
854
 
926
-func NewVersionCommand(args []string) (Command, error) {
855
+func ParseVersionCommand(args []string) (Command, error) {
927
 	cmd := &VersionCommand{}
856
 	cmd := &VersionCommand{}
928
 	if len(args) > 0 {
857
 	if len(args) > 0 {
929
 		cmd.target = NewName(args[0])
858
 		cmd.target = NewName(args[0])
937
 	channel  Name
866
 	channel  Name
938
 }
867
 }
939
 
868
 
940
-func NewInviteCommand(args []string) (Command, error) {
869
+func ParseInviteCommand(args []string) (Command, error) {
941
 	if len(args) < 2 {
870
 	if len(args) < 2 {
942
 		return nil, NotEnoughArgsError
871
 		return nil, NotEnoughArgsError
943
 	}
872
 	}
948
 	}, nil
877
 	}, nil
949
 }
878
 }
950
 
879
 
951
-func NewTheaterCommand(args []string) (Command, error) {
880
+func ParseTheaterCommand(args []string) (Command, error) {
952
 	if len(args) < 1 {
881
 	if len(args) < 1 {
953
 		return nil, NotEnoughArgsError
882
 		return nil, NotEnoughArgsError
954
 	} else if upperSubCmd := strings.ToUpper(args[0]); upperSubCmd == "IDENTIFY" && len(args) == 3 {
883
 	} else if upperSubCmd := strings.ToUpper(args[0]); upperSubCmd == "IDENTIFY" && len(args) == 3 {
978
 	target Name
907
 	target Name
979
 }
908
 }
980
 
909
 
981
-func NewTimeCommand(args []string) (Command, error) {
910
+func ParseTimeCommand(args []string) (Command, error) {
982
 	cmd := &TimeCommand{}
911
 	cmd := &TimeCommand{}
983
 	if len(args) > 0 {
912
 	if len(args) > 0 {
984
 		cmd.target = NewName(args[0])
913
 		cmd.target = NewName(args[0])
992
 	comment  Text
921
 	comment  Text
993
 }
922
 }
994
 
923
 
995
-func NewKillCommand(args []string) (Command, error) {
924
+func ParseKillCommand(args []string) (Command, error) {
996
 	if len(args) < 2 {
925
 	if len(args) < 2 {
997
 		return nil, NotEnoughArgsError
926
 		return nil, NotEnoughArgsError
998
 	}
927
 	}
1009
 	target    Name
938
 	target    Name
1010
 }
939
 }
1011
 
940
 
1012
-func NewWhoWasCommand(args []string) (Command, error) {
941
+func ParseWhoWasCommand(args []string) (Command, error) {
1013
 	if len(args) < 1 {
942
 	if len(args) < 1 {
1014
 		return nil, NotEnoughArgsError
943
 		return nil, NotEnoughArgsError
1015
 	}
944
 	}
1025
 	return cmd, nil
954
 	return cmd, nil
1026
 }
955
 }
1027
 
956
 
1028
-func NewOperNickCommand(args []string) (Command, error) {
957
+func ParseOperNickCommand(args []string) (Command, error) {
1029
 	if len(args) < 2 {
958
 	if len(args) < 2 {
1030
 		return nil, NotEnoughArgsError
959
 		return nil, NotEnoughArgsError
1031
 	}
960
 	}

+ 9
- 8
irc/nickname.go 파일 보기

1
 package irc
1
 package irc
2
 
2
 
3
-import (
4
-	"fmt"
5
-)
6
-
7
 type NickCommand struct {
3
 type NickCommand struct {
8
 	BaseCommand
4
 	BaseCommand
9
 	nickname Name
5
 	nickname Name
10
 }
6
 }
11
 
7
 
12
-func (m *NickCommand) String() string {
13
-	return fmt.Sprintf("NICK(nickname=%s)", m.nickname)
14
-}
15
-
16
 func (m *NickCommand) HandleRegServer(s *Server) {
8
 func (m *NickCommand) HandleRegServer(s *Server) {
17
 	client := m.Client()
9
 	client := m.Client()
18
 	if !client.authorized {
10
 	if !client.authorized {
89
 		return
81
 		return
90
 	}
82
 	}
91
 
83
 
84
+	if msg.nick == client.nick {
85
+		return
86
+	}
87
+
92
 	target := server.clients.Get(msg.target)
88
 	target := server.clients.Get(msg.target)
93
 	if target == nil {
89
 	if target == nil {
94
 		client.ErrNoSuchNick(msg.target)
90
 		client.ErrNoSuchNick(msg.target)
95
 		return
91
 		return
96
 	}
92
 	}
97
 
93
 
94
+	if server.clients.Get(msg.nick) != nil {
95
+		client.ErrNickNameInUse(msg.nick)
96
+		return
97
+	}
98
+
98
 	target.ChangeNickname(msg.nick)
99
 	target.ChangeNickname(msg.nick)
99
 }
100
 }

+ 1
- 1
irc/reply.go 파일 보기

81
 	for to < len(names) {
81
 	for to < len(names) {
82
 		if (from < (to - 1)) && tooLong(names[from:to]) {
82
 		if (from < (to - 1)) && tooLong(names[from:to]) {
83
 			target.NumericReply(code, format, argsAndNames(names[from:to-1])...)
83
 			target.NumericReply(code, format, argsAndNames(names[from:to-1])...)
84
-			from, to = to-1, to
84
+			from = to - 1
85
 		} else {
85
 		} else {
86
 			to += 1
86
 			to += 1
87
 		}
87
 		}

+ 7
- 4
irc/server.go 파일 보기

121
 
121
 
122
 func (server *Server) processCommand(cmd Command) {
122
 func (server *Server) processCommand(cmd Command) {
123
 	client := cmd.Client()
123
 	client := cmd.Client()
124
-	Log.debug.Printf("%s → %s %s", client, server, cmd)
124
+	Log.debug.Printf("%s → %+v", client, cmd)
125
 
125
 
126
 	if !client.registered {
126
 	if !client.registered {
127
 		regCmd, ok := cmd.(RegServerCommand)
127
 		regCmd, ok := cmd.(RegServerCommand)
654
 		server.Reply(client, "OK")
654
 		server.Reply(client, "OK")
655
 
655
 
656
 	case "GCSTATS":
656
 	case "GCSTATS":
657
-		stats := &debug.GCStats{
657
+		stats := debug.GCStats{
658
+			Pause:          make([]time.Duration, 10),
658
 			PauseQuantiles: make([]time.Duration, 5),
659
 			PauseQuantiles: make([]time.Duration, 5),
659
 		}
660
 		}
661
+		debug.ReadGCStats(&stats)
660
 		server.Reply(client, "last GC:     %s", stats.LastGC.Format(time.RFC1123))
662
 		server.Reply(client, "last GC:     %s", stats.LastGC.Format(time.RFC1123))
661
 		server.Reply(client, "num GC:      %d", stats.NumGC)
663
 		server.Reply(client, "num GC:      %d", stats.NumGC)
662
 		server.Reply(client, "pause total: %s", stats.PauseTotal)
664
 		server.Reply(client, "pause total: %s", stats.PauseTotal)
671
 		server.Reply(client, "num goroutines: %d", count)
673
 		server.Reply(client, "num goroutines: %d", count)
672
 
674
 
673
 	case "PROFILEHEAP":
675
 	case "PROFILEHEAP":
674
-		file, err := os.Create("ergonomadic.heap.prof")
676
+		profFile := "ergonomadic-heap.prof"
677
+		file, err := os.Create(profFile)
675
 		if err != nil {
678
 		if err != nil {
676
 			log.Printf("error: %s", err)
679
 			log.Printf("error: %s", err)
677
 			break
680
 			break
678
 		}
681
 		}
679
 		defer file.Close()
682
 		defer file.Close()
680
 		pprof.Lookup("heap").WriteTo(file, 0)
683
 		pprof.Lookup("heap").WriteTo(file, 0)
681
-		server.Reply(client, "written to ergonomadic-heap.prof")
684
+		server.Reply(client, "written to %s", profFile)
682
 	}
685
 	}
683
 }
686
 }
684
 
687
 

+ 10
- 14
irc/socket.go 파일 보기

4
 	"bufio"
4
 	"bufio"
5
 	"io"
5
 	"io"
6
 	"net"
6
 	"net"
7
-	"strings"
8
 )
7
 )
9
 
8
 
10
 const (
9
 const (
15
 
14
 
16
 type Socket struct {
15
 type Socket struct {
17
 	conn   net.Conn
16
 	conn   net.Conn
18
-	reader *bufio.Reader
19
 	writer *bufio.Writer
17
 	writer *bufio.Writer
20
 }
18
 }
21
 
19
 
22
 func NewSocket(conn net.Conn, commands chan<- Command) *Socket {
20
 func NewSocket(conn net.Conn, commands chan<- Command) *Socket {
23
 	socket := &Socket{
21
 	socket := &Socket{
24
 		conn:   conn,
22
 		conn:   conn,
25
-		reader: bufio.NewReader(conn),
26
 		writer: bufio.NewWriter(conn),
23
 		writer: bufio.NewWriter(conn),
27
 	}
24
 	}
28
 
25
 
41
 }
38
 }
42
 
39
 
43
 func (socket *Socket) readLines(commands chan<- Command) {
40
 func (socket *Socket) readLines(commands chan<- Command) {
44
-	commands <- &ProxyCommand{
45
-		hostname: AddrLookupHostname(socket.conn.RemoteAddr()),
46
-	}
41
+	commands <- NewProxyCommand(AddrLookupHostname(socket.conn.RemoteAddr()))
47
 
42
 
48
-	for {
49
-		line, err := socket.reader.ReadString('\n')
50
-		if socket.isError(err, R) {
51
-			break
52
-		}
53
-		line = strings.TrimRight(line, CRLF)
43
+	scanner := bufio.NewScanner(socket.conn)
44
+	for scanner.Scan() {
45
+		line := scanner.Text()
54
 		if len(line) == 0 {
46
 		if len(line) == 0 {
55
 			continue
47
 			continue
56
 		}
48
 		}
64
 		commands <- msg
56
 		commands <- msg
65
 	}
57
 	}
66
 
58
 
67
-	commands <- &QuitCommand{
68
-		message: "connection closed",
59
+	if err := scanner.Err(); err != nil {
60
+		Log.debug.Printf("%s error: %s", socket, err)
69
 	}
61
 	}
62
+
63
+	commands <- NewQuitCommand("connection closed")
64
+
65
+	close(commands)
70
 }
66
 }
71
 
67
 
72
 func (socket *Socket) Write(line string) (err error) {
68
 func (socket *Socket) Write(line string) (err error) {

+ 4
- 18
irc/theater.go 파일 보기

1
 package irc
1
 package irc
2
 
2
 
3
-import (
4
-	"fmt"
5
-)
6
-
7
 type TheaterClient Name
3
 type TheaterClient Name
8
 
4
 
9
 func (c TheaterClient) Id() Name {
5
 func (c TheaterClient) Id() Name {
29
 	m.hash = s.theaters[m.channel]
25
 	m.hash = s.theaters[m.channel]
30
 }
26
 }
31
 
27
 
32
-func (cmd *TheaterIdentifyCommand) String() string {
33
-	return fmt.Sprintf("THEATER_IDENTIFY(channel=%s)", cmd.channel)
34
-}
35
-
36
 func (m *TheaterIdentifyCommand) HandleServer(s *Server) {
28
 func (m *TheaterIdentifyCommand) HandleServer(s *Server) {
37
 	client := m.Client()
29
 	client := m.Client()
38
 	if !m.channel.IsChannel() {
30
 	if !m.channel.IsChannel() {
66
 	message Text
58
 	message Text
67
 }
59
 }
68
 
60
 
69
-func (cmd *TheaterPrivMsgCommand) String() string {
70
-	return fmt.Sprintf("THEATER_PRIVMSG(channel=%s, asNick=%s, message=%s)", cmd.channel, cmd.asNick, cmd.message)
71
-
72
-}
73
 func (m *TheaterPrivMsgCommand) HandleServer(s *Server) {
61
 func (m *TheaterPrivMsgCommand) HandleServer(s *Server) {
74
 	client := m.Client()
62
 	client := m.Client()
75
 
63
 
89
 		return
77
 		return
90
 	}
78
 	}
91
 
79
 
80
+	reply := RplPrivMsg(TheaterClient(m.asNick), channel, m.message)
92
 	for member := range channel.members {
81
 	for member := range channel.members {
93
-		member.Reply(RplPrivMsg(TheaterClient(m.asNick), channel, m.message))
82
+		member.Reply(reply)
94
 	}
83
 	}
95
 }
84
 }
96
 
85
 
101
 	action  CTCPText
90
 	action  CTCPText
102
 }
91
 }
103
 
92
 
104
-func (cmd *TheaterActionCommand) String() string {
105
-	return fmt.Sprintf("THEATER_ACTION(channel=%s, asNick=%s, action=%s)", cmd.channel, cmd.asNick, cmd.action)
106
-}
107
-
108
 func (m *TheaterActionCommand) HandleServer(s *Server) {
93
 func (m *TheaterActionCommand) HandleServer(s *Server) {
109
 	client := m.Client()
94
 	client := m.Client()
110
 
95
 
124
 		return
109
 		return
125
 	}
110
 	}
126
 
111
 
112
+	reply := RplCTCPAction(TheaterClient(m.asNick), channel, m.action)
127
 	for member := range channel.members {
113
 	for member := range channel.members {
128
-		member.Reply(RplCTCPAction(TheaterClient(m.asNick), channel, m.action))
114
+		member.Reply(reply)
129
 	}
115
 	}
130
 }
116
 }

Loading…
취소
저장