Daniel Oaks 8 лет назад
Родитель
Сommit
9e7a590f23
8 измененных файлов: 305 добавлений и 285 удалений
  1. 1
    1
      irc/channel.go
  2. 1
    1
      irc/client.go
  3. 20
    11
      irc/commands.go
  4. 14
    9
      irc/isupport.go
  5. 4
    0
      irc/modes.go
  6. 1
    1
      irc/reply.go
  7. 263
    261
      irc/server.go
  8. 1
    1
      irc/whowas.go

+ 1
- 1
irc/channel.go Просмотреть файл

@@ -338,7 +338,7 @@ func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
338 338
 	if target == nil {
339 339
 		//TODO(dan): investigate using NOSUCHNICK and NOSUCHCHANNEL specifically as that other IRCd (insp?) does,
340 340
 		// since I think that would make sense
341
-		client.Send(nil, client.server.nameString, ERR_NOSUCHNICK, nick, "No such nick/channel")
341
+		client.Send(nil, client.server.nameString, ERR_NOSUCHNICK, nick, "No such nick")
342 342
 		return false
343 343
 	}
344 344
 

+ 1
- 1
irc/client.go Просмотреть файл

@@ -35,7 +35,7 @@ type Client struct {
35 35
 	nickString     string // cache for nick string since it's used with most numerics
36 36
 	nickMaskString string // cache for nickmask string since it's used with lots of replies
37 37
 	quitTimer      *time.Timer
38
-	realname       Text
38
+	realname       string
39 39
 	registered     bool
40 40
 	server         *Server
41 41
 	socket         *Socket

+ 20
- 11
irc/commands.go Просмотреть файл

@@ -9,9 +9,11 @@ import "github.com/DanielOaks/girc-go/ircmsg"
9 9
 
10 10
 // Command represents a command accepted from a client.
11 11
 type Command struct {
12
-	handler      func(server *Server, client *Client, msg ircmsg.IrcMessage) bool
13
-	usablePreReg bool
14
-	minParams    int
12
+	handler           func(server *Server, client *Client, msg ircmsg.IrcMessage) bool
13
+	usablePreReg      bool
14
+	leaveClientActive bool // if true, leaves the client active time alone. reversed because we can't default a struct element to True
15
+	leaveClientIdle   bool
16
+	minParams         int
15 17
 }
16 18
 
17 19
 // Run runs this command with the given client/message.
@@ -24,6 +26,12 @@ func (cmd *Command) Run(server *Server, client *Client, msg ircmsg.IrcMessage) b
24 26
 		client.Send(nil, server.nameString, ERR_NEEDMOREPARAMS, client.nickString, msg.Command, "Not enough parameters")
25 27
 		return false
26 28
 	}
29
+	if !cmd.leaveClientActive {
30
+		client.Active()
31
+	}
32
+	if !cmd.leaveClientIdle {
33
+		client.Touch()
34
+	}
27 35
 	exiting := cmd.handler(server, client, msg)
28 36
 
29 37
 	// after each command, see if we can send registration to the client
@@ -95,11 +103,10 @@ var Commands = map[string]Command{
95 103
 		handler:   noticeHandler,
96 104
 		minParams: 2,
97 105
 	},
98
-	/*TODO(dan): ADD THIS BACK
99 106
 	"ONICK": Command{
100 107
 		handler:   onickHandler,
101 108
 		minParams: 2,
102
-	},*/
109
+	},
103 110
 	"OPER": Command{
104 111
 		handler:   operHandler,
105 112
 		minParams: 2,
@@ -114,14 +121,16 @@ var Commands = map[string]Command{
114 121
 		minParams:    1,
115 122
 	},
116 123
 	"PING": Command{
117
-		handler:      pingHandler,
118
-		usablePreReg: true,
119
-		minParams:    1,
124
+		handler:           pingHandler,
125
+		usablePreReg:      true,
126
+		minParams:         1,
127
+		leaveClientActive: true,
120 128
 	},
121 129
 	"PONG": Command{
122
-		handler:      pongHandler,
123
-		usablePreReg: true,
124
-		minParams:    1,
130
+		handler:           pongHandler,
131
+		usablePreReg:      true,
132
+		minParams:         1,
133
+		leaveClientActive: true,
125 134
 	},
126 135
 	"PRIVMSG": Command{
127 136
 		handler:   privmsgHandler,

+ 14
- 9
irc/isupport.go Просмотреть файл

@@ -3,22 +3,19 @@
3 3
 
4 4
 package irc
5 5
 
6
-import (
7
-	"fmt"
8
-	"strings"
9
-)
6
+import "fmt"
10 7
 
11 8
 // ISupportList holds a list of ISUPPORT tokens
12 9
 type ISupportList struct {
13 10
 	Tokens      map[string]*string
14
-	CachedReply []string
11
+	CachedReply [][]string
15 12
 }
16 13
 
17 14
 // NewISupportList returns a new ISupportList
18 15
 func NewISupportList() *ISupportList {
19 16
 	var il ISupportList
20 17
 	il.Tokens = make(map[string]*string)
21
-	il.CachedReply = make([]string, 0)
18
+	il.CachedReply = make([][]string, 0)
22 19
 	return &il
23 20
 }
24 21
 
@@ -34,7 +31,7 @@ func (il *ISupportList) AddNoValue(name string) {
34 31
 
35 32
 // RegenerateCachedReply regenerates the cached RPL_ISUPPORT reply
36 33
 func (il *ISupportList) RegenerateCachedReply() {
37
-	il.CachedReply = make([]string, 0)
34
+	il.CachedReply = make([][]string, 0)
38 35
 	maxlen := 400      // Max length of a single ISUPPORT token line
39 36
 	var length int     // Length of the current cache
40 37
 	var cache []string // Token list cache
@@ -57,13 +54,21 @@ func (il *ISupportList) RegenerateCachedReply() {
57 54
 		}
58 55
 
59 56
 		if len(cache) == 13 || len(token)+length >= maxlen {
60
-			il.CachedReply = append(il.CachedReply, strings.Join(cache, " "))
57
+			cache = append(cache, "are supported by this server")
58
+			il.CachedReply = append(il.CachedReply, cache)
61 59
 			cache = make([]string, 0)
62 60
 			length = 0
63 61
 		}
64 62
 	}
65 63
 
66 64
 	if len(cache) > 0 {
67
-		il.CachedReply = append(il.CachedReply, strings.Join(cache, " "))
65
+		cache = append(cache, "are supported by this server")
66
+		il.CachedReply = append(il.CachedReply, cache)
67
+	}
68
+}
69
+
70
+func (client *Client) RplISupport() {
71
+	for _, tokenline := range client.server.isupport.CachedReply {
72
+		client.Send(nil, client.server.nameString, RPL_ISUPPORT, tokenline...)
68 73
 	}
69 74
 }

+ 4
- 0
irc/modes.go Просмотреть файл

@@ -69,6 +69,8 @@ var (
69 69
 	SupportedUserModes = UserModes{
70 70
 		Away, Invisible, Operator,
71 71
 	}
72
+	// supportedUserModesString acts as a cache for when we introduce users
73
+	supportedUserModesString = SupportedUserModes.String()
72 74
 )
73 75
 
74 76
 const (
@@ -98,6 +100,8 @@ var (
98 100
 		BanMask, ExceptMask, InviteMask, InviteOnly, Key, NoOutside,
99 101
 		OpOnlyTopic, Persistent, Secret, Theater, UserLimit,
100 102
 	}
103
+	// supportedChannelModesString acts as a cache for when we introduce users
104
+	supportedChannelModesString = SupportedChannelModes.String()
101 105
 
102 106
 	DefaultChannelModes = ChannelModes{
103 107
 		NoOutside, OpOnlyTopic,

+ 1
- 1
irc/reply.go Просмотреть файл

@@ -494,7 +494,7 @@ func (target *Client) ErrBadChannelKey(channel *Channel) {
494 494
 
495 495
 func (target *Client) ErrNoSuchNick(nick Name) {
496 496
 	target.NumericReply(ERR_NOSUCHNICK,
497
-		"%s :No such nick/channel", nick)
497
+		"%s :No such nick", nick)
498 498
 }
499 499
 
500 500
 func (target *Client) ErrPasswdMismatch() {

+ 263
- 261
irc/server.go Просмотреть файл

@@ -66,14 +66,6 @@ func NewServer(config *Config) *Server {
66 66
 		theaters:         config.Theaters(),
67 67
 	}
68 68
 
69
-	// ensure that there is a minimum number of args specified for every command
70
-	for name, _ := range parseCommandFuncs {
71
-		_, exists := commandMinimumArgs[name]
72
-		if !exists {
73
-			log.Fatal("commandMinArgs not found for ", name)
74
-		}
75
-	}
76
-
77 69
 	if config.Server.MOTD != "" {
78 70
 		file, err := os.Open(config.Server.MOTD)
79 71
 		if err == nil {
@@ -86,6 +78,9 @@ func NewServer(config *Config) *Server {
86 78
 					break
87 79
 				}
88 80
 				line = strings.TrimRight(line, "\r\n")
81
+				// "- " is the required prefix for MOTD, we just add it here to make
82
+				// bursting it out to clients easier
83
+				line = fmt.Sprintf("- %s", line)
89 84
 
90 85
 				server.motdLines = append(server.motdLines, line)
91 86
 			}
@@ -160,8 +155,8 @@ func (server *Server) loadChannels() {
160 155
 		for _, flag := range flags {
161 156
 			channel.flags[ChannelMode(flag)] = true
162 157
 		}
163
-		channel.key = NewText(key)
164
-		channel.topic = NewText(topic)
158
+		channel.key = key
159
+		channel.topic = topic
165 160
 		channel.userLimit = userLimit
166 161
 		loadChannelList(channel, banList, BanMask)
167 162
 		loadChannelList(channel, exceptList, ExceptMask)
@@ -169,51 +164,10 @@ func (server *Server) loadChannels() {
169 164
 	}
170 165
 }
171 166
 
172
-func (server *Server) processCommand(cmd Command) {
173
-	client := cmd.Client()
174
-
175
-	numCmd, ok := cmd.(*NeedMoreParamsCommand)
176
-	if ok {
177
-		client.ErrNeedMoreParams(numCmd.code)
178
-		return
179
-	}
180
-
181
-	if !client.registered {
182
-		regCmd, ok := cmd.(RegServerCommand)
183
-		if !ok {
184
-			client.Quit("unexpected command")
185
-			return
186
-		}
187
-		regCmd.HandleRegServer(server)
188
-		return
189
-	}
190
-
191
-	srvCmd, ok := cmd.(ServerCommand)
192
-	if !ok {
193
-		client.ErrUnknownCommand(cmd.Code())
194
-		return
195
-	}
196
-
197
-	switch srvCmd.(type) {
198
-	case *PingCommand, *PongCommand:
199
-		client.Touch()
200
-
201
-	case *QuitCommand:
202
-		// no-op
203
-
204
-	default:
205
-		client.Active()
206
-		client.Touch()
207
-	}
208
-
209
-	srvCmd.HandleServer(server)
210
-}
211
-
212 167
 func (server *Server) Shutdown() {
213 168
 	server.db.Close()
214 169
 	for _, client := range server.clients.byNick {
215
-		client.Send("notice")
216
-		client.Reply(RplNotice(server, client, "shutting down"))
170
+		client.Notice("Server is shutting down")
217 171
 	}
218 172
 }
219 173
 
@@ -228,8 +182,10 @@ func (server *Server) Run() {
228 182
 		case conn := <-server.newConns:
229 183
 			NewClient(server, conn)
230 184
 
185
+		/*TODO(dan): LOOK AT THIS MORE CLOSELY
231 186
 		case cmd := <-server.commands:
232 187
 			server.processCommand(cmd)
188
+		*/
233 189
 
234 190
 		case client := <-server.idle:
235 191
 			client.Idle()
@@ -326,32 +282,31 @@ func (s *Server) tryRegister(c *Client) {
326 282
 		(c.capState == CapNegotiating) {
327 283
 		return
328 284
 	}
329
-	c.registered = true
330
-
331
-	c.Send("Intro to the network")
332 285
 	c.Register()
333
-	c.RplWelcome()
334
-	c.RplYourHost()
335
-	c.RplCreated()
336
-	c.RplMyInfo()
286
+
287
+	// send welcome text
288
+	//NOTE(dan): we specifically use the NICK here instead of the nickmask
289
+	// see http://modern.ircdocs.horse/#rplwelcome-001 for details on why we avoid using the nickmask
290
+	c.Send(nil, s.nameString, RPL_WELCOME, fmt.Sprintf("Welcome to the Internet Relay Network %s", c.nickString))
291
+	c.Send(nil, s.nameString, RPL_YOURHOST, fmt.Sprintf("Your host is %s, running version %s", s.nameString, SEM_VER))
292
+	c.Send(nil, s.nameString, RPL_CREATED, fmt.Sprintf("This server was created %s", s.ctime.Format(time.RFC1123)))
293
+	//TODO(dan): Look at adding last optional [<channel modes with a parameter>] parameter
294
+	c.Send(nil, s.nameString, RPL_MYINFO, s.nameString, SEM_VER, supportedUserModesString, supportedChannelModesString)
337 295
 	c.RplISupport()
338 296
 	s.MOTD(c)
339 297
 }
340 298
 
341 299
 func (server *Server) MOTD(client *Client) {
342 300
 	if len(server.motdLines) < 1 {
343
-		c.Send("send")
344
-		client.ErrNoMOTD()
301
+		client.Send(nil, server.nameString, ERR_NOMOTD, client.nickString, "MOTD File is missing")
345 302
 		return
346 303
 	}
347 304
 
348
-	client.RplMOTDStart()
305
+	client.Send(nil, server.nameString, RPL_MOTDSTART, client.nickString, fmt.Sprintf("- %s Message of the day - ", server.nameString))
349 306
 	for _, line := range server.motdLines {
350
-		c.Send("send")
351
-		client.RplMOTD(line)
307
+		client.Send(nil, server.nameString, RPL_MOTD, client.nickString, line)
352 308
 	}
353
-	c.Send("send")
354
-	client.RplMOTDEnd()
309
+	client.Send(nil, server.nameString, RPL_ENDOFMOTD, client.nickString, "End of MOTD command")
355 310
 }
356 311
 
357 312
 func (s *Server) Id() Name {
@@ -372,17 +327,14 @@ func (s *Server) Nick() Name {
372 327
 
373 328
 // PASS <password>
374 329
 func passHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
375
-	if client.Registered {
376
-		client.Send("send")
377
-		client.ErrAlreadyRegistered()
330
+	if client.registered {
331
+		client.Send(nil, server.nameString, ERR_ALREADYREGISTRED, client.nickString, "You may not reregister")
378 332
 		return false
379 333
 	}
380 334
 
381 335
 	// check the provided password
382
-	logger.Fatal("Implement PASS command")
383
-	password := []byte(args[0])
336
+	password := []byte(msg.Params[0])
384 337
 	if ComparePassword(server.password, password) != nil {
385
-		logger.Fatal("SEND BACK REJECTION")
386 338
 		client.Quit("bad password")
387 339
 		return true
388 340
 	}
@@ -410,14 +362,13 @@ func proxyHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
410 362
 
411 363
 // USER <username> * 0 <realname>
412 364
 func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
413
-	if client.Registered {
414
-		client.Send("send")
415
-		client.ErrAlreadyRegistered()
365
+	if client.registered {
366
+		client.Send(nil, server.nameString, ERR_ALREADYREGISTRED, client.nickString, "You may not reregister")
416 367
 		return false
417 368
 	}
418 369
 
419 370
 	if !client.authorized {
420
-		client.Quit("bad password")
371
+		client.Quit("Bad password")
421 372
 		return true
422 373
 	}
423 374
 
@@ -433,16 +384,17 @@ func userHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
433 384
 	server.clients.Remove(client)
434 385
 
435 386
 	if client.username != "" {
436
-		client.username = msg.username
387
+		client.username = Name(msg.Params[0])
388
+		client.updateNickMask()
437 389
 	}
438 390
 	if client.realname != "" {
439
-		client.realname = msg.realname
391
+		client.realname = msg.Params[3]
440 392
 	}
441
-	client.updateNickMask()
442 393
 
443 394
 	server.clients.Add(client)
444
-
445 395
 	server.tryRegister(client)
396
+
397
+	return false
446 398
 }
447 399
 
448 400
 // QUIT [<reason>]
@@ -451,7 +403,7 @@ func quitHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
451 403
 	if len(msg.Params) > 0 {
452 404
 		reason += ": " + msg.Params[0]
453 405
 	}
454
-	client.Quit(msg.message)
406
+	client.Quit(reason)
455 407
 	return true
456 408
 }
457 409
 
@@ -478,7 +430,7 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
478 430
 	// handle JOIN 0
479 431
 	if msg.Params[0] == "0" {
480 432
 		for channel := range client.channels {
481
-			channel.Part(client, client.Nick().Text())
433
+			channel.Part(client, client.nickString)
482 434
 		}
483 435
 		return false
484 436
 	}
@@ -490,15 +442,17 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
490 442
 		keys = strings.Split(msg.Params[1], ",")
491 443
 	}
492 444
 
493
-	for i, name := range channels {
445
+	var name Name
446
+	for i, nameString := range channels {
447
+		name = Name(nameString)
494 448
 		if !name.IsChannel() {
495
-			log.Fatal("Implement ErrNoSuchChannel")
449
+			client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, nameString, "No such channel")
496 450
 			continue
497 451
 		}
498 452
 
499
-		channel := s.channels.Get(name)
453
+		channel := server.channels.Get(name)
500 454
 		if channel == nil {
501
-			channel = NewChannel(s, name, true)
455
+			channel = NewChannel(server, name, true)
502 456
 		}
503 457
 
504 458
 		var key string
@@ -508,34 +462,36 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
508 462
 
509 463
 		channel.Join(client, key)
510 464
 	}
465
+	return false
511 466
 }
512 467
 
513 468
 // PART <channel>{,<channel>} [<reason>]
514 469
 func partHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
515 470
 	channels := strings.Split(msg.Params[0], ",")
516
-	var reason string //TODO(dan): should this be the user's nickname instead of empty?
471
+	var reason string //TODO(dan): if this isn't supplied here, make sure the param doesn't exist in the PART message sent to other users
517 472
 	if len(msg.Params) > 1 {
518 473
 		reason = msg.Params[1]
519 474
 	}
520 475
 
521 476
 	for _, chname := range channels {
522
-		channel := server.channels.Get(chname)
477
+		channel := server.channels.Get(Name(chname))
523 478
 
524 479
 		if channel == nil {
525
-			log.Fatal("Implement ErrNoSuchChannel")
480
+			client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, chname, "No such channel")
526 481
 			continue
527 482
 		}
528 483
 
529
-		channel.Part(client, m.Message())
484
+		channel.Part(client, reason)
530 485
 	}
486
+	return false
531 487
 }
532 488
 
533 489
 // TOPIC <channel> [<topic>]
534 490
 func topicHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
535
-	channel := server.channels.Get(msg.Params[0])
491
+	channel := server.channels.Get(Name(msg.Params[0]))
536 492
 	if channel == nil {
537
-		log.Fatal("Implement ErrNoSuchChannel")
538
-		return
493
+		client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, msg.Params[0], "No such channel")
494
+		return false
539 495
 	}
540 496
 
541 497
 	if len(msg.Params) > 1 {
@@ -543,6 +499,7 @@ func topicHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
543 499
 	} else {
544 500
 		channel.GetTopic(client)
545 501
 	}
502
+	return false
546 503
 }
547 504
 
548 505
 // PRIVMSG <target>{,<target>} <message>
@@ -550,28 +507,30 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool
550 507
 	targets := strings.Split(msg.Params[0], ",")
551 508
 	message := msg.Params[1]
552 509
 
553
-	for _, target := range targets {
510
+	var target Name
511
+	for _, targetString := range targets {
512
+		target = Name(targetString)
554 513
 		if target.IsChannel() {
555 514
 			channel := server.channels.Get(target)
556 515
 			if channel == nil {
557
-				client.Send("send")
558
-				client.ErrNoSuchChannel(target)
516
+				client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, targetString, "No such channel")
559 517
 				continue
560 518
 			}
561 519
 			channel.PrivMsg(client, message)
562 520
 		} else {
563 521
 			user := server.clients.Get(target)
564 522
 			if user == nil {
565
-				client.Send("send")
566
-				client.ErrNoSuchNick(target)
567
-				return
523
+				client.Send(nil, server.nameString, ERR_NOSUCHNICK, targetString, "No such nick")
524
+				continue
568 525
 			}
569
-			user.Send("content here")
526
+			user.Send(nil, client.nickMaskString, "PRIVMSG", user.nickString, message)
570 527
 			if user.flags[Away] {
571
-				client.Send("target is AWAY")
528
+				//TODO(dan): possibly implement cooldown of away notifications to users
529
+				client.Send(nil, server.nameString, RPL_AWAY, user.nickString, user.awayMessage)
572 530
 			}
573 531
 		}
574 532
 	}
533
+	return false
575 534
 }
576 535
 
577 536
 func (client *Client) WhoisChannelsNames(target *Client) []string {
@@ -591,35 +550,79 @@ func (client *Client) WhoisChannelsNames(target *Client) []string {
591 550
 
592 551
 // WHOIS [ <target> ] <mask> *( "," <mask> )
593 552
 func whoisHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
594
-	var masks string
553
+	var masksString string
595 554
 	var target string
596 555
 
597 556
 	if len(msg.Params) > 1 {
598 557
 		target = msg.Params[0]
599
-		masks = msg.Params[1]
558
+		masksString = msg.Params[1]
600 559
 	} else {
601
-		masks = msg.Params[0]
560
+		masksString = msg.Params[0]
602 561
 	}
603 562
 
604
-	// TODO implement target query
605
-	for _, mask := range masks {
606
-		matches := server.clients.FindAll(mask)
607
-		if len(matches) == 0 {
608
-			client.ErrNoSuchNick(mask)
609
-			client.Send("NOSUCHNICK")
610
-			continue
563
+	if client.flags[Operator] {
564
+		masks := strings.Split(masksString, ",")
565
+		for _, mask := range masks {
566
+			matches := server.clients.FindAll(Name(mask))
567
+			if len(matches) == 0 {
568
+				client.Send(nil, client.server.nameString, ERR_NOSUCHNICK, mask, "No such nick")
569
+				continue
570
+			}
571
+			for mclient := range matches {
572
+				mclient.getWhoisOf(client)
573
+			}
611 574
 		}
612
-		for mclient := range matches {
613
-			client.RplWhois(mclient)
614
-			client.Send("WHOIS")
575
+	} else {
576
+		// specifically treat this as a single lookup rather than splitting as we do above
577
+		// this is by design
578
+		mclient := server.clients.Get(Name(masksString))
579
+		if mclient == nil {
580
+			client.Send(nil, client.server.nameString, ERR_NOSUCHNICK, masksString, "No such nick")
581
+			// fall through, ENDOFWHOIS is always sent
582
+		} else {
583
+			client.getWhoisOf(mclient)
615 584
 		}
616 585
 	}
586
+	client.Send(nil, server.nameString, RPL_ENDOFWHOIS, client.nickString, masksString, "End of /WHOIS list")
587
+	return false
588
+}
589
+
590
+func (client *Client) getWhoisOf(target *Client) {
591
+	client.Send(nil, client.server.nameString, RPL_WHOISUSER, client.nickString, target.nickString, target.username.String(), target.hostname.String(), "*", target.realname)
592
+	if target.flags[Operator] {
593
+		client.Send(nil, client.server.nameString, RPL_WHOISOPERATOR, client.nickString, target.nickString, "is an IRC operator")
594
+	}
595
+	client.Send(nil, client.server.nameString, RPL_WHOISIDLE, client.nickString, target.nickString, string(target.IdleSeconds()), string(target.SignonTime()), "seconds idle, signon time")
596
+	for _, line := range client.WhoisChannelsNames(target) {
597
+		client.Send(nil, client.server.nameString, RPL_WHOISCHANNELS, client.nickString, target.nickString, line)
598
+	}
599
+}
600
+
601
+// <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
602
+// :<hopcount> <real name>
603
+func (target *Client) RplWhoReply(channel *Channel, client *Client) {
604
+	channelName := "*"
605
+	flags := ""
606
+
607
+	if client.flags[Away] {
608
+		flags = "G"
609
+	} else {
610
+		flags = "H"
611
+	}
612
+	if client.flags[Operator] {
613
+		flags += "*"
614
+	}
615
+
616
+	if channel != nil {
617
+		flags += channel.members[client].Prefixes(target.capabilities[MultiPrefix])
618
+		channelName = channel.name.String()
619
+	}
620
+	target.Send(nil, target.server.nameString, RPL_WHOREPLY, target.nickString, channelName, client.username.String(), client.hostname.String(), client.server.nameString, client.nickString, flags, string(client.hops), client.realname)
617 621
 }
618 622
 
619 623
 func whoChannel(client *Client, channel *Channel, friends ClientSet) {
620 624
 	for member := range channel.members {
621 625
 		if !client.flags[Invisible] || friends[client] {
622
-			client.Send("send")
623 626
 			client.RplWhoReply(channel, member)
624 627
 		}
625 628
 	}
@@ -629,15 +632,15 @@ func whoChannel(client *Client, channel *Channel, friends ClientSet) {
629 632
 func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
630 633
 	friends := client.Friends()
631 634
 
632
-	var mask string
635
+	var mask Name
633 636
 	if len(msg.Params) > 0 {
634
-		mask = NewName(msg.Params[0])
637
+		mask = Name(msg.Params[0])
635 638
 	}
636 639
 
637 640
 	//TODO(dan): is this used and would I put this param in the Modern doc?
638 641
 	// if not, can we remove it?
639 642
 	var operatorOnly bool
640
-	if len(msg.Params) > 1 && msr.Params[1] == "o" {
643
+	if len(msg.Params) > 1 && msg.Params[1] == "o" {
641 644
 		operatorOnly = true
642 645
 	}
643 646
 
@@ -647,6 +650,7 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
647 650
 		}
648 651
 	} else if mask.IsChannel() {
649 652
 		// TODO implement wildcard matching
653
+		//TODO(dan): ^ only for opers
650 654
 		channel := server.channels.Get(mask)
651 655
 		if channel != nil {
652 656
 			whoChannel(client, channel, friends)
@@ -654,37 +658,35 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
654 658
 	} else {
655 659
 		for mclient := range server.clients.FindAll(mask) {
656 660
 			client.RplWhoReply(nil, mclient)
657
-			client.Send("REPLY")
658 661
 		}
659 662
 	}
660 663
 
661
-	client.RplEndOfWho(mask)
662
-	client.Send("ENDOFWHO")
664
+	client.Send(nil, server.nameString, RPL_ENDOFWHO, client.nickString, mask.String(), "End of WHO list")
665
+	return false
663 666
 }
664 667
 
665 668
 // OPER <name> <password>
666 669
 func operHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
667
-	name = NewName(msg.Params[0])
668
-	hash = server.operators[name]
669
-	password = []byte(msg.Params[1])
670
+	name := NewName(msg.Params[0])
671
+	hash := server.operators[name]
672
+	password := []byte(msg.Params[1])
670 673
 
671
-	err = ComparePassword(hash, password)
674
+	err := ComparePassword(hash, password)
672 675
 
673 676
 	if (hash == nil) || (err != nil) {
674
-		client.ErrPasswdMismatch()
675
-		client.Send("PASSWDBAD")
677
+		client.Send(nil, server.nameString, ERR_PASSWDMISMATCH, client.nickString, "Password incorrect")
676 678
 		return true
677 679
 	}
678 680
 
679
-	//TODO(dan): Split this into client.makeOper() ??
680 681
 	client.flags[Operator] = true
681
-	client.RplYoureOper()
682
-	client.Send("YOUROPER")
683
-	client.Reply(RplModeChanges(client, client, ModeChanges{&ModeChange{
682
+	client.Send(nil, server.nameString, RPL_YOUREOPER, client.nickString, "You are not an IRC operator")
683
+	//TODO(dan): Should this be sent automagically as part of setting the flag/mode?
684
+	modech := ModeChanges{&ModeChange{
684 685
 		mode: Operator,
685 686
 		op:   Add,
686
-	}}))
687
-	client.Send("OPERMODECHANGE")
687
+	}}
688
+	client.Send(nil, server.nameString, "MODE", client.nickString, client.nickString, modech.String())
689
+	return false
688 690
 }
689 691
 
690 692
 // AWAY [<message>]
@@ -692,8 +694,8 @@ func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
692 694
 	var isAway bool
693 695
 	var text string
694 696
 	if len(msg.Params) > 0 {
695
-		isAway = True
696
-		text = NewText(msg.Params[0])
697
+		isAway = true
698
+		text = msg.Params[0]
697 699
 	}
698 700
 
699 701
 	if isAway {
@@ -706,33 +708,33 @@ func awayHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
706 708
 	var op ModeOp
707 709
 	if client.flags[Away] {
708 710
 		op = Add
709
-		client.Send("imaway")
710
-		client.RplNowAway()
711
+		client.Send(nil, server.nameString, RPL_NOWAWAY, client.nickString, "You have been marked as being away")
711 712
 	} else {
712 713
 		op = Remove
713
-		client.Send("unaway")
714
-		client.RplUnAway()
714
+		client.Send(nil, server.nameString, RPL_UNAWAY, client.nickString, "You are no longer marked as being away")
715 715
 	}
716
-	client.Send("mode changes I guess?")
717
-	client.Reply(RplModeChanges(client, client, ModeChanges{&ModeChange{
716
+	//TODO(dan): Should this be sent automagically as part of setting the flag/mode?
717
+	modech := ModeChanges{&ModeChange{
718 718
 		mode: Away,
719 719
 		op:   op,
720
-	}}))
720
+	}}
721
+	client.Send(nil, server.nameString, "MODE", client.nickString, client.nickString, modech.String())
722
+	return false
721 723
 }
722 724
 
723 725
 // ISON <nick>{ <nick>}
724 726
 func isonHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
725
-	var nicks = NewNames(msg.Params)
727
+	var nicks = msg.Params
726 728
 
727 729
 	ison := make([]string, 0)
728 730
 	for _, nick := range nicks {
729
-		if iclient := server.clients.Get(nick); iclient != nil {
731
+		if iclient := server.clients.Get(Name(nick)); iclient != nil {
730 732
 			ison = append(ison, iclient.Nick().String())
731 733
 		}
732 734
 	}
733 735
 
734
-	client.Send("ISON")
735
-	client.RplIsOn(ison)
736
+	client.Send(nil, server.nameString, RPL_ISON, client.nickString, strings.Join(nicks, " "))
737
+	return false
736 738
 }
737 739
 
738 740
 // MOTD [<target>]
@@ -740,51 +742,50 @@ func motdHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
740 742
 	//TODO(dan): hook this up when we have multiple servers I guess???
741 743
 	var target string
742 744
 	if len(msg.Params) > 0 {
743
-		target = NewName(msg.Params[0])
745
+		target = msg.Params[0]
744 746
 	}
745 747
 
746
-	client.Send("MOTD")
747
-	server.MOTD(msg.Client())
748
+	server.MOTD(client)
749
+	return false
748 750
 }
749 751
 
750 752
 // NOTICE <target>{,<target>} <message>
751 753
 func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
752
-	targetName := NewName(msg.Params[0])
753
-	message := NewText(msg.Params[1])
754
+	targets := strings.Split(msg.Params[0], ",")
755
+	message := msg.Params[1]
754 756
 
755
-	if targetName.IsChannel() {
756
-		channel := server.channels.Get(targetName)
757
-		if channel == nil {
758
-			client.Send("ERRNOSUCHCHAN")
759
-			client.ErrNoSuchChannel(targetName)
760
-			return
757
+	var target Name
758
+	for _, targetString := range targets {
759
+		target = Name(targetString)
760
+		if target.IsChannel() {
761
+			channel := server.channels.Get(target)
762
+			if channel == nil {
763
+				// errors silently ignored with NOTICE as per RFC
764
+				continue
765
+			}
766
+			channel.PrivMsg(client, message)
767
+		} else {
768
+			user := server.clients.Get(target)
769
+			if user == nil {
770
+				// errors silently ignored with NOTICE as per RFC
771
+				continue
772
+			}
773
+			user.Send(nil, client.nickMaskString, "NOTICE", user.nickString, message)
761 774
 		}
762
-
763
-		channel.Notice(client, message)
764
-		return
765
-	}
766
-
767
-	target := server.clients.Get(targetName)
768
-	if target == nil {
769
-		client.Send("ERRNOSUCHNICK")
770
-		client.ErrNoSuchNick(targetName)
771
-		return
772 775
 	}
773
-	client.Send("NOTICE")
774
-	target.Reply(RplNotice(client, target, message))
776
+	return false
775 777
 }
776 778
 
777 779
 // KICK <channel>{,<channel>} <user>{,<user>} [<comment>]
778 780
 func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
779
-	channels := NewNames(strings.Split(msg.Params[0], ","))
780
-	users := NewNames(strings.Split(msg.Params[1], ","))
781
+	channels := strings.Split(msg.Params[0], ",")
782
+	users := strings.Split(msg.Params[1], ",")
781 783
 	if (len(channels) != len(users)) && (len(users) != 1) {
782
-		client.Send("NotEnoughArgs??")
784
+		client.Send(nil, server.nameString, ERR_NEEDMOREPARAMS, client.nickString, "KICK", "Not enough parameters")
783 785
 		return false
784
-		// not needed return nil, NotEnoughArgsError
785 786
 	}
786 787
 
787
-	kicks := make(map[Name]Name)
788
+	kicks := make(map[string]string)
788 789
 	for index, channel := range channels {
789 790
 		if len(users) == 1 {
790 791
 			kicks[channel] = users[0]
@@ -798,17 +799,15 @@ func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
798 799
 		comment = msg.Params[2]
799 800
 	}
800 801
 	for chname, nickname := range kicks {
801
-		channel := server.channels.Get(chname)
802
+		channel := server.channels.Get(Name(chname))
802 803
 		if channel == nil {
803
-			client.ErrNoSuchChannel(chname)
804
-			client.Send("send")
804
+			client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, chname, "No such channel")
805 805
 			continue
806 806
 		}
807 807
 
808
-		target := server.clients.Get(nickname)
808
+		target := server.clients.Get(Name(nickname))
809 809
 		if target == nil {
810
-			client.ErrNoSuchNick(nickname)
811
-			client.Send("send")
810
+			client.Send(nil, server.nameString, ERR_NOSUCHNICK, nickname, "No such nick")
812 811
 			continue
813 812
 		}
814 813
 
@@ -836,29 +835,28 @@ func kickHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
836 835
 				channel.Kick(client, target, comment)
837 836
 			}
838 837
 		} else {
839
-			client.ErrChanOPrivIsNeeded(channel)
840
-			client.Send("send")
838
+			client.Send(nil, client.server.nameString, ERR_CHANOPRIVSNEEDED, chname, "You're not a channel operator")
841 839
 		}
842 840
 	}
841
+	return false
843 842
 }
844 843
 
845 844
 // LIST [<channel>{,<channel>} [<server>]]
846 845
 func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
847
-	var channels []Name
848
-	if len(args) > 0 {
849
-		channels = NewNames(strings.Split(args[0], ","))
846
+	var channels []string
847
+	if len(msg.Params) > 0 {
848
+		channels = strings.Split(msg.Params[0], ",")
850 849
 	}
851
-	var target Name
852
-	if len(args) > 1 {
853
-		target = NewName(args[1])
850
+	var target string
851
+	if len(msg.Params) > 1 {
852
+		target = msg.Params[1]
854 853
 	}
855 854
 
856 855
 	//TODO(dan): target server when we have multiple servers
857 856
 	//TODO(dan): we should continue just fine if it's this current server though
858 857
 	if target != "" {
859
-		client.ErrNoSuchServer(msg.target)
860
-		client.Send("send")
861
-		return
858
+		client.Send(nil, server.nameString, ERR_NOSUCHSERVER, client.nickString, target, "No such server")
859
+		return false
862 860
 	}
863 861
 
864 862
 	if len(channels) == 0 {
@@ -867,33 +865,46 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
867 865
 				continue
868 866
 			}
869 867
 			client.RplList(channel)
870
-			client.Send("send")
871 868
 		}
872 869
 	} else {
873 870
 		for _, chname := range channels {
874
-			channel := server.channels.Get(chname)
871
+			channel := server.channels.Get(Name(chname))
875 872
 			if channel == nil || (!client.flags[Operator] && channel.flags[Secret]) {
876
-				client.ErrNoSuchChannel(chname)
877
-				client.Send("send")
873
+				client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, chname, "No such channel")
878 874
 				continue
879 875
 			}
880 876
 			client.RplList(channel)
881
-			client.Send("send")
882 877
 		}
883 878
 	}
884
-	client.RplListEnd(server)
885
-	client.Send("send")
879
+	client.Send(nil, server.nameString, RPL_LISTEND, client.nickString, "End of LIST")
880
+	return false
881
+}
882
+
883
+func (target *Client) RplList(channel *Channel) {
884
+	// get the correct number of channel members
885
+	var memberCount int
886
+	if target.flags[Operator] || channel.members.Has(target) {
887
+		memberCount = len(channel.members)
888
+	} else {
889
+		for member := range channel.members {
890
+			if !member.flags[Invisible] {
891
+				memberCount += 1
892
+			}
893
+		}
894
+	}
895
+
896
+	target.Send(nil, target.server.nameString, RPL_LIST, target.nickString, channel.nameString, string(memberCount), channel.topic)
886 897
 }
887 898
 
888 899
 // NAMES [<channel>{,<channel>}]
889 900
 func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
890
-	var channels []Name
891
-	if len(args) > 0 {
892
-		channels = NewNames(strings.Split(args[0], ","))
901
+	var channels []string
902
+	if len(msg.Params) > 0 {
903
+		channels = strings.Split(msg.Params[0], ",")
893 904
 	}
894
-	var target Name
895
-	if len(args) > 1 {
896
-		target = NewName(args[1])
905
+	var target string
906
+	if len(msg.Params) > 1 {
907
+		target = msg.Params[1]
897 908
 	}
898 909
 
899 910
 	if len(channels) == 0 {
@@ -904,121 +915,112 @@ func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
904 915
 	}
905 916
 
906 917
 	for _, chname := range channels {
907
-		channel := server.channels.Get(chname)
918
+		channel := server.channels.Get(Name(chname))
908 919
 		if channel == nil {
909
-			client.ErrNoSuchChannel(chname)
910
-			client.Send("send")
920
+			client.Send(nil, server.nameString, ERR_NOSUCHCHANNEL, client.nickString, chname, "No such channel")
911 921
 			continue
912 922
 		}
913 923
 		channel.Names(client)
914
-		client.Send("send")
915 924
 	}
925
+	return false
916 926
 }
917 927
 
918 928
 // VERSION [<server>]
919 929
 func versionHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
920
-	var target Name
921
-	if len(args) > 0 {
922
-		target = NewName(args[0])
930
+	var target string
931
+	if len(msg.Params) > 0 {
932
+		target = msg.Params[0]
923 933
 	}
924
-	if (target != "") && (target != server.name) {
925
-		client.ErrNoSuchServer(target)
926
-		client.Send("send")
927
-		return
934
+	if (target != "") && (Name(target) != server.name) {
935
+		client.Send(nil, server.nameString, ERR_NOSUCHSERVER, client.nickString, target, "No such server")
936
+		return false
928 937
 	}
929 938
 
930
-	client.RplVersion()
931
-	client.Send("send")
939
+	client.Send(nil, server.nameString, RPL_VERSION, client.nickString, SEM_VER, server.nameString)
932 940
 	client.RplISupport()
933
-	client.Send("send")
941
+	return false
934 942
 }
935 943
 
936 944
 // INVITE <nickname> <channel>
937 945
 func inviteHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
938
-	nickname := NewName(msg.Params[0])
939
-	channelName := NewName(msg.Params[1])
946
+	nickname := msg.Params[0]
947
+	channelName := msg.Params[1]
940 948
 
941
-	target := server.clients.Get(nickname)
949
+	target := server.clients.Get(Name(nickname))
942 950
 	if target == nil {
943
-		client.ErrNoSuchNick(nickname)
944
-		client.Send("send")
945
-		return
951
+		client.Send(nil, server.nameString, ERR_NOSUCHNICK, client.nickString, nickname, "No such nick")
952
+		return false
946 953
 	}
947 954
 
948
-	channel := server.channels.Get(channelName)
955
+	channel := server.channels.Get(Name(channelName))
949 956
 	if channel == nil {
950
-		client.RplInviting(target, channelName)
951
-		client.Send("send")
952
-		target.Reply(RplInviteMsg(client, target, channelName))
953
-		client.Send("send")
954
-		return
957
+		client.Send(nil, server.nameString, RPL_INVITING, client.nickString, target.nickString, channelName)
958
+		target.Send(nil, client.nickMaskString, "INVITE", target.nickString, channel.nameString)
959
+		return true
955 960
 	}
956 961
 
957 962
 	channel.Invite(target, client)
963
+	return false
958 964
 }
959 965
 
960 966
 // TIME [<server>]
961 967
 func timeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
962
-	var target Name
968
+	var target string
963 969
 	if len(msg.Params) > 0 {
964
-		target = NewName(msg.Params[0])
970
+		target = msg.Params[0]
965 971
 	}
966
-	if (target != "") && (target != server.name) {
967
-		client.ErrNoSuchServer(target)
968
-		client.Send("send")
969
-		return
972
+	if (target != "") && (Name(target) != server.name) {
973
+		client.Send(nil, server.nameString, ERR_NOSUCHSERVER, client.nickString, target, "No such server")
974
+		return false
970 975
 	}
971
-	client.RplTime()
972
-	client.Send("send")
976
+	client.Send(nil, server.nameString, RPL_TIME, client.nickString, server.nameString, time.Now().Format(time.RFC1123))
977
+	return false
973 978
 }
974 979
 
975 980
 // KILL <nickname> <comment>
976 981
 func killHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
977
-	nickname := NewName(msg.Params[0])
978
-	comment := NewText(msg.Params[1])
982
+	nickname := msg.Params[0]
983
+	comment := msg.Params[1]
979 984
 
980 985
 	if !client.flags[Operator] {
981
-		client.ErrNoPrivileges()
982
-		client.Send("send")
983
-		return
986
+		client.Send(nil, server.nameString, ERR_NOPRIVILEGES, client.nickString, "Permission Denied - You're not an IRC operator")
987
+		return false
984 988
 	}
985 989
 
986
-	target := server.clients.Get(nickname)
990
+	target := server.clients.Get(Name(nickname))
987 991
 	if target == nil {
988
-		client.ErrNoSuchNick(nickname)
989
-		client.Send("send")
990
-		return
992
+		client.Send(nil, client.server.nameString, ERR_NOSUCHNICK, nickname, "No such nick")
993
+		return false
991 994
 	}
992 995
 
993
-	//TODO(dan): make below format match that from other IRCds
994
-	quitMsg := fmt.Sprintf("KILLed by %s: %s", client.Nick(), comment)
995
-	target.Quit(NewText(quitMsg))
996
-	return true
996
+	quitMsg := fmt.Sprintf("Killed (%s (%s))", client.nickString, comment)
997
+	target.Quit(quitMsg)
998
+	target.destroy()
999
+	return false
997 1000
 }
998 1001
 
999 1002
 // WHOWAS <nickname> [<count> [<server>]]
1000 1003
 func whowasHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
1001
-	nicknames := NewNames(strings.Split(msg.Params[0], ","))
1004
+	nicknames := strings.Split(msg.Params[0], ",")
1002 1005
 
1003
-	var count int
1006
+	var count int64
1004 1007
 	if len(msg.Params) > 1 {
1005 1008
 		count, _ = strconv.ParseInt(msg.Params[1], 10, 64)
1006 1009
 	}
1007
-	var target Name
1010
+	var target string
1008 1011
 	if len(msg.Params) > 2 {
1009
-		target = NewName(msg.Params[2])
1012
+		target = msg.Params[2]
1010 1013
 	}
1011 1014
 	for _, nickname := range nicknames {
1012
-		results := server.whoWas.Find(nickname, msg.count)
1015
+		results := server.whoWas.Find(Name(nickname), count)
1013 1016
 		if len(results) == 0 {
1014
-			client.ErrWasNoSuchNick(nickname)
1015
-			client.Send("send")
1017
+			client.Send(nil, server.nameString, ERR_WASNOSUCHNICK, client.nickString, nickname, "There was no such nickname")
1016 1018
 		} else {
1017 1019
 			for _, whoWas := range results {
1018
-				client.RplWhoWasUser(whoWas)
1019
-				client.Send("send")
1020
+				client.Send(nil, server.nameString, RPL_WHOWASUSER, client.nickString, whoWas.nickname.String(), whoWas.username.String(), whoWas.hostname.String(), "*", whoWas.realname)
1020 1021
 			}
1021 1022
 		}
1022
-		client.Send(nil, server.Name, RPL_ENDOFWHOWAS, nickname, "End of WHOWAS")
1023
+		client.Send(nil, server.nameString, RPL_ENDOFWHOWAS, client.nickString, nickname, "End of WHOWAS")
1023 1024
 	}
1025
+	return false
1024 1026
 }

+ 1
- 1
irc/whowas.go Просмотреть файл

@@ -13,7 +13,7 @@ type WhoWas struct {
13 13
 	nickname Name
14 14
 	username Name
15 15
 	hostname Name
16
-	realname Text
16
+	realname string
17 17
 }
18 18
 
19 19
 func NewWhoWasList(size uint) *WhoWasList {

Загрузка…
Отмена
Сохранить