source: code/trunk/upstream.go@ 25

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

Mark channel info as complete on RPL_ENDOFNAMES

File size: 5.3 KB
RevLine 
[13]1package jounce
2
3import (
4 "crypto/tls"
5 "fmt"
6 "io"
7 "net"
[19]8 "strconv"
[17]9 "strings"
[19]10 "time"
[13]11
12 "gopkg.in/irc.v3"
13)
14
[19]15type upstreamChannel struct {
16 Name string
17 Topic string
18 TopicWho string
19 TopicTime time.Time
20 Status channelStatus
21 Members map[string]membership
[25]22 complete bool
[19]23}
24
[13]25type upstreamConn struct {
[19]26 upstream *Upstream
[21]27 logger Logger
[19]28 net net.Conn
29 irc *irc.Conn
30 srv *Server
[16]31
32 serverName string
33 availableUserModes string
34 availableChannelModes string
35 channelModesWithParam string
[19]36
37 registered bool
38 modes modeSet
39 channels map[string]*upstreamChannel
[13]40}
41
[19]42func (c *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
43 ch, ok := c.channels[name]
44 if !ok {
45 return nil, fmt.Errorf("unknown channel %q", name)
46 }
47 return ch, nil
48}
49
[13]50func (c *upstreamConn) handleMessage(msg *irc.Message) error {
51 switch msg.Command {
52 case "PING":
53 // TODO: handle params
54 return c.irc.WriteMessage(&irc.Message{
55 Command: "PONG",
56 Params: []string{c.srv.Hostname},
57 })
[17]58 case "MODE":
59 if len(msg.Params) < 2 {
60 return newNeedMoreParamsError(msg.Command)
61 }
62 if nick := msg.Params[0]; nick != c.upstream.Nick {
63 return fmt.Errorf("received MODE message for unknow nick %q", nick)
64 }
65 return c.modes.Apply(msg.Params[1])
[18]66 case "NOTICE":
[21]67 c.logger.Print(msg)
[14]68 case irc.RPL_WELCOME:
69 c.registered = true
[21]70 c.logger.Printf("connection registered")
[19]71
72 for _, ch := range c.upstream.Channels {
73 err := c.irc.WriteMessage(&irc.Message{
74 Command: "JOIN",
75 Params: []string{ch},
76 })
77 if err != nil {
78 return err
79 }
80 }
[16]81 case irc.RPL_MYINFO:
82 if len(msg.Params) < 5 {
83 return newNeedMoreParamsError(msg.Command)
84 }
85 c.serverName = msg.Params[1]
86 c.availableUserModes = msg.Params[3]
87 c.availableChannelModes = msg.Params[4]
88 if len(msg.Params) > 5 {
89 c.channelModesWithParam = msg.Params[5]
90 }
[19]91 case "JOIN":
92 if len(msg.Params) < 1 {
93 return newNeedMoreParamsError(msg.Command)
94 }
95 for _, ch := range strings.Split(msg.Params[0], ",") {
[21]96 c.logger.Printf("joined channel %q", ch)
[19]97 c.channels[ch] = &upstreamChannel{
98 Name: ch,
99 Members: make(map[string]membership),
100 }
101 }
102 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
103 if len(msg.Params) < 3 {
104 return newNeedMoreParamsError(msg.Command)
105 }
106 ch, err := c.getChannel(msg.Params[1])
107 if err != nil {
108 return err
109 }
110 if msg.Command == irc.RPL_TOPIC {
111 ch.Topic = msg.Params[2]
112 } else {
113 ch.Topic = ""
114 }
115 case "TOPIC":
116 if len(msg.Params) < 1 {
117 return newNeedMoreParamsError(msg.Command)
118 }
119 ch, err := c.getChannel(msg.Params[0])
120 if err != nil {
121 return err
122 }
123 if len(msg.Params) > 1 {
124 ch.Topic = msg.Params[1]
125 } else {
126 ch.Topic = ""
127 }
128 case rpl_topicwhotime:
129 if len(msg.Params) < 4 {
130 return newNeedMoreParamsError(msg.Command)
131 }
132 ch, err := c.getChannel(msg.Params[1])
133 if err != nil {
134 return err
135 }
136 ch.TopicWho = msg.Params[2]
137 sec, err := strconv.ParseInt(msg.Params[3], 10, 64)
138 if err != nil {
139 return fmt.Errorf("failed to parse topic time: %v", err)
140 }
141 ch.TopicTime = time.Unix(sec, 0)
142 case irc.RPL_NAMREPLY:
143 if len(msg.Params) < 4 {
144 return newNeedMoreParamsError(msg.Command)
145 }
146 ch, err := c.getChannel(msg.Params[2])
147 if err != nil {
148 return err
149 }
150
151 status, err := parseChannelStatus(msg.Params[1])
152 if err != nil {
153 return err
154 }
155 ch.Status = status
156
157 for _, s := range strings.Split(msg.Params[3], " ") {
158 membership, nick := parseMembershipPrefix(s)
159 ch.Members[nick] = membership
160 }
161 case irc.RPL_ENDOFNAMES:
[25]162 if len(msg.Params) < 2 {
163 return newNeedMoreParamsError(msg.Command)
164 }
165 ch, err := c.getChannel(msg.Params[1])
166 if err != nil {
167 return err
168 }
169
170 ch.complete = true
[16]171 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]172 // Ignore
173 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
174 // Ignore
175 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
176 // Ignore
177 case rpl_localusers, rpl_globalusers:
178 // Ignore
179 case irc.RPL_STATSVLINE, irc.RPL_STATSPING, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
180 // Ignore
[13]181 default:
[21]182 c.logger.Printf("unhandled upstream message: %v", msg)
[13]183 }
[14]184 return nil
[13]185}
186
[23]187func (c *upstreamConn) readMessages() error {
188 defer c.net.Close()
[13]189
[23]190 err := c.irc.WriteMessage(&irc.Message{
[13]191 Command: "NICK",
[23]192 Params: []string{c.upstream.Nick},
[13]193 })
194 if err != nil {
195 return err
196 }
197
198 err = c.irc.WriteMessage(&irc.Message{
199 Command: "USER",
[23]200 Params: []string{c.upstream.Username, "0", "*", c.upstream.Realname},
[13]201 })
202 if err != nil {
203 return err
204 }
205
206 for {
207 msg, err := c.irc.ReadMessage()
208 if err == io.EOF {
209 break
210 } else if err != nil {
211 return fmt.Errorf("failed to read IRC command: %v", err)
212 }
213
214 if err := c.handleMessage(msg); err != nil {
[21]215 c.logger.Printf("failed to handle message %q: %v", msg, err)
[13]216 }
217 }
218
[23]219 return c.net.Close()
[13]220}
[23]221
222func connectToUpstream(s *Server, upstream *Upstream) (*upstreamConn, error) {
223 logger := &prefixLogger{s.Logger, fmt.Sprintf("upstream %q: ", upstream.Addr)}
224 logger.Printf("connecting to server")
225
226 netConn, err := tls.Dial("tcp", upstream.Addr, nil)
227 if err != nil {
228 return nil, fmt.Errorf("failed to dial %q: %v", upstream.Addr, err)
229 }
230
231 return &upstreamConn{
232 upstream: upstream,
233 logger: logger,
234 net: netConn,
235 irc: irc.NewConn(netConn),
236 srv: s,
237 channels: make(map[string]*upstreamChannel),
238 }, nil
239}
Note: See TracBrowser for help on using the repository browser.