source: code/trunk/upstream.go@ 64

Last change on this file since 64 was 64, checked in by contact, 5 years ago

Add a -debug flag

File size: 8.1 KB
Line 
1package jounce
2
3import (
4 "crypto/tls"
5 "fmt"
6 "io"
7 "net"
8 "strconv"
9 "strings"
10 "time"
11
12 "gopkg.in/irc.v3"
13)
14
15type upstreamChannel struct {
16 Name string
17 conn *upstreamConn
18 Topic string
19 TopicWho string
20 TopicTime time.Time
21 Status channelStatus
22 modes modeSet
23 Members map[string]membership
24 complete bool
25}
26
27type upstreamConn struct {
28 upstream *Upstream
29 logger Logger
30 net net.Conn
31 irc *irc.Conn
32 srv *Server
33 user *user
34 messages chan<- *irc.Message
35 ring *Ring
36
37 serverName string
38 availableUserModes string
39 availableChannelModes string
40 channelModesWithParam string
41
42 registered bool
43 nick string
44 closed bool
45 modes modeSet
46 channels map[string]*upstreamChannel
47 history map[string]uint64
48}
49
50func connectToUpstream(u *user, upstream *Upstream) (*upstreamConn, error) {
51 logger := &prefixLogger{u.srv.Logger, fmt.Sprintf("upstream %q: ", upstream.Addr)}
52 logger.Printf("connecting to server")
53
54 netConn, err := tls.Dial("tcp", upstream.Addr, nil)
55 if err != nil {
56 return nil, fmt.Errorf("failed to dial %q: %v", upstream.Addr, err)
57 }
58
59 msgs := make(chan *irc.Message, 64)
60 uc := &upstreamConn{
61 upstream: upstream,
62 logger: logger,
63 net: netConn,
64 irc: irc.NewConn(netConn),
65 srv: u.srv,
66 user: u,
67 messages: msgs,
68 ring: NewRing(u.srv.RingCap),
69 channels: make(map[string]*upstreamChannel),
70 history: make(map[string]uint64),
71 }
72
73 go func() {
74 for msg := range msgs {
75 if uc.srv.Debug {
76 uc.logger.Printf("sent: %v", msg)
77 }
78 if err := uc.irc.WriteMessage(msg); err != nil {
79 uc.logger.Printf("failed to write message: %v", err)
80 }
81 }
82 if err := uc.net.Close(); err != nil {
83 uc.logger.Printf("failed to close connection: %v", err)
84 } else {
85 uc.logger.Printf("connection closed")
86 }
87 }()
88
89 return uc, nil
90}
91
92func (uc *upstreamConn) Close() error {
93 if uc.closed {
94 return fmt.Errorf("upstream connection already closed")
95 }
96 close(uc.messages)
97 uc.closed = true
98 return nil
99}
100
101func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
102 ch, ok := uc.channels[name]
103 if !ok {
104 return nil, fmt.Errorf("unknown channel %q", name)
105 }
106 return ch, nil
107}
108
109func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
110 switch msg.Command {
111 case "PING":
112 // TODO: handle params
113 uc.SendMessage(&irc.Message{
114 Command: "PONG",
115 Params: []string{uc.srv.Hostname},
116 })
117 return nil
118 case "MODE":
119 var name, modeStr string
120 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
121 return err
122 }
123
124 if name == msg.Prefix.Name { // user mode change
125 if name != uc.nick {
126 return fmt.Errorf("received MODE message for unknow nick %q", name)
127 }
128 return uc.modes.Apply(modeStr)
129 } else { // channel mode change
130 ch, err := uc.getChannel(name)
131 if err != nil {
132 return err
133 }
134 if err := ch.modes.Apply(modeStr); err != nil {
135 return err
136 }
137 }
138
139 uc.user.forEachDownstream(func(dc *downstreamConn) {
140 dc.SendMessage(msg)
141 })
142 case "NOTICE":
143 uc.logger.Print(msg)
144 case irc.RPL_WELCOME:
145 uc.registered = true
146 uc.logger.Printf("connection registered")
147
148 for _, ch := range uc.upstream.Channels {
149 uc.SendMessage(&irc.Message{
150 Command: "JOIN",
151 Params: []string{ch},
152 })
153 }
154 case irc.RPL_MYINFO:
155 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, &uc.availableChannelModes); err != nil {
156 return err
157 }
158 if len(msg.Params) > 5 {
159 uc.channelModesWithParam = msg.Params[5]
160 }
161 case "NICK":
162 var newNick string
163 if err := parseMessageParams(msg, &newNick); err != nil {
164 return err
165 }
166
167 if msg.Prefix.Name == uc.nick {
168 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
169 uc.nick = newNick
170 }
171
172 for _, ch := range uc.channels {
173 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
174 delete(ch.Members, msg.Prefix.Name)
175 ch.Members[newNick] = membership
176 }
177 }
178
179 uc.user.forEachDownstream(func(dc *downstreamConn) {
180 dc.SendMessage(msg)
181 })
182 case "JOIN":
183 var channels string
184 if err := parseMessageParams(msg, &channels); err != nil {
185 return err
186 }
187
188 for _, ch := range strings.Split(channels, ",") {
189 if msg.Prefix.Name == uc.nick {
190 uc.logger.Printf("joined channel %q", ch)
191 uc.channels[ch] = &upstreamChannel{
192 Name: ch,
193 conn: uc,
194 Members: make(map[string]membership),
195 }
196 } else {
197 ch, err := uc.getChannel(ch)
198 if err != nil {
199 return err
200 }
201 ch.Members[msg.Prefix.Name] = 0
202 }
203 }
204
205 uc.user.forEachDownstream(func(dc *downstreamConn) {
206 dc.SendMessage(msg)
207 })
208 case "PART":
209 var channels string
210 if err := parseMessageParams(msg, &channels); err != nil {
211 return err
212 }
213
214 for _, ch := range strings.Split(channels, ",") {
215 if msg.Prefix.Name == uc.nick {
216 uc.logger.Printf("parted channel %q", ch)
217 delete(uc.channels, ch)
218 } else {
219 ch, err := uc.getChannel(ch)
220 if err != nil {
221 return err
222 }
223 delete(ch.Members, msg.Prefix.Name)
224 }
225 }
226
227 uc.user.forEachDownstream(func(dc *downstreamConn) {
228 dc.SendMessage(msg)
229 })
230 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
231 var name, topic string
232 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
233 return err
234 }
235 ch, err := uc.getChannel(name)
236 if err != nil {
237 return err
238 }
239 if msg.Command == irc.RPL_TOPIC {
240 ch.Topic = topic
241 } else {
242 ch.Topic = ""
243 }
244 case "TOPIC":
245 var name string
246 if err := parseMessageParams(msg, nil, &name); err != nil {
247 return err
248 }
249 ch, err := uc.getChannel(name)
250 if err != nil {
251 return err
252 }
253 if len(msg.Params) > 1 {
254 ch.Topic = msg.Params[1]
255 } else {
256 ch.Topic = ""
257 }
258 case rpl_topicwhotime:
259 var name, who, timeStr string
260 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
261 return err
262 }
263 ch, err := uc.getChannel(name)
264 if err != nil {
265 return err
266 }
267 ch.TopicWho = who
268 sec, err := strconv.ParseInt(timeStr, 10, 64)
269 if err != nil {
270 return fmt.Errorf("failed to parse topic time: %v", err)
271 }
272 ch.TopicTime = time.Unix(sec, 0)
273 case irc.RPL_NAMREPLY:
274 var name, statusStr, members string
275 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
276 return err
277 }
278 ch, err := uc.getChannel(name)
279 if err != nil {
280 return err
281 }
282
283 status, err := parseChannelStatus(statusStr)
284 if err != nil {
285 return err
286 }
287 ch.Status = status
288
289 for _, s := range strings.Split(members, " ") {
290 membership, nick := parseMembershipPrefix(s)
291 ch.Members[nick] = membership
292 }
293 case irc.RPL_ENDOFNAMES:
294 var name string
295 if err := parseMessageParams(msg, nil, &name); err != nil {
296 return err
297 }
298 ch, err := uc.getChannel(name)
299 if err != nil {
300 return err
301 }
302
303 if ch.complete {
304 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
305 }
306 ch.complete = true
307
308 uc.user.forEachDownstream(func(dc *downstreamConn) {
309 forwardChannel(dc, ch)
310 })
311 case "PRIVMSG":
312 uc.ring.Produce(msg)
313 case irc.RPL_YOURHOST, irc.RPL_CREATED:
314 // Ignore
315 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
316 // Ignore
317 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
318 // Ignore
319 case rpl_localusers, rpl_globalusers:
320 // Ignore
321 case irc.RPL_STATSVLINE, irc.RPL_STATSPING, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
322 // Ignore
323 default:
324 uc.logger.Printf("unhandled upstream message: %v", msg)
325 }
326 return nil
327}
328
329func (uc *upstreamConn) register() {
330 uc.nick = uc.upstream.Nick
331 uc.SendMessage(&irc.Message{
332 Command: "NICK",
333 Params: []string{uc.upstream.Nick},
334 })
335 uc.SendMessage(&irc.Message{
336 Command: "USER",
337 Params: []string{uc.upstream.Username, "0", "*", uc.upstream.Realname},
338 })
339}
340
341func (uc *upstreamConn) readMessages() error {
342 for {
343 msg, err := uc.irc.ReadMessage()
344 if err == io.EOF {
345 break
346 } else if err != nil {
347 return fmt.Errorf("failed to read IRC command: %v", err)
348 }
349
350 if uc.srv.Debug {
351 uc.logger.Printf("received: %v", msg)
352 }
353
354 if err := uc.handleMessage(msg); err != nil {
355 uc.logger.Printf("failed to handle message %q: %v", msg, err)
356 }
357 }
358
359 return nil
360}
361
362func (uc *upstreamConn) SendMessage(msg *irc.Message) {
363 uc.messages <- msg
364}
Note: See TracBrowser for help on using the repository browser.