source: code/trunk/downstream.go@ 21

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

Send standard user/channel modes to downstream connections

File size: 3.9 KB
Line 
1package jounce
2
3import (
4 "fmt"
5 "io"
6 "net"
7
8 "gopkg.in/irc.v3"
9)
10
11type ircError struct {
12 Message *irc.Message
13}
14
15func newUnknownCommandError(cmd string) ircError {
16 return ircError{&irc.Message{
17 Command: irc.ERR_UNKNOWNCOMMAND,
18 Params: []string{
19 "*",
20 cmd,
21 "Unknown command",
22 },
23 }}
24}
25
26func newNeedMoreParamsError(cmd string) ircError {
27 return ircError{&irc.Message{
28 Command: irc.ERR_NEEDMOREPARAMS,
29 Params: []string{
30 "*",
31 cmd,
32 "Not enough parameters",
33 },
34 }}
35}
36
37func (err ircError) Error() string {
38 return err.Message.String()
39}
40
41type downstreamConn struct {
42 net net.Conn
43 irc *irc.Conn
44 srv *Server
45 registered bool
46 closed bool
47 nick string
48 username string
49 realname string
50}
51
52func (c *downstreamConn) Close() error {
53 if err := c.net.Close(); err != nil {
54 return err
55 }
56 c.closed = true
57 return nil
58}
59
60func (c *downstreamConn) WriteMessage(msg *irc.Message) error {
61 msg.Prefix = c.srv.prefix()
62 return c.irc.WriteMessage(msg)
63}
64
65func (c *downstreamConn) handleMessage(msg *irc.Message) error {
66 switch msg.Command {
67 case "PING":
68 // TODO: handle params
69 return c.WriteMessage(&irc.Message{
70 Command: "PONG",
71 Params: []string{c.srv.Hostname},
72 })
73 default:
74 if c.registered {
75 return c.handleMessageRegistered(msg)
76 } else {
77 return c.handleMessageUnregistered(msg)
78 }
79 }
80}
81
82func (c *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
83 switch msg.Command {
84 case "NICK":
85 if len(msg.Params) != 1 {
86 return newNeedMoreParamsError(msg.Command)
87 }
88 c.nick = msg.Params[0]
89 case "USER":
90 if len(msg.Params) != 4 {
91 return newNeedMoreParamsError(msg.Command)
92 }
93 c.username = "~" + msg.Params[0]
94 c.realname = msg.Params[3]
95 case "QUIT":
96 return c.Close()
97 default:
98 return newUnknownCommandError(msg.Command)
99 }
100 if c.username != "" && c.nick != "" {
101 return c.register()
102 }
103 return nil
104}
105
106func (c *downstreamConn) register() error {
107 c.registered = true
108
109 err := c.WriteMessage(&irc.Message{
110 Command: irc.RPL_WELCOME,
111 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
112 })
113 if err != nil {
114 return err
115 }
116
117 err = c.WriteMessage(&irc.Message{
118 Command: irc.RPL_YOURHOST,
119 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
120 })
121 if err != nil {
122 return err
123 }
124
125 err = c.WriteMessage(&irc.Message{
126 Command: irc.RPL_CREATED,
127 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
128 })
129 if err != nil {
130 return err
131 }
132
133 err = c.WriteMessage(&irc.Message{
134 Command: irc.RPL_MYINFO,
135 Params: []string{c.nick, c.srv.Hostname, "unknown", "aiwroO", "OovaimnqpsrtklbeI"},
136 })
137 if err != nil {
138 return err
139 }
140
141 err = c.WriteMessage(&irc.Message{
142 Command: irc.ERR_NOMOTD,
143 Params: []string{c.nick, "No MOTD"},
144 })
145 if err != nil {
146 return err
147 }
148
149 return nil
150}
151
152func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
153 switch msg.Command {
154 case "NICK", "USER":
155 return ircError{&irc.Message{
156 Command: irc.ERR_ALREADYREGISTERED,
157 Params: []string{
158 c.nick,
159 "You may not reregister",
160 },
161 }}
162 case "QUIT":
163 return c.Close()
164 default:
165 return newUnknownCommandError(msg.Command)
166 }
167}
168
169func handleConn(s *Server, netConn net.Conn) error {
170 s.Logger.Printf("Handling connection from %v", netConn.RemoteAddr())
171
172 c := downstreamConn{net: netConn, irc: irc.NewConn(netConn), srv: s}
173 defer c.Close()
174 for {
175 msg, err := c.irc.ReadMessage()
176 if err == io.EOF {
177 break
178 } else if err != nil {
179 return fmt.Errorf("failed to read IRC command: %v", err)
180 }
181 s.Logger.Printf("Downstream message: %v", msg)
182
183 err = c.handleMessage(msg)
184 if ircErr, ok := err.(ircError); ok {
185 ircErr.Message.Prefix = s.prefix()
186 if err := c.WriteMessage(ircErr.Message); err != nil {
187 return fmt.Errorf("failed to write IRC reply: %v", err)
188 }
189 } else if err != nil {
190 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
191 }
192
193 if c.closed {
194 return nil
195 }
196 }
197
198 return c.Close()
199}
Note: See TracBrowser for help on using the repository browser.