source: code/trunk/upstream.go@ 17

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

Handle MODE messages from upstream servers

File size: 3.4 KB
RevLine 
[13]1package jounce
2
3import (
4 "crypto/tls"
5 "fmt"
6 "io"
7 "net"
[17]8 "strings"
[13]9
10 "gopkg.in/irc.v3"
11)
12
[14]13const (
[16]14 rpl_localusers = "265"
[14]15 rpl_globalusers = "266"
16)
17
[17]18type modeSet string
19
20func (ms modeSet) Has(c byte) bool {
21 return strings.IndexByte(string(ms), c) >= 0
22}
23
24func (ms *modeSet) Add(c byte) {
25 if !ms.Has(c) {
26 *ms += modeSet(c)
27 }
28}
29
30func (ms *modeSet) Del(c byte) {
31 i := strings.IndexByte(string(*ms), c)
32 if i >= 0 {
33 *ms = (*ms)[:i] + (*ms)[i+1:]
34 }
35}
36
37func (ms *modeSet) Apply(s string) error {
38 var plusMinus byte
39 for i := 0; i < len(s); i++ {
40 switch c := s[i]; c {
41 case '+', '-':
42 plusMinus = c
43 default:
44 switch plusMinus {
45 case '+':
46 ms.Add(c)
47 case '-':
48 ms.Del(c)
49 default:
50 return fmt.Errorf("malformed modestring %q: missing plus/minus", s)
51 }
52 }
53 }
54 return nil
55}
56
[13]57type upstreamConn struct {
[16]58 upstream *Upstream
59 net net.Conn
60 irc *irc.Conn
61 srv *Server
[14]62 registered bool
[17]63 modes modeSet
[16]64
65 serverName string
66 availableUserModes string
67 availableChannelModes string
68 channelModesWithParam string
[13]69}
70
71func (c *upstreamConn) handleMessage(msg *irc.Message) error {
72 switch msg.Command {
73 case "PING":
74 // TODO: handle params
75 return c.irc.WriteMessage(&irc.Message{
76 Command: "PONG",
77 Params: []string{c.srv.Hostname},
78 })
[17]79 case "MODE":
80 if len(msg.Params) < 2 {
81 return newNeedMoreParamsError(msg.Command)
82 }
83 if nick := msg.Params[0]; nick != c.upstream.Nick {
84 return fmt.Errorf("received MODE message for unknow nick %q", nick)
85 }
86 return c.modes.Apply(msg.Params[1])
[14]87 case irc.RPL_WELCOME:
88 c.registered = true
[16]89 c.srv.Logger.Printf("Connection to %q registered", c.upstream.Addr)
90 case irc.RPL_MYINFO:
91 if len(msg.Params) < 5 {
92 return newNeedMoreParamsError(msg.Command)
93 }
94 c.serverName = msg.Params[1]
95 c.availableUserModes = msg.Params[3]
96 c.availableChannelModes = msg.Params[4]
97 if len(msg.Params) > 5 {
98 c.channelModesWithParam = msg.Params[5]
99 }
100 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]101 // Ignore
102 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
103 // Ignore
104 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
105 // Ignore
106 case rpl_localusers, rpl_globalusers:
107 // Ignore
108 case irc.RPL_STATSVLINE, irc.RPL_STATSPING, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
109 // Ignore
[13]110 default:
111 c.srv.Logger.Printf("Unhandled upstream message: %v", msg)
112 }
[14]113 return nil
[13]114}
115
116func connect(s *Server, upstream *Upstream) error {
117 s.Logger.Printf("Connecting to %v", upstream.Addr)
118
119 netConn, err := tls.Dial("tcp", upstream.Addr, nil)
120 if err != nil {
121 return fmt.Errorf("failed to dial %q: %v", upstream.Addr, err)
122 }
123
[16]124 c := upstreamConn{
125 upstream: upstream,
126 net: netConn,
127 irc: irc.NewConn(netConn),
128 srv: s,
129 }
[13]130 defer netConn.Close()
131
132 err = c.irc.WriteMessage(&irc.Message{
133 Command: "NICK",
134 Params: []string{upstream.Nick},
135 })
136 if err != nil {
137 return err
138 }
139
140 err = c.irc.WriteMessage(&irc.Message{
141 Command: "USER",
142 Params: []string{upstream.Username, "0", "*", upstream.Realname},
143 })
144 if err != nil {
145 return err
146 }
147
148 for {
149 msg, err := c.irc.ReadMessage()
150 if err == io.EOF {
151 break
152 } else if err != nil {
153 return fmt.Errorf("failed to read IRC command: %v", err)
154 }
155
156 if err := c.handleMessage(msg); err != nil {
[16]157 c.srv.Logger.Printf("Failed to handle message %q from %q: %v", msg, upstream.Addr, err)
[13]158 }
159 }
160
161 return netConn.Close()
162}
Note: See TracBrowser for help on using the repository browser.