Browse Source

rudimentary (broken) invite support

tags/v0.1.0
Jeremy Latt 11 years ago
parent
commit
d7d6263e35
5 changed files with 244 additions and 46 deletions
  1. 17
    0
      src/irc/channel.go
  2. 46
    0
      src/irc/commands.go
  3. 129
    15
      src/irc/constants.go
  4. 2
    13
      src/irc/parse.go
  5. 50
    18
      src/irc/reply.go

+ 17
- 0
src/irc/channel.go View File

@@ -119,3 +119,20 @@ func (ch *Channel) ChangeTopic(cl *Client, newTopic string) {
119 119
 		ch.Send(RplNoTopic(ch), nil)
120 120
 	}
121 121
 }
122
+
123
+func (ch *Channel) Invite(inviter *Client, invitee *Client) {
124
+	if !ch.members[inviter] {
125
+		inviter.send <- ErrNotOnChannel(ch)
126
+		return
127
+	}
128
+
129
+	if ch.members[invitee] {
130
+		inviter.send <- ErrUserOnChannel(ch, invitee)
131
+		return
132
+	}
133
+
134
+	ch.invites[invitee.nick] = true
135
+
136
+	invitee.send <- RplInviteMsg(ch, inviter)
137
+	inviter.send <- RplInvitingMsg(ch, invitee)
138
+}

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

@@ -15,6 +15,19 @@ type Message interface {
15 15
 var (
16 16
 	ErrNotEnoughArgs    = errors.New("not enough arguments")
17 17
 	ErrUModeUnknownFlag = errors.New("unknown umode flag")
18
+	parseCommandFuncs   = map[string]ParseFunc{
19
+		"INVITE":  NewInviteMessage,
20
+		"JOIN":    NewJoinMessage,
21
+		"MODE":    NewModeMessage,
22
+		"NICK":    NewNickMessage,
23
+		"PART":    NewPartMessage,
24
+		"PING":    NewPingMessage,
25
+		"PONG":    NewPongMessage,
26
+		"PRIVMSG": NewPrivMsgMessage,
27
+		"QUIT":    NewQuitMessage,
28
+		"TOPIC":   NewTopicMessage,
29
+		"USER":    NewUserMessage,
30
+	}
18 31
 )
19 32
 
20 33
 // unknown
@@ -319,3 +332,36 @@ func (m *TopicMessage) Handle(s *Server, c *Client) {
319 332
 		channel.ChangeTopic(c, m.topic)
320 333
 	}
321 334
 }
335
+
336
+// INVITE <nickname> <channel>
337
+
338
+type InviteMessage struct {
339
+	nickname string
340
+	channel  string
341
+}
342
+
343
+func NewInviteMessage(args []string) (Message, error) {
344
+	if len(args) < 2 {
345
+		return nil, ErrNotEnoughArgs
346
+	}
347
+	return &InviteMessage{
348
+		nickname: args[0],
349
+		channel:  args[1],
350
+	}, nil
351
+}
352
+
353
+func (m *InviteMessage) Handle(s *Server, c *Client) {
354
+	channel := s.channels[m.channel]
355
+	if channel == nil {
356
+		c.send <- ErrNoSuchNick(s, m.channel)
357
+		return
358
+	}
359
+
360
+	invitee := s.nicks[m.nickname]
361
+	if invitee == nil {
362
+		c.send <- ErrNoSuchNick(s, m.nickname)
363
+		return
364
+	}
365
+
366
+	channel.Invite(c, invitee)
367
+}

+ 129
- 15
src/irc/constants.go View File

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

+ 2
- 13
src/irc/parse.go View File

@@ -5,18 +5,7 @@ import (
5 5
 	"strings"
6 6
 )
7 7
 
8
-var commands = map[string]func([]string) (Message, error){
9
-	"JOIN":    NewJoinMessage,
10
-	"MODE":    NewModeMessage,
11
-	"NICK":    NewNickMessage,
12
-	"PART":    NewPartMessage,
13
-	"PING":    NewPingMessage,
14
-	"PONG":    NewPongMessage,
15
-	"PRIVMSG": NewPrivMsgMessage,
16
-	"QUIT":    NewQuitMessage,
17
-	"TOPIC":   NewTopicMessage,
18
-	"USER":    NewUserMessage,
19
-}
8
+type ParseFunc func([]string) (Message, error)
20 9
 
21 10
 var (
22 11
 	ErrParseMessage = errors.New("failed to parse message")
@@ -24,7 +13,7 @@ var (
24 13
 
25 14
 func ParseMessage(line string) (msg Message, err error) {
26 15
 	command, args := parseLine(line)
27
-	constructor, ok := commands[command]
16
+	constructor, ok := parseCommandFuncs[command]
28 17
 	if !ok {
29 18
 		msg = &UnknownMessage{command}
30 19
 		return

+ 50
- 18
src/irc/reply.go View File

@@ -49,6 +49,15 @@ func RplNick(client *Client, newNick string) Reply {
49 49
 	return NewReply(client, RPL_NICK, ":"+newNick)
50 50
 }
51 51
 
52
+func RplInviteMsg(channel *Channel, inviter *Client) Reply {
53
+	return NewReply(inviter, RPL_INVITE, channel.name)
54
+}
55
+
56
+func RplInvitingMsg(channel *Channel, invitee *Client) Reply {
57
+	return NewReply(channel.server, RPL_INVITING,
58
+		fmt.Sprintf("%s %s", channel.name, invitee.Nick()))
59
+}
60
+
52 61
 func RplPrivMsgChannel(channel *Channel, source *Client, message string) Reply {
53 62
 	return &ChannelReply{NewReply(source, RPL_PRIVMSG, ":"+message), channel}
54 63
 }
@@ -64,19 +73,23 @@ func RplPart(channel *Channel, client *Client, message string) Reply {
64 73
 // Server Info
65 74
 
66 75
 func RplWelcome(source Identifier, client *Client) Reply {
67
-	return NewReply(source, RPL_WELCOME, "Welcome to the Internet Relay Network "+client.Id())
76
+	return NewReply(source, RPL_WELCOME,
77
+		"Welcome to the Internet Relay Network "+client.Id())
68 78
 }
69 79
 
70 80
 func RplYourHost(server *Server, target *Client) Reply {
71
-	return NewReply(server, RPL_YOURHOST, fmt.Sprintf("Your host is %s, running version %s", server.hostname, VERSION))
81
+	return NewReply(server, RPL_YOURHOST,
82
+		fmt.Sprintf("Your host is %s, running version %s", server.hostname, VERSION))
72 83
 }
73 84
 
74 85
 func RplCreated(server *Server) Reply {
75
-	return NewReply(server, RPL_CREATED, "This server was created "+server.ctime.Format(time.RFC1123))
86
+	return NewReply(server, RPL_CREATED,
87
+		"This server was created "+server.ctime.Format(time.RFC1123))
76 88
 }
77 89
 
78 90
 func RplMyInfo(server *Server) Reply {
79
-	return NewReply(server, RPL_MYINFO, fmt.Sprintf("%s %s i ik", server.name, VERSION))
91
+	return NewReply(server, RPL_MYINFO,
92
+		fmt.Sprintf("%s %s i ik", server.name, VERSION))
80 93
 }
81 94
 
82 95
 func RplUModeIs(server *Server, client *Client) Reply {
@@ -86,20 +99,24 @@ func RplUModeIs(server *Server, client *Client) Reply {
86 99
 // channel operations
87 100
 
88 101
 func RplNoTopic(channel *Channel) Reply {
89
-	return &ChannelReply{NewReply(channel.server, RPL_NOTOPIC, channel.name+" :No topic is set"), channel}
102
+	return &ChannelReply{NewReply(channel.server, RPL_NOTOPIC,
103
+		channel.name+" :No topic is set"), channel}
90 104
 }
91 105
 
92 106
 func RplTopic(channel *Channel) Reply {
93
-	return &ChannelReply{NewReply(channel.server, RPL_TOPIC, fmt.Sprintf("%s :%s", channel.name, channel.topic)), channel}
107
+	return &ChannelReply{NewReply(channel.server, RPL_TOPIC,
108
+		fmt.Sprintf("%s :%s", channel.name, channel.topic)), channel}
94 109
 }
95 110
 
96 111
 func RplNamReply(channel *Channel) Reply {
97 112
 	// TODO multiple names and splitting based on message size
98
-	return NewReply(channel.server, RPL_NAMREPLY, fmt.Sprintf("= %s :%s", channel.name, strings.Join(channel.Nicks(), " ")))
113
+	return NewReply(channel.server, RPL_NAMREPLY,
114
+		fmt.Sprintf("= %s :%s", channel.name, strings.Join(channel.Nicks(), " ")))
99 115
 }
100 116
 
101 117
 func RplEndOfNames(source Identifier) Reply {
102
-	return NewReply(source, RPL_ENDOFNAMES, ":End of NAMES list")
118
+	return NewReply(source, RPL_ENDOFNAMES,
119
+		":End of NAMES list")
103 120
 }
104 121
 
105 122
 func RplPong(server *Server) Reply {
@@ -109,41 +126,56 @@ func RplPong(server *Server) Reply {
109 126
 // errors
110 127
 
111 128
 func ErrAlreadyRegistered(source Identifier) Reply {
112
-	return NewReply(source, ERR_ALREADYREGISTRED, ":You may not reregister")
129
+	return NewReply(source, ERR_ALREADYREGISTRED,
130
+		":You may not reregister")
113 131
 }
114 132
 
115 133
 func ErrNickNameInUse(source Identifier, nick string) Reply {
116
-	return NewReply(source, ERR_NICKNAMEINUSE, nick+" :Nickname is already in use")
134
+	return NewReply(source, ERR_NICKNAMEINUSE,
135
+		nick+" :Nickname is already in use")
117 136
 }
118 137
 
119 138
 func ErrUnknownCommand(source Identifier, command string) Reply {
120
-	return NewReply(source, ERR_UNKNOWNCOMMAND, command+" :Unknown command")
139
+	return NewReply(source, ERR_UNKNOWNCOMMAND,
140
+		command+" :Unknown command")
121 141
 }
122 142
 
123 143
 func ErrUsersDontMatch(source Identifier) Reply {
124
-	return NewReply(source, ERR_USERSDONTMATCH, ":Cannot change mode for other users")
144
+	return NewReply(source, ERR_USERSDONTMATCH,
145
+		":Cannot change mode for other users")
125 146
 }
126 147
 
127 148
 func ErrNeedMoreParams(source Identifier, command string) Reply {
128
-	return NewReply(source, ERR_NEEDMOREPARAMS, command+"%s :Not enough parameters")
149
+	return NewReply(source, ERR_NEEDMOREPARAMS,
150
+		command+"%s :Not enough parameters")
129 151
 }
130 152
 
131 153
 func ErrNoSuchChannel(source Identifier, channel string) Reply {
132
-	return NewReply(source, ERR_NOSUCHCHANNEL, channel+" :No such channel")
154
+	return NewReply(source, ERR_NOSUCHCHANNEL,
155
+		channel+" :No such channel")
156
+}
157
+
158
+func ErrUserOnChannel(channel *Channel, member *Client) Reply {
159
+	return NewReply(channel.server, ERR_USERONCHANNEL,
160
+		fmt.Sprintf("%s %s :is already on channel", member.nick, channel.name))
133 161
 }
134 162
 
135 163
 func ErrNotOnChannel(channel *Channel) Reply {
136
-	return NewReply(channel.server, ERR_NOTONCHANNEL, channel.name+" :You're not on that channel")
164
+	return NewReply(channel.server, ERR_NOTONCHANNEL,
165
+		channel.name+" :You're not on that channel")
137 166
 }
138 167
 
139 168
 func ErrInviteOnlyChannel(channel *Channel) Reply {
140
-	return NewReply(channel.server, ERR_INVITEONLYCHANNEL, channel.name+" :Cannot join channel (+i)")
169
+	return NewReply(channel.server, ERR_INVITEONLYCHAN,
170
+		channel.name+" :Cannot join channel (+i)")
141 171
 }
142 172
 
143 173
 func ErrBadChannelKey(channel *Channel) Reply {
144
-	return NewReply(channel.server, ERR_BADCHANNELKEY, channel.name+" :Cannot join channel (+k)")
174
+	return NewReply(channel.server, ERR_BADCHANNELKEY,
175
+		channel.name+" :Cannot join channel (+k)")
145 176
 }
146 177
 
147 178
 func ErrNoSuchNick(source Identifier, nick string) Reply {
148
-	return NewReply(source, ERR_NOSUCHNICK, nick+" :No such nick/channel")
179
+	return NewReply(source, ERR_NOSUCHNICK,
180
+		nick+" :No such nick/channel")
149 181
 }

Loading…
Cancel
Save