Просмотр исходного кода

get rid of mutexes in favor of channel-base syncing

tags/v0.1.0
Jeremy Latt 10 лет назад
Родитель
Сommit
e411dafda7
6 измененных файлов: 396 добавлений и 350 удалений
  1. 80
    83
      irc/channel.go
  2. 82
    51
      irc/client.go
  3. 150
    136
      irc/constants.go
  4. 31
    28
      irc/reply.go
  5. 13
    41
      irc/server.go
  6. 40
    11
      irc/types.go

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

@@ -2,21 +2,18 @@ package irc
2 2
 
3 3
 import (
4 4
 	"log"
5
-	"sync"
6 5
 )
7 6
 
8 7
 type Channel struct {
9
-	banList   []UserMask
10
-	commands  chan<- ChannelCommand
11
-	destroyed bool
12
-	flags     map[ChannelMode]bool
13
-	key       string
14
-	members   ClientSet
15
-	mutex     *sync.Mutex
16
-	name      string
17
-	replies   chan<- Reply
18
-	server    *Server
19
-	topic     string
8
+	banList  []UserMask
9
+	commands chan<- ChannelCommand
10
+	flags    ChannelModeSet
11
+	key      string
12
+	members  MemberSet
13
+	name     string
14
+	replies  chan<- Reply
15
+	server   *Server
16
+	topic    string
20 17
 }
21 18
 
22 19
 func IsChannel(target string) bool {
@@ -38,9 +35,8 @@ func NewChannel(s *Server, name string) *Channel {
38 35
 	channel := &Channel{
39 36
 		banList:  make([]UserMask, 0),
40 37
 		commands: commands,
41
-		flags:    make(map[ChannelMode]bool),
42
-		members:  make(ClientSet),
43
-		mutex:    &sync.Mutex{},
38
+		flags:    make(ChannelModeSet),
39
+		members:  make(MemberSet),
44 40
 		name:     name,
45 41
 		replies:  replies,
46 42
 		server:   s,
@@ -50,19 +46,17 @@ func NewChannel(s *Server, name string) *Channel {
50 46
 	return channel
51 47
 }
52 48
 
53
-func (channel *Channel) Destroy() {
54
-	if channel.IsDestroyed() {
55
-		return
56
-	}
57
-
58
-	channel.withMutex(func() {
59
-		channel.destroyed = true
60
-		channel.members = make(ClientSet)
61
-	})
49
+type DestroyChannel struct {
50
+	BaseCommand
51
+	channel *Channel
52
+}
62 53
 
63
-	channel.server.withMutex(func() {
64
-		channel.server.channels.Remove(channel)
54
+func (channel *Channel) Destroy() {
55
+	channel.server.Command(&DestroyChannel{
56
+		channel: channel,
65 57
 	})
58
+	close(channel.commands)
59
+	close(channel.replies)
66 60
 }
67 61
 
68 62
 func (channel *Channel) Command(command ChannelCommand) {
@@ -75,13 +69,6 @@ func (channel *Channel) Reply(reply Reply) {
75 69
 
76 70
 func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
77 71
 	for command := range commands {
78
-		if channel.IsDestroyed() {
79
-			if DEBUG_CHANNEL {
80
-				log.Printf("%s → %s %s dropped", command.Source(), channel, command)
81
-			}
82
-			continue
83
-		}
84
-
85 72
 		if DEBUG_CHANNEL {
86 73
 			log.Printf("%s → %s %s", command.Source(), channel, command)
87 74
 		}
@@ -89,40 +76,19 @@ func (channel *Channel) receiveCommands(commands <-chan ChannelCommand) {
89 76
 	}
90 77
 }
91 78
 
92
-func IsPrivMsg(reply Reply) bool {
93
-	strReply, ok := reply.(*StringReply)
94
-	if !ok {
95
-		return false
96
-	}
97
-	return strReply.code == "PRIVMSG"
98
-}
99
-
100
-func (channel *Channel) IsDestroyed() bool {
101
-	channel.mutex.Lock()
102
-	defer channel.mutex.Unlock()
103
-	return channel.destroyed
104
-}
105
-
106 79
 func (channel *Channel) receiveReplies(replies <-chan Reply) {
107 80
 	for reply := range replies {
108
-		if channel.IsDestroyed() {
109
-			if DEBUG_CHANNEL {
110
-				log.Printf("%s ← %s %s dropped", channel, reply.Source(), reply)
111
-			}
112
-			continue
113
-		}
114
-
115 81
 		if DEBUG_CHANNEL {
116 82
 			log.Printf("%s ← %s %s", channel, reply.Source(), reply)
117 83
 		}
118
-		channel.withMutex(func() {
119
-			for client := range channel.members {
120
-				if IsPrivMsg(reply) && (reply.Source() == Identifier(client)) {
121
-					continue
122
-				}
123
-				client.Reply(reply)
84
+
85
+		for client := range channel.members {
86
+			if (reply.Code() == ReplyCode(PRIVMSG)) &&
87
+				(reply.Source() == Identifier(client)) {
88
+				continue
124 89
 			}
125
-		})
90
+			client.Reply(reply)
91
+		}
126 92
 	}
127 93
 }
128 94
 
@@ -198,25 +164,9 @@ func (channel *Channel) ModeString() (str string) {
198 164
 	return
199 165
 }
200 166
 
201
-func (channel *Channel) withMutex(f func()) {
202
-	channel.mutex.Lock()
203
-	defer channel.mutex.Unlock()
204
-	f()
205
-}
206
-
207
-func (channel *Channel) Join(client *Client) {
208
-	channel.withMutex(func() {
209
-		channel.members.Add(client)
210
-		if len(channel.members) == 1 {
211
-			channel.members[client][ChannelCreator] = true
212
-			channel.members[client][ChannelOperator] = true
213
-		}
214
-		client.channels.Add(channel)
215
-	})
216
-
217
-	channel.Reply(RplJoin(client, channel))
218
-	channel.GetTopic(client)
219
-	channel.GetUsers(client)
167
+type JoinChannel struct {
168
+	BaseCommand
169
+	channel *Channel
220 170
 }
221 171
 
222 172
 //
@@ -230,7 +180,33 @@ func (m *JoinCommand) HandleChannel(channel *Channel) {
230 180
 		return
231 181
 	}
232 182
 
233
-	channel.Join(client)
183
+	channel.members.Add(client)
184
+	if len(channel.members) == 1 {
185
+		channel.members[client][ChannelCreator] = true
186
+		channel.members[client][ChannelOperator] = true
187
+	}
188
+
189
+	client.commands <- &JoinChannel{
190
+		channel: channel,
191
+	}
192
+
193
+	addClient := &AddFriend{
194
+		client: client,
195
+	}
196
+	for member := range channel.members {
197
+		client.commands <- &AddFriend{
198
+			client: member,
199
+		}
200
+		member.commands <- addClient
201
+	}
202
+
203
+	channel.Reply(RplJoin(client, channel))
204
+	channel.GetTopic(client)
205
+	channel.GetUsers(client)
206
+}
207
+
208
+type PartChannel struct {
209
+	channel *Channel
234 210
 }
235 211
 
236 212
 func (m *PartCommand) HandleChannel(channel *Channel) {
@@ -244,9 +220,15 @@ func (m *PartCommand) HandleChannel(channel *Channel) {
244 220
 	channel.Reply(RplPart(client, channel, m.Message()))
245 221
 
246 222
 	channel.members.Remove(client)
247
-	client.channels.Remove(channel)
223
+	client.commands <- &PartChannel{
224
+		channel: channel,
225
+	}
226
+	for member := range channel.members {
227
+		member.commands <- &RemoveFriend{
228
+			client: client,
229
+		}
230
+	}
248 231
 
249
-	// TODO persistent channels
250 232
 	if channel.IsEmpty() {
251 233
 		channel.Destroy()
252 234
 	}
@@ -387,3 +369,18 @@ func (m *NoticeCommand) HandleChannel(channel *Channel) {
387 369
 	}
388 370
 	channel.Reply(RplNotice(client, channel, m.message))
389 371
 }
372
+
373
+func (msg *QuitCommand) HandleChannel(channel *Channel) {
374
+	client := msg.Client()
375
+	removeClient := &RemoveFriend{
376
+		client: client,
377
+	}
378
+	for member := range channel.members {
379
+		member.commands <- removeClient
380
+	}
381
+	channel.members.Remove(client)
382
+}
383
+
384
+func (msg *DestroyClient) HandleChannel(channel *Channel) {
385
+	channel.members.Remove(msg.client)
386
+}

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

@@ -4,7 +4,6 @@ import (
4 4
 	"fmt"
5 5
 	"log"
6 6
 	"net"
7
-	"sync"
8 7
 	"time"
9 8
 )
10 9
 
@@ -13,13 +12,13 @@ type Client struct {
13 12
 	away        bool
14 13
 	awayMessage string
15 14
 	channels    ChannelSet
15
+	commands    chan ClientCommand
16 16
 	ctime       time.Time
17
-	destroyed   bool
17
+	friends     map[*Client]uint
18 18
 	hostname    string
19 19
 	idleTimer   *time.Timer
20 20
 	invisible   bool
21 21
 	loginTimer  *time.Timer
22
-	mutex       *sync.Mutex
23 22
 	nick        string
24 23
 	operator    bool
25 24
 	phase       Phase
@@ -36,16 +35,18 @@ func NewClient(server *Server, conn net.Conn) *Client {
36 35
 	client := &Client{
37 36
 		atime:    now,
38 37
 		channels: make(ChannelSet),
38
+		commands: make(chan ClientCommand),
39 39
 		ctime:    now,
40
+		friends:  make(map[*Client]uint),
40 41
 		hostname: AddrLookupHostname(conn.RemoteAddr()),
41 42
 		phase:    server.InitPhase(),
42 43
 		replies:  make(chan Reply),
43 44
 		server:   server,
44 45
 		socket:   NewSocket(conn),
45
-		mutex:    &sync.Mutex{},
46 46
 	}
47
-	client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.Destroy)
48 47
 
48
+	client.loginTimer = time.AfterFunc(LOGIN_TIMEOUT, client.Destroy)
49
+	go client.readClientCommands()
49 50
 	go client.readCommands()
50 51
 	go client.writeReplies()
51 52
 
@@ -53,10 +54,6 @@ func NewClient(server *Server, conn net.Conn) *Client {
53 54
 }
54 55
 
55 56
 func (client *Client) Touch() {
56
-	if client.IsDestroyed() {
57
-		return
58
-	}
59
-
60 57
 	client.atime = time.Now()
61 58
 
62 59
 	if client.quitTimer != nil {
@@ -89,10 +86,6 @@ func (client *Client) ConnectionTimeout() {
89 86
 }
90 87
 
91 88
 func (client *Client) ConnectionClosed() {
92
-	if client.IsDestroyed() {
93
-		return
94
-	}
95
-
96 89
 	msg := &QuitCommand{
97 90
 		message: "connection closed",
98 91
 	}
@@ -100,6 +93,12 @@ func (client *Client) ConnectionClosed() {
100 93
 	client.server.Command(msg)
101 94
 }
102 95
 
96
+func (client *Client) readClientCommands() {
97
+	for command := range client.commands {
98
+		command.HandleClient(client)
99
+	}
100
+}
101
+
103 102
 func (c *Client) readCommands() {
104 103
 	for line := range c.socket.Read() {
105 104
 		m, err := ParseCommand(line)
@@ -126,13 +125,6 @@ func (c *Client) readCommands() {
126 125
 
127 126
 func (client *Client) writeReplies() {
128 127
 	for reply := range client.replies {
129
-		if client.IsDestroyed() {
130
-			if DEBUG_CLIENT {
131
-				log.Printf("%s ← %s dropped", client, reply)
132
-			}
133
-			continue
134
-		}
135
-
136 128
 		if DEBUG_CLIENT {
137 129
 			log.Printf("%s ← %s", client, reply)
138 130
 		}
@@ -143,24 +135,12 @@ func (client *Client) writeReplies() {
143 135
 	}
144 136
 }
145 137
 
146
-func (client *Client) IsDestroyed() bool {
147
-	client.mutex.Lock()
148
-	defer client.mutex.Unlock()
149
-	return client.destroyed
138
+type DestroyClient struct {
139
+	BaseCommand
140
+	client *Client
150 141
 }
151 142
 
152 143
 func (client *Client) Destroy() {
153
-	if client.IsDestroyed() {
154
-		return
155
-	}
156
-
157
-	if DEBUG_CLIENT {
158
-		log.Printf("%s destroying", client)
159
-	}
160
-
161
-	client.mutex.Lock()
162
-	client.destroyed = true
163
-
164 144
 	client.socket.Close()
165 145
 
166 146
 	if client.idleTimer != nil {
@@ -171,14 +151,15 @@ func (client *Client) Destroy() {
171 151
 		client.quitTimer.Stop()
172 152
 	}
173 153
 
174
-	client.channels = make(ChannelSet) // clear channel list
175
-	client.server.clients.Remove(client)
176
-
177
-	client.mutex.Unlock()
154
+	cmd := &DestroyClient{
155
+		client: client,
156
+	}
178 157
 
179
-	if DEBUG_CLIENT {
180
-		log.Printf("%s destroyed", client)
158
+	for channel := range client.channels {
159
+		channel.Command(cmd)
181 160
 	}
161
+
162
+	client.server.Command(cmd)
182 163
 }
183 164
 
184 165
 func (client *Client) Reply(reply Reply) {
@@ -199,16 +180,6 @@ func (client *Client) HasUsername() bool {
199 180
 	return client.username != ""
200 181
 }
201 182
 
202
-func (client *Client) InterestedClients() ClientSet {
203
-	clients := make(ClientSet)
204
-	for channel := range client.channels {
205
-		for member := range channel.members {
206
-			clients.Add(member)
207
-		}
208
-	}
209
-	return clients
210
-}
211
-
212 183
 // <mode>
213 184
 func (c *Client) ModeString() (str string) {
214 185
 	if c.invisible {
@@ -246,3 +217,63 @@ func (c *Client) Id() string {
246 217
 func (c *Client) String() string {
247 218
 	return c.UserHost()
248 219
 }
220
+
221
+//
222
+// commands
223
+//
224
+
225
+type AddFriend struct {
226
+	client *Client
227
+}
228
+
229
+func (msg *AddFriend) HandleClient(client *Client) {
230
+	client.friends[msg.client] += 1
231
+}
232
+
233
+type RemoveFriend struct {
234
+	client *Client
235
+}
236
+
237
+func (msg *RemoveFriend) HandleClient(client *Client) {
238
+	client.friends[msg.client] -= 1
239
+	if client.friends[msg.client] <= 0 {
240
+		delete(client.friends, msg.client)
241
+	}
242
+}
243
+
244
+func (msg *JoinChannel) HandleClient(client *Client) {
245
+	client.channels.Add(msg.channel)
246
+}
247
+
248
+func (msg *PartChannel) HandleClient(client *Client) {
249
+	client.channels.Remove(msg.channel)
250
+}
251
+
252
+func (msg *NickCommand) HandleClient(client *Client) {
253
+	// Make reply before changing nick.
254
+	reply := RplNick(client, msg.nickname)
255
+
256
+	client.nick = msg.nickname
257
+
258
+	for friend := range client.friends {
259
+		friend.Reply(reply)
260
+	}
261
+}
262
+
263
+func (msg *QuitCommand) HandleClient(client *Client) {
264
+	if len(client.friends) > 0 {
265
+		reply := RplQuit(client, msg.message)
266
+		for friend := range client.friends {
267
+			if friend == client {
268
+				continue
269
+			}
270
+			friend.Reply(reply)
271
+		}
272
+	}
273
+
274
+	for channel := range client.channels {
275
+		channel.commands <- msg
276
+	}
277
+
278
+	client.Destroy()
279
+}

+ 150
- 136
irc/constants.go Просмотреть файл

@@ -23,143 +23,157 @@ const (
23 23
 	IDLE_TIMEOUT  = time.Minute     // how long before a client is considered idle
24 24
 	QUIT_TIMEOUT  = time.Minute     // how long after idle before a client is kicked
25 25
 
26
+	// string codes
27
+	PRIVMSG StringCode = "PRIVMSG"
28
+	NOTICE  StringCode = "NOTICE"
29
+	NICK    StringCode = "NICK"
30
+	JOIN    StringCode = "JOIN"
31
+	PART    StringCode = "PART"
32
+	MODE    StringCode = "MODE"
33
+	TOPIC   StringCode = "TOPIC"
34
+	PING    StringCode = "PING"
35
+	PONG    StringCode = "PONG"
36
+	QUIT    StringCode = "QUIT"
37
+	ERROR   StringCode = "ERROR"
38
+	INVITE  StringCode = "INVITE"
39
+
26 40
 	// numeric codes
27
-	RPL_WELCOME           Numeric = 1
28
-	RPL_YOURHOST          Numeric = 2
29
-	RPL_CREATED           Numeric = 3
30
-	RPL_MYINFO            Numeric = 4
31
-	RPL_BOUNCE            Numeric = 5
32
-	RPL_TRACELINK         Numeric = 200
33
-	RPL_TRACECONNECTING   Numeric = 201
34
-	RPL_TRACEHANDSHAKE    Numeric = 202
35
-	RPL_TRACEUNKNOWN      Numeric = 203
36
-	RPL_TRACEOPERATOR     Numeric = 204
37
-	RPL_TRACEUSER         Numeric = 205
38
-	RPL_TRACESERVER       Numeric = 206
39
-	RPL_TRACESERVICE      Numeric = 207
40
-	RPL_TRACENEWTYPE      Numeric = 208
41
-	RPL_TRACECLASS        Numeric = 209
42
-	RPL_TRACERECONNECT    Numeric = 210
43
-	RPL_STATSLINKINFO     Numeric = 211
44
-	RPL_STATSCOMMANDS     Numeric = 212
45
-	RPL_ENDOFSTATS        Numeric = 219
46
-	RPL_UMODEIS           Numeric = 221
47
-	RPL_SERVLIST          Numeric = 234
48
-	RPL_SERVLISTEND       Numeric = 235
49
-	RPL_STATSUPTIME       Numeric = 242
50
-	RPL_STATSOLINE        Numeric = 243
51
-	RPL_LUSERCLIENT       Numeric = 251
52
-	RPL_LUSEROP           Numeric = 252
53
-	RPL_LUSERUNKNOWN      Numeric = 253
54
-	RPL_LUSERCHANNELS     Numeric = 254
55
-	RPL_LUSERME           Numeric = 255
56
-	RPL_ADMINME           Numeric = 256
57
-	RPL_ADMINLOC1         Numeric = 257
58
-	RPL_ADMINLOC2         Numeric = 258
59
-	RPL_ADMINEMAIL        Numeric = 259
60
-	RPL_TRACELOG          Numeric = 261
61
-	RPL_TRACEEND          Numeric = 262
62
-	RPL_TRYAGAIN          Numeric = 263
63
-	RPL_AWAY              Numeric = 301
64
-	RPL_USERHOST          Numeric = 302
65
-	RPL_ISON              Numeric = 303
66
-	RPL_UNAWAY            Numeric = 305
67
-	RPL_NOWAWAY           Numeric = 306
68
-	RPL_WHOISUSER         Numeric = 311
69
-	RPL_WHOISSERVER       Numeric = 312
70
-	RPL_WHOISOPERATOR     Numeric = 313
71
-	RPL_WHOWASUSER        Numeric = 314
72
-	RPL_ENDOFWHO          Numeric = 315
73
-	RPL_WHOISIDLE         Numeric = 317
74
-	RPL_ENDOFWHOIS        Numeric = 318
75
-	RPL_WHOISCHANNELS     Numeric = 319
76
-	RPL_LIST              Numeric = 322
77
-	RPL_LISTEND           Numeric = 323
78
-	RPL_CHANNELMODEIS     Numeric = 324
79
-	RPL_UNIQOPIS          Numeric = 325
80
-	RPL_NOTOPIC           Numeric = 331
81
-	RPL_TOPIC             Numeric = 332
82
-	RPL_INVITING          Numeric = 341
83
-	RPL_SUMMONING         Numeric = 342
84
-	RPL_INVITELIST        Numeric = 346
85
-	RPL_ENDOFINVITELIST   Numeric = 347
86
-	RPL_EXCEPTLIST        Numeric = 348
87
-	RPL_ENDOFEXCEPTLIST   Numeric = 349
88
-	RPL_VERSION           Numeric = 351
89
-	RPL_WHOREPLY          Numeric = 352
90
-	RPL_NAMREPLY          Numeric = 353
91
-	RPL_LINKS             Numeric = 364
92
-	RPL_ENDOFLINKS        Numeric = 365
93
-	RPL_ENDOFNAMES        Numeric = 366
94
-	RPL_BANLIST           Numeric = 367
95
-	RPL_ENDOFBANLIST      Numeric = 368
96
-	RPL_ENDOFWHOWAS       Numeric = 369
97
-	RPL_INFO              Numeric = 371
98
-	RPL_MOTD              Numeric = 372
99
-	RPL_ENDOFINFO         Numeric = 374
100
-	RPL_MOTDSTART         Numeric = 375
101
-	RPL_ENDOFMOTD         Numeric = 376
102
-	RPL_YOUREOPER         Numeric = 381
103
-	RPL_REHASHING         Numeric = 382
104
-	RPL_YOURESERVICE      Numeric = 383
105
-	RPL_TIME              Numeric = 391
106
-	RPL_USERSSTART        Numeric = 392
107
-	RPL_USERS             Numeric = 393
108
-	RPL_ENDOFUSERS        Numeric = 394
109
-	RPL_NOUSERS           Numeric = 395
110
-	ERR_NOSUCHNICK        Numeric = 401
111
-	ERR_NOSUCHSERVER      Numeric = 402
112
-	ERR_NOSUCHCHANNEL     Numeric = 403
113
-	ERR_CANNOTSENDTOCHAN  Numeric = 404
114
-	ERR_TOOMANYCHANNELS   Numeric = 405
115
-	ERR_WASNOSUCHNICK     Numeric = 406
116
-	ERR_TOOMANYTARGETS    Numeric = 407
117
-	ERR_NOSUCHSERVICE     Numeric = 408
118
-	ERR_NOORIGIN          Numeric = 409
119
-	ERR_NORECIPIENT       Numeric = 411
120
-	ERR_NOTEXTTOSEND      Numeric = 412
121
-	ERR_NOTOPLEVEL        Numeric = 413
122
-	ERR_WILDTOPLEVEL      Numeric = 414
123
-	ERR_BADMASK           Numeric = 415
124
-	ERR_UNKNOWNCOMMAND    Numeric = 421
125
-	ERR_NOMOTD            Numeric = 422
126
-	ERR_NOADMININFO       Numeric = 423
127
-	ERR_FILEERROR         Numeric = 424
128
-	ERR_NONICKNAMEGIVEN   Numeric = 431
129
-	ERR_ERRONEUSNICKNAME  Numeric = 432
130
-	ERR_NICKNAMEINUSE     Numeric = 433
131
-	ERR_NICKCOLLISION     Numeric = 436
132
-	ERR_UNAVAILRESOURCE   Numeric = 437
133
-	ERR_USERNOTINCHANNEL  Numeric = 441
134
-	ERR_NOTONCHANNEL      Numeric = 442
135
-	ERR_USERONCHANNEL     Numeric = 443
136
-	ERR_NOLOGIN           Numeric = 444
137
-	ERR_SUMMONDISABLED    Numeric = 445
138
-	ERR_USERSDISABLED     Numeric = 446
139
-	ERR_NOTREGISTERED     Numeric = 451
140
-	ERR_NEEDMOREPARAMS    Numeric = 461
141
-	ERR_ALREADYREGISTRED  Numeric = 462
142
-	ERR_NOPERMFORHOST     Numeric = 463
143
-	ERR_PASSWDMISMATCH    Numeric = 464
144
-	ERR_YOUREBANNEDCREEP  Numeric = 465
145
-	ERR_YOUWILLBEBANNED   Numeric = 466
146
-	ERR_KEYSET            Numeric = 467
147
-	ERR_CHANNELISFULL     Numeric = 471
148
-	ERR_UNKNOWNMODE       Numeric = 472
149
-	ERR_INVITEONLYCHAN    Numeric = 473
150
-	ERR_BANNEDFROMCHAN    Numeric = 474
151
-	ERR_BADCHANNELKEY     Numeric = 475
152
-	ERR_BADCHANMASK       Numeric = 476
153
-	ERR_NOCHANMODES       Numeric = 477
154
-	ERR_BANLISTFULL       Numeric = 478
155
-	ERR_NOPRIVILEGES      Numeric = 481
156
-	ERR_CHANOPRIVSNEEDED  Numeric = 482
157
-	ERR_CANTKILLSERVER    Numeric = 483
158
-	ERR_RESTRICTED        Numeric = 484
159
-	ERR_UNIQOPPRIVSNEEDED Numeric = 485
160
-	ERR_NOOPERHOST        Numeric = 491
161
-	ERR_UMODEUNKNOWNFLAG  Numeric = 501
162
-	ERR_USERSDONTMATCH    Numeric = 502
41
+	RPL_WELCOME           NumericCode = 1
42
+	RPL_YOURHOST          NumericCode = 2
43
+	RPL_CREATED           NumericCode = 3
44
+	RPL_MYINFO            NumericCode = 4
45
+	RPL_BOUNCE            NumericCode = 5
46
+	RPL_TRACELINK         NumericCode = 200
47
+	RPL_TRACECONNECTING   NumericCode = 201
48
+	RPL_TRACEHANDSHAKE    NumericCode = 202
49
+	RPL_TRACEUNKNOWN      NumericCode = 203
50
+	RPL_TRACEOPERATOR     NumericCode = 204
51
+	RPL_TRACEUSER         NumericCode = 205
52
+	RPL_TRACESERVER       NumericCode = 206
53
+	RPL_TRACESERVICE      NumericCode = 207
54
+	RPL_TRACENEWTYPE      NumericCode = 208
55
+	RPL_TRACECLASS        NumericCode = 209
56
+	RPL_TRACERECONNECT    NumericCode = 210
57
+	RPL_STATSLINKINFO     NumericCode = 211
58
+	RPL_STATSCOMMANDS     NumericCode = 212
59
+	RPL_ENDOFSTATS        NumericCode = 219
60
+	RPL_UMODEIS           NumericCode = 221
61
+	RPL_SERVLIST          NumericCode = 234
62
+	RPL_SERVLISTEND       NumericCode = 235
63
+	RPL_STATSUPTIME       NumericCode = 242
64
+	RPL_STATSOLINE        NumericCode = 243
65
+	RPL_LUSERCLIENT       NumericCode = 251
66
+	RPL_LUSEROP           NumericCode = 252
67
+	RPL_LUSERUNKNOWN      NumericCode = 253
68
+	RPL_LUSERCHANNELS     NumericCode = 254
69
+	RPL_LUSERME           NumericCode = 255
70
+	RPL_ADMINME           NumericCode = 256
71
+	RPL_ADMINLOC1         NumericCode = 257
72
+	RPL_ADMINLOC2         NumericCode = 258
73
+	RPL_ADMINEMAIL        NumericCode = 259
74
+	RPL_TRACELOG          NumericCode = 261
75
+	RPL_TRACEEND          NumericCode = 262
76
+	RPL_TRYAGAIN          NumericCode = 263
77
+	RPL_AWAY              NumericCode = 301
78
+	RPL_USERHOST          NumericCode = 302
79
+	RPL_ISON              NumericCode = 303
80
+	RPL_UNAWAY            NumericCode = 305
81
+	RPL_NOWAWAY           NumericCode = 306
82
+	RPL_WHOISUSER         NumericCode = 311
83
+	RPL_WHOISSERVER       NumericCode = 312
84
+	RPL_WHOISOPERATOR     NumericCode = 313
85
+	RPL_WHOWASUSER        NumericCode = 314
86
+	RPL_ENDOFWHO          NumericCode = 315
87
+	RPL_WHOISIDLE         NumericCode = 317
88
+	RPL_ENDOFWHOIS        NumericCode = 318
89
+	RPL_WHOISCHANNELS     NumericCode = 319
90
+	RPL_LIST              NumericCode = 322
91
+	RPL_LISTEND           NumericCode = 323
92
+	RPL_CHANNELMODEIS     NumericCode = 324
93
+	RPL_UNIQOPIS          NumericCode = 325
94
+	RPL_NOTOPIC           NumericCode = 331
95
+	RPL_TOPIC             NumericCode = 332
96
+	RPL_INVITING          NumericCode = 341
97
+	RPL_SUMMONING         NumericCode = 342
98
+	RPL_INVITELIST        NumericCode = 346
99
+	RPL_ENDOFINVITELIST   NumericCode = 347
100
+	RPL_EXCEPTLIST        NumericCode = 348
101
+	RPL_ENDOFEXCEPTLIST   NumericCode = 349
102
+	RPL_VERSION           NumericCode = 351
103
+	RPL_WHOREPLY          NumericCode = 352
104
+	RPL_NAMREPLY          NumericCode = 353
105
+	RPL_LINKS             NumericCode = 364
106
+	RPL_ENDOFLINKS        NumericCode = 365
107
+	RPL_ENDOFNAMES        NumericCode = 366
108
+	RPL_BANLIST           NumericCode = 367
109
+	RPL_ENDOFBANLIST      NumericCode = 368
110
+	RPL_ENDOFWHOWAS       NumericCode = 369
111
+	RPL_INFO              NumericCode = 371
112
+	RPL_MOTD              NumericCode = 372
113
+	RPL_ENDOFINFO         NumericCode = 374
114
+	RPL_MOTDSTART         NumericCode = 375
115
+	RPL_ENDOFMOTD         NumericCode = 376
116
+	RPL_YOUREOPER         NumericCode = 381
117
+	RPL_REHASHING         NumericCode = 382
118
+	RPL_YOURESERVICE      NumericCode = 383
119
+	RPL_TIME              NumericCode = 391
120
+	RPL_USERSSTART        NumericCode = 392
121
+	RPL_USERS             NumericCode = 393
122
+	RPL_ENDOFUSERS        NumericCode = 394
123
+	RPL_NOUSERS           NumericCode = 395
124
+	ERR_NOSUCHNICK        NumericCode = 401
125
+	ERR_NOSUCHSERVER      NumericCode = 402
126
+	ERR_NOSUCHCHANNEL     NumericCode = 403
127
+	ERR_CANNOTSENDTOCHAN  NumericCode = 404
128
+	ERR_TOOMANYCHANNELS   NumericCode = 405
129
+	ERR_WASNOSUCHNICK     NumericCode = 406
130
+	ERR_TOOMANYTARGETS    NumericCode = 407
131
+	ERR_NOSUCHSERVICE     NumericCode = 408
132
+	ERR_NOORIGIN          NumericCode = 409
133
+	ERR_NORECIPIENT       NumericCode = 411
134
+	ERR_NOTEXTTOSEND      NumericCode = 412
135
+	ERR_NOTOPLEVEL        NumericCode = 413
136
+	ERR_WILDTOPLEVEL      NumericCode = 414
137
+	ERR_BADMASK           NumericCode = 415
138
+	ERR_UNKNOWNCOMMAND    NumericCode = 421
139
+	ERR_NOMOTD            NumericCode = 422
140
+	ERR_NOADMININFO       NumericCode = 423
141
+	ERR_FILEERROR         NumericCode = 424
142
+	ERR_NONICKNAMEGIVEN   NumericCode = 431
143
+	ERR_ERRONEUSNICKNAME  NumericCode = 432
144
+	ERR_NICKNAMEINUSE     NumericCode = 433
145
+	ERR_NICKCOLLISION     NumericCode = 436
146
+	ERR_UNAVAILRESOURCE   NumericCode = 437
147
+	ERR_USERNOTINCHANNEL  NumericCode = 441
148
+	ERR_NOTONCHANNEL      NumericCode = 442
149
+	ERR_USERONCHANNEL     NumericCode = 443
150
+	ERR_NOLOGIN           NumericCode = 444
151
+	ERR_SUMMONDISABLED    NumericCode = 445
152
+	ERR_USERSDISABLED     NumericCode = 446
153
+	ERR_NOTREGISTERED     NumericCode = 451
154
+	ERR_NEEDMOREPARAMS    NumericCode = 461
155
+	ERR_ALREADYREGISTRED  NumericCode = 462
156
+	ERR_NOPERMFORHOST     NumericCode = 463
157
+	ERR_PASSWDMISMATCH    NumericCode = 464
158
+	ERR_YOUREBANNEDCREEP  NumericCode = 465
159
+	ERR_YOUWILLBEBANNED   NumericCode = 466
160
+	ERR_KEYSET            NumericCode = 467
161
+	ERR_CHANNELISFULL     NumericCode = 471
162
+	ERR_UNKNOWNMODE       NumericCode = 472
163
+	ERR_INVITEONLYCHAN    NumericCode = 473
164
+	ERR_BANNEDFROMCHAN    NumericCode = 474
165
+	ERR_BADCHANNELKEY     NumericCode = 475
166
+	ERR_BADCHANMASK       NumericCode = 476
167
+	ERR_NOCHANMODES       NumericCode = 477
168
+	ERR_BANLISTFULL       NumericCode = 478
169
+	ERR_NOPRIVILEGES      NumericCode = 481
170
+	ERR_CHANOPRIVSNEEDED  NumericCode = 482
171
+	ERR_CANTKILLSERVER    NumericCode = 483
172
+	ERR_RESTRICTED        NumericCode = 484
173
+	ERR_UNIQOPPRIVSNEEDED NumericCode = 485
174
+	ERR_NOOPERHOST        NumericCode = 491
175
+	ERR_UMODEUNKNOWNFLAG  NumericCode = 501
176
+	ERR_USERSDONTMATCH    NumericCode = 502
163 177
 
164 178
 	Add    ModeOp = '+'
165 179
 	List   ModeOp = '='

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

@@ -15,11 +15,16 @@ func joinedLen(names []string) int {
15 15
 }
16 16
 
17 17
 type BaseReply struct {
18
+	code    ReplyCode
18 19
 	id      string
19 20
 	message string
20 21
 	source  Identifier
21 22
 }
22 23
 
24
+func (reply *BaseReply) Code() ReplyCode {
25
+	return reply.code
26
+}
27
+
23 28
 func (reply *BaseReply) SetSource(source Identifier) {
24 29
 	reply.id = source.Id()
25 30
 	reply.source = source
@@ -31,16 +36,14 @@ func (reply *BaseReply) Source() Identifier {
31 36
 
32 37
 type StringReply struct {
33 38
 	BaseReply
34
-	code string
35 39
 }
36 40
 
37
-func NewStringReply(source Identifier, code string,
41
+func NewStringReply(source Identifier, code StringCode,
38 42
 	format string, args ...interface{}) *StringReply {
39
-	reply := &StringReply{
40
-		code: code,
41
-	}
42
-	reply.SetSource(source)
43
+	reply := &StringReply{}
44
+	reply.code = code
43 45
 	reply.message = fmt.Sprintf(format, args...)
46
+	reply.SetSource(source)
44 47
 	return reply
45 48
 }
46 49
 
@@ -57,16 +60,14 @@ func (reply *StringReply) String() string {
57 60
 
58 61
 type NumericReply struct {
59 62
 	BaseReply
60
-	code Numeric
61 63
 }
62 64
 
63
-func NewNumericReply(source Identifier, code Numeric, format string,
65
+func NewNumericReply(source Identifier, code NumericCode, format string,
64 66
 	args ...interface{}) *NumericReply {
65
-	reply := &NumericReply{
66
-		code: code,
67
-	}
68
-	reply.SetSource(source)
67
+	reply := &NumericReply{}
68
+	reply.code = code
69 69
 	reply.message = fmt.Sprintf(format, args...)
70
+	reply.SetSource(source)
70 71
 	return reply
71 72
 }
72 73
 
@@ -92,7 +93,7 @@ func NewNamesReply(channel *Channel) Reply {
92 93
 	reply := &NamesReply{
93 94
 		channel: channel,
94 95
 	}
95
-	reply.SetSource(channel)
96
+	reply.SetSource(channel.server)
96 97
 	return reply
97 98
 }
98 99
 
@@ -107,14 +108,16 @@ func (reply *NamesReply) Format(client *Client) []string {
107 108
 	nicks := reply.channel.Nicks()
108 109
 	for to < len(nicks) {
109 110
 		if (from < (to - 1)) && tooLong(nicks[from:to]) {
110
-			lines = append(lines, RplNamReply(reply.channel, nicks[from:to-1]).Format(client)...)
111
+			lines = append(lines, RplNamReply(reply.channel,
112
+				nicks[from:to-1]).Format(client)...)
111 113
 			from, to = to-1, to
112 114
 		} else {
113 115
 			to += 1
114 116
 		}
115 117
 	}
116 118
 	if from < len(nicks) {
117
-		lines = append(lines, RplNamReply(reply.channel, nicks[from:]).Format(client)...)
119
+		lines = append(lines, RplNamReply(reply.channel,
120
+			nicks[from:]).Format(client)...)
118 121
 	}
119 122
 	lines = append(lines, RplEndOfNames(reply.channel).Format(client)...)
120 123
 	return lines
@@ -128,56 +131,56 @@ func (reply *NamesReply) String() string {
128 131
 // messaging replies
129 132
 
130 133
 func RplPrivMsg(source Identifier, target Identifier, message string) Reply {
131
-	return NewStringReply(source, "PRIVMSG", "%s :%s", target.Nick(), message)
134
+	return NewStringReply(source, PRIVMSG, "%s :%s", target.Nick(), message)
132 135
 }
133 136
 
134 137
 func RplNotice(source Identifier, target Identifier, message string) Reply {
135
-	return NewStringReply(source, "NOTICE", "%s :%s", target.Nick(), message)
138
+	return NewStringReply(source, NOTICE, "%s :%s", target.Nick(), message)
136 139
 }
137 140
 
138 141
 func RplNick(source Identifier, newNick string) Reply {
139
-	return NewStringReply(source, "NICK", newNick)
142
+	return NewStringReply(source, NICK, newNick)
140 143
 }
141 144
 
142 145
 func RplJoin(client *Client, channel *Channel) Reply {
143
-	return NewStringReply(client, "JOIN", channel.name)
146
+	return NewStringReply(client, JOIN, channel.name)
144 147
 }
145 148
 
146 149
 func RplPart(client *Client, channel *Channel, message string) Reply {
147
-	return NewStringReply(client, "PART", "%s :%s", channel, message)
150
+	return NewStringReply(client, PART, "%s :%s", channel, message)
148 151
 }
149 152
 
150 153
 func RplMode(client *Client, changes ModeChanges) Reply {
151
-	return NewStringReply(client, "MODE", "%s :%s", client.Nick(), changes)
154
+	return NewStringReply(client, MODE, "%s :%s", client.Nick(), changes)
152 155
 }
153 156
 
154 157
 func RplChannelMode(client *Client, channel *Channel,
155 158
 	changes ChannelModeChanges) Reply {
156
-	return NewStringReply(client, "MODE", "%s %s", channel, changes)
159
+	return NewStringReply(client, MODE, "%s %s", channel, changes)
157 160
 }
158 161
 
159 162
 func RplTopicMsg(source Identifier, channel *Channel) Reply {
160
-	return NewStringReply(source, "TOPIC", "%s :%s", channel, channel.topic)
163
+	return NewStringReply(source, TOPIC, "%s :%s", channel, channel.topic)
161 164
 }
162 165
 
163 166
 func RplPing(server *Server, target Identifier) Reply {
164
-	return NewStringReply(server, "PING", target.Nick())
167
+	return NewStringReply(server, PING, target.Nick())
165 168
 }
166 169
 
167 170
 func RplPong(server *Server, client *Client) Reply {
168
-	return NewStringReply(server, "PONG", client.Nick())
171
+	return NewStringReply(server, PONG, client.Nick())
169 172
 }
170 173
 
171 174
 func RplQuit(client *Client, message string) Reply {
172
-	return NewStringReply(client, "QUIT", ":%s", message)
175
+	return NewStringReply(client, QUIT, ":%s", message)
173 176
 }
174 177
 
175 178
 func RplError(server *Server, target Identifier) Reply {
176
-	return NewStringReply(server, "ERROR", target.Nick())
179
+	return NewStringReply(server, ERROR, target.Nick())
177 180
 }
178 181
 
179 182
 func RplInviteMsg(channel *Channel, inviter *Client) Reply {
180
-	return NewStringReply(inviter, "INVITE", channel.name)
183
+	return NewStringReply(inviter, INVITE, channel.name)
181 184
 }
182 185
 
183 186
 // numeric replies

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

@@ -9,20 +9,18 @@ import (
9 9
 	"log"
10 10
 	"net"
11 11
 	"os"
12
-	"sync"
13 12
 	"time"
14 13
 )
15 14
 
16 15
 type Server struct {
17 16
 	channels  ChannelNameMap
17
+	clients   ClientNameMap
18 18
 	commands  chan Command
19 19
 	ctime     time.Time
20 20
 	motdFile  string
21
-	mutex     *sync.Mutex
22 21
 	name      string
23 22
 	operators map[string]string
24 23
 	password  string
25
-	clients   ClientNameMap
26 24
 }
27 25
 
28 26
 func NewServer(config *Config) *Server {
@@ -32,7 +30,6 @@ func NewServer(config *Config) *Server {
32 30
 		commands:  make(chan Command),
33 31
 		ctime:     time.Now(),
34 32
 		motdFile:  config.MOTD,
35
-		mutex:     &sync.Mutex{},
36 33
 		name:      config.Name,
37 34
 		operators: make(map[string]string),
38 35
 		password:  config.Password,
@@ -43,7 +40,6 @@ func NewServer(config *Config) *Server {
43 40
 	}
44 41
 
45 42
 	go server.receiveCommands()
46
-
47 43
 	for _, listenerConf := range config.Listeners {
48 44
 		go server.listen(listenerConf)
49 45
 	}
@@ -51,12 +47,6 @@ func NewServer(config *Config) *Server {
51 47
 	return server
52 48
 }
53 49
 
54
-func (server *Server) withMutex(f func()) {
55
-	server.mutex.Lock()
56
-	defer server.mutex.Unlock()
57
-	f()
58
-}
59
-
60 50
 func (server *Server) receiveCommands() {
61 51
 	for command := range server.commands {
62 52
 		if DEBUG_SERVER {
@@ -145,9 +135,7 @@ func (s *Server) GetOrMakeChannel(name string) *Channel {
145 135
 
146 136
 	if !ok {
147 137
 		channel = NewChannel(s, name)
148
-		s.withMutex(func() {
149
-			s.channels[name] = channel
150
-		})
138
+		s.channels[name] = channel
151 139
 	}
152 140
 
153 141
 	return channel
@@ -316,17 +304,9 @@ func (m *NickCommand) HandleServer(s *Server) {
316 304
 		return
317 305
 	}
318 306
 
319
-	// Make reply before changing nick.
320
-	reply := RplNick(c, m.nickname)
321
-
322 307
 	s.clients.Remove(c)
323
-	c.nick = m.nickname
308
+	c.commands <- m
324 309
 	s.clients.Add(c)
325
-
326
-	iclients := c.InterestedClients()
327
-	for iclient := range iclients {
328
-		iclient.Reply(reply)
329
-	}
330 310
 }
331 311
 
332 312
 func (m *UserMsgCommand) HandleServer(s *Server) {
@@ -335,24 +315,10 @@ func (m *UserMsgCommand) HandleServer(s *Server) {
335 315
 
336 316
 func (m *QuitCommand) HandleServer(server *Server) {
337 317
 	client := m.Client()
338
-	iclients := client.InterestedClients()
339
-	iclients.Remove(client)
340 318
 
341
-	for channel := range client.channels {
342
-		channel.withMutex(func() {
343
-			channel.members.Remove(client)
344
-		})
345
-	}
346
-
347
-	client.Reply(RplError(server, client))
348
-	client.Destroy()
319
+	server.clients.Remove(client)
349 320
 
350
-	if len(iclients) > 0 {
351
-		reply := RplQuit(client, m.message)
352
-		for iclient := range iclients {
353
-			iclient.Reply(reply)
354
-		}
355
-	}
321
+	client.commands <- m
356 322
 }
357 323
 
358 324
 func (m *JoinCommand) HandleServer(s *Server) {
@@ -374,9 +340,7 @@ func (m *JoinCommand) HandleServer(s *Server) {
374 340
 
375 341
 func (m *PartCommand) HandleServer(server *Server) {
376 342
 	for _, chname := range m.channels {
377
-		server.mutex.Lock()
378 343
 		channel := server.channels[chname]
379
-		server.mutex.Unlock()
380 344
 
381 345
 		if channel == nil {
382 346
 			m.Client().Reply(ErrNoSuchChannel(server, chname))
@@ -567,3 +531,11 @@ func (msg *NoticeCommand) HandleServer(server *Server) {
567 531
 	}
568 532
 	target.Reply(RplPrivMsg(msg.Client(), target, msg.message))
569 533
 }
534
+
535
+func (msg *DestroyChannel) HandleServer(server *Server) {
536
+	server.channels.Remove(msg.channel)
537
+}
538
+
539
+func (msg *DestroyClient) HandleServer(server *Server) {
540
+	server.clients.Remove(msg.client)
541
+}

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

@@ -28,9 +28,19 @@ func (mode UserMode) String() string {
28 28
 
29 29
 type Phase uint
30 30
 
31
-type Numeric uint
31
+type ReplyCode interface {
32
+	String() string
33
+}
34
+
35
+type StringCode string
36
+
37
+func (code StringCode) String() string {
38
+	return string(code)
39
+}
40
+
41
+type NumericCode uint
32 42
 
33
-func (code Numeric) String() string {
43
+func (code NumericCode) String() string {
34 44
 	return fmt.Sprintf("%03d", code)
35 45
 }
36 46
 
@@ -87,29 +97,43 @@ func (clients ClientNameMap) Remove(client *Client) error {
87 97
 
88 98
 type ChannelModeSet map[ChannelMode]bool
89 99
 
90
-type ClientSet map[*Client]ChannelModeSet
100
+type ClientSet map[*Client]bool
91 101
 
92 102
 func (clients ClientSet) Add(client *Client) {
93
-	clients[client] = make(ChannelModeSet)
103
+	clients[client] = true
94 104
 }
95 105
 
96 106
 func (clients ClientSet) Remove(client *Client) {
97 107
 	delete(clients, client)
98 108
 }
99 109
 
100
-func (clients ClientSet) HasMode(client *Client, mode ChannelMode) bool {
101
-	modes, ok := clients[client]
110
+func (clients ClientSet) Has(client *Client) bool {
111
+	return clients[client]
112
+}
113
+
114
+type MemberSet map[*Client]ChannelModeSet
115
+
116
+func (members MemberSet) Add(member *Client) {
117
+	members[member] = make(ChannelModeSet)
118
+}
119
+
120
+func (members MemberSet) Remove(member *Client) {
121
+	delete(members, member)
122
+}
123
+
124
+func (members MemberSet) Has(member *Client) bool {
125
+	_, ok := members[member]
126
+	return ok
127
+}
128
+
129
+func (members MemberSet) HasMode(member *Client, mode ChannelMode) bool {
130
+	modes, ok := members[member]
102 131
 	if !ok {
103 132
 		return false
104 133
 	}
105 134
 	return modes[mode]
106 135
 }
107 136
 
108
-func (clients ClientSet) Has(client *Client) bool {
109
-	_, ok := clients[client]
110
-	return ok
111
-}
112
-
113 137
 type ChannelSet map[*Channel]bool
114 138
 
115 139
 func (channels ChannelSet) Add(channel *Channel) {
@@ -141,6 +165,7 @@ type Replier interface {
141 165
 }
142 166
 
143 167
 type Reply interface {
168
+	Code() ReplyCode
144 169
 	Format(*Client) []string
145 170
 	Source() Identifier
146 171
 }
@@ -172,6 +197,10 @@ type ChannelCommand interface {
172 197
 	HandleChannel(channel *Channel)
173 198
 }
174 199
 
200
+type ClientCommand interface {
201
+	HandleClient(client *Client)
202
+}
203
+
175 204
 //
176 205
 // structs
177 206
 //

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