source: code/trunk/upstream.go@ 18

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

Handle NOTICE from upstream servers

File size: 3.5 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])
[18]87 case "NOTICE":
88 c.srv.Logger.Printf("%q: %v", c.upstream.Addr, msg)
[14]89 case irc.RPL_WELCOME:
90 c.registered = true
[16]91 c.srv.Logger.Printf("Connection to %q registered", c.upstream.Addr)
92 case irc.RPL_MYINFO:
93 if len(msg.Params) < 5 {
94 return newNeedMoreParamsError(msg.Command)
95 }
96 c.serverName = msg.Params[1]
97 c.availableUserModes = msg.Params[3]
98 c.availableChannelModes = msg.Params[4]
99 if len(msg.Params) > 5 {
100 c.channelModesWithParam = msg.Params[5]
101 }
102 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]103 // Ignore
104 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
105 // Ignore
106 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
107 // Ignore
108 case rpl_localusers, rpl_globalusers:
109 // Ignore
110 case irc.RPL_STATSVLINE, irc.RPL_STATSPING, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
111 // Ignore
[13]112 default:
113 c.srv.Logger.Printf("Unhandled upstream message: %v", msg)
114 }
[14]115 return nil
[13]116}
117
118func connect(s *Server, upstream *Upstream) error {
119 s.Logger.Printf("Connecting to %v", upstream.Addr)
120
121 netConn, err := tls.Dial("tcp", upstream.Addr, nil)
122 if err != nil {
123 return fmt.Errorf("failed to dial %q: %v", upstream.Addr, err)
124 }
125
[16]126 c := upstreamConn{
127 upstream: upstream,
128 net: netConn,
129 irc: irc.NewConn(netConn),
130 srv: s,
131 }
[13]132 defer netConn.Close()
133
134 err = c.irc.WriteMessage(&irc.Message{
135 Command: "NICK",
136 Params: []string{upstream.Nick},
137 })
138 if err != nil {
139 return err
140 }
141
142 err = c.irc.WriteMessage(&irc.Message{
143 Command: "USER",
144 Params: []string{upstream.Username, "0", "*", upstream.Realname},
145 })
146 if err != nil {
147 return err
148 }
149
150 for {
151 msg, err := c.irc.ReadMessage()
152 if err == io.EOF {
153 break
154 } else if err != nil {
155 return fmt.Errorf("failed to read IRC command: %v", err)
156 }
157
158 if err := c.handleMessage(msg); err != nil {
[16]159 c.srv.Logger.Printf("Failed to handle message %q from %q: %v", msg, upstream.Addr, err)
[13]160 }
161 }
162
163 return netConn.Close()
164}
Note: See TracBrowser for help on using the repository browser.