serverless-ircd 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -0
- package/README.md +28 -19
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/scripts/smoke.mjs +0 -0
- package/apps/cf-worker/src/worker.ts +8 -2
- package/apps/cf-worker/tests/smoke.test.ts +1 -0
- package/apps/cf-worker/wrangler.test.toml +5 -1
- package/apps/cf-worker/wrangler.toml +41 -17
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +1 -0
- package/apps/local-cli/src/main.ts +1 -1
- package/apps/local-cli/src/server.ts +28 -2
- package/apps/local-cli/tests/e2e.test.ts +52 -0
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +93 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +86 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/AWS-Adapter-Architecture.md +6 -4
- package/docs/AWS-Deployment.md +86 -7
- package/docs/Cloudflare-Deployment-Guide.md +5 -5
- package/docs/Home.md +11 -1
- package/docs/Release-Process.md +9 -6
- package/package.json +14 -15
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/account-store.ts +121 -0
- package/packages/aws-adapter/src/aws-runtime.ts +118 -30
- package/packages/aws-adapter/src/cdk-table-defs.ts +5 -1
- package/packages/aws-adapter/src/config-loader.ts +30 -0
- package/packages/aws-adapter/src/dynamo-account-store.ts +156 -0
- package/packages/aws-adapter/src/handlers/default.ts +8 -0
- package/packages/aws-adapter/src/handlers/disconnect.ts +27 -8
- package/packages/aws-adapter/src/handlers/index.ts +51 -6
- package/packages/aws-adapter/src/index.ts +7 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +171 -0
- package/packages/aws-adapter/tests/account-store.test.ts +280 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +55 -0
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +336 -0
- package/packages/aws-adapter/tests/handlers.test.ts +46 -0
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/cf-runtime.ts +42 -22
- package/packages/cf-adapter/src/channel-do.ts +40 -1
- package/packages/cf-adapter/src/channel-registry-do.ts +58 -0
- package/packages/cf-adapter/src/config-loader.ts +29 -0
- package/packages/cf-adapter/src/connection-do.ts +85 -0
- package/packages/cf-adapter/src/env.ts +27 -1
- package/packages/cf-adapter/src/index.ts +2 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +40 -0
- package/packages/cf-adapter/tests/worker/main.ts +2 -1
- package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
- package/packages/cf-adapter/wrangler.test.toml +10 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +14 -28
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +19 -0
- package/packages/irc-core/package.json +3 -2
- package/packages/irc-core/src/case-fold.ts +64 -0
- package/packages/irc-core/src/commands/index.ts +2 -0
- package/packages/irc-core/src/commands/invite.ts +6 -9
- package/packages/irc-core/src/commands/isupport.ts +1 -1
- package/packages/irc-core/src/commands/join.ts +4 -3
- package/packages/irc-core/src/commands/kick.ts +4 -11
- package/packages/irc-core/src/commands/list.ts +5 -4
- package/packages/irc-core/src/commands/mode.ts +5 -9
- package/packages/irc-core/src/commands/motd-lines.ts +127 -0
- package/packages/irc-core/src/commands/motd.ts +6 -110
- package/packages/irc-core/src/commands/oper.ts +151 -0
- package/packages/irc-core/src/commands/registration.ts +8 -7
- package/packages/irc-core/src/commands/tagmsg.ts +205 -0
- package/packages/irc-core/src/config.ts +26 -3
- package/packages/irc-core/src/index.ts +2 -0
- package/packages/irc-core/src/ports.ts +68 -4
- package/packages/irc-core/src/types.ts +22 -0
- package/packages/irc-core/tests/account-store.test.ts +88 -0
- package/packages/irc-core/tests/case-fold.test.ts +80 -0
- package/packages/irc-core/tests/commands/kick.test.ts +15 -0
- package/packages/irc-core/tests/commands/oper.test.ts +257 -0
- package/packages/irc-core/tests/commands/registration.test.ts +106 -14
- package/packages/irc-core/tests/commands/tagmsg.test.ts +688 -0
- package/packages/irc-core/tests/commands/who.test.ts +22 -4
- package/packages/irc-core/tests/commands/whois.test.ts +8 -1
- package/packages/irc-core/tests/config.test.ts +75 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +24 -0
- package/packages/irc-server/src/routing.ts +9 -0
- package/packages/irc-server/tests/actor.test.ts +220 -1
- package/packages/irc-server/tests/routing.test.ts +3 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +5 -4
- package/pnpm-workspace.yaml +1 -0
- package/tools/seed-aws-accounts.ts +80 -0
- package/AGENTS.md +0 -5
- package/MOTD.txt +0 -3
- package/PLAN-FIXES.md +0 -358
- package/PLAN.md +0 -420
- package/dashboards/cloudwatch-irc.json +0 -139
- package/progress.md +0 -107
- package/tickets.md +0 -2485
- package/webircgateway/LICENSE +0 -201
- package/webircgateway/Makefile +0 -44
- package/webircgateway/README.md +0 -134
- package/webircgateway/config.conf.example +0 -135
- package/webircgateway/go.mod +0 -16
- package/webircgateway/go.sum +0 -89
- package/webircgateway/main.go +0 -118
- package/webircgateway/pkg/dnsbl/dnsbl.go +0 -121
- package/webircgateway/pkg/identd/identd.go +0 -86
- package/webircgateway/pkg/identd/rpcclient.go +0 -59
- package/webircgateway/pkg/irc/isupport.go +0 -56
- package/webircgateway/pkg/irc/message.go +0 -217
- package/webircgateway/pkg/irc/state.go +0 -79
- package/webircgateway/pkg/proxy/proxy.go +0 -129
- package/webircgateway/pkg/proxy/server.go +0 -237
- package/webircgateway/pkg/recaptcha/recaptcha.go +0 -59
- package/webircgateway/pkg/webircgateway/client.go +0 -741
- package/webircgateway/pkg/webircgateway/client_command_handlers.go +0 -495
- package/webircgateway/pkg/webircgateway/config.go +0 -385
- package/webircgateway/pkg/webircgateway/gateway.go +0 -278
- package/webircgateway/pkg/webircgateway/gateway_utils.go +0 -133
- package/webircgateway/pkg/webircgateway/hooks.go +0 -152
- package/webircgateway/pkg/webircgateway/letsencrypt.go +0 -41
- package/webircgateway/pkg/webircgateway/messagetags.go +0 -103
- package/webircgateway/pkg/webircgateway/transport_kiwiirc.go +0 -206
- package/webircgateway/pkg/webircgateway/transport_sockjs.go +0 -107
- package/webircgateway/pkg/webircgateway/transport_tcp.go +0 -113
- package/webircgateway/pkg/webircgateway/transport_websocket.go +0 -126
- package/webircgateway/pkg/webircgateway/utils.go +0 -147
- package/webircgateway/plugins/example/plugin.go +0 -11
- package/webircgateway/plugins/stats/plugin.go +0 -52
- package/webircgateway/staticcheck.conf +0 -1
- package/webircgateway/webircgateway.svg +0 -3
|
@@ -1,495 +0,0 @@
|
|
|
1
|
-
package webircgateway
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"errors"
|
|
5
|
-
"strconv"
|
|
6
|
-
"strings"
|
|
7
|
-
"time"
|
|
8
|
-
|
|
9
|
-
"github.com/golang-jwt/jwt/v4"
|
|
10
|
-
"github.com/kiwiirc/webircgateway/pkg/irc"
|
|
11
|
-
"github.com/kiwiirc/webircgateway/pkg/recaptcha"
|
|
12
|
-
"golang.org/x/net/html/charset"
|
|
13
|
-
"golang.org/x/time/rate"
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
var MAX_EXTJWT_SIZE = 200
|
|
17
|
-
|
|
18
|
-
/*
|
|
19
|
-
* ProcessLineFromUpstream
|
|
20
|
-
* Processes and makes any changes to a line of data sent from an upstream
|
|
21
|
-
*/
|
|
22
|
-
func (c *Client) ProcessLineFromUpstream(data string) string {
|
|
23
|
-
client := c
|
|
24
|
-
|
|
25
|
-
m, parseErr := irc.ParseLine(data)
|
|
26
|
-
if parseErr != nil {
|
|
27
|
-
return data
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
pLen := len(m.Params)
|
|
31
|
-
|
|
32
|
-
if pLen > 0 && m.Command == "NICK" && m.Prefix.Nick == c.IrcState.Nick {
|
|
33
|
-
client.IrcState.Nick = m.Params[0]
|
|
34
|
-
}
|
|
35
|
-
if pLen > 0 && m.Command == "001" {
|
|
36
|
-
client.IrcState.Nick = m.Params[0]
|
|
37
|
-
client.State = ClientStateConnected
|
|
38
|
-
client.ServerMessagePrefix = *m.Prefix
|
|
39
|
-
|
|
40
|
-
// Throttle writes if configured, but only after registration is complete. Typical IRCd
|
|
41
|
-
// behavior is to not throttle registration commands.
|
|
42
|
-
client.ThrottledRecv.Limiter = rate.NewLimiter(rate.Limit(client.UpstreamConfig.Throttle), 1)
|
|
43
|
-
}
|
|
44
|
-
if pLen > 0 && m.Command == "005" {
|
|
45
|
-
tokenPairs := m.Params[1 : pLen-1]
|
|
46
|
-
iSupport := c.IrcState.ISupport
|
|
47
|
-
iSupport.Received = true
|
|
48
|
-
iSupport.Tags = m.Tags
|
|
49
|
-
iSupport.AddTokens(tokenPairs)
|
|
50
|
-
}
|
|
51
|
-
if c.IrcState.ISupport.Received && !c.IrcState.ISupport.Injected && m.Command != "005" {
|
|
52
|
-
iSupport := c.IrcState.ISupport
|
|
53
|
-
iSupport.Injected = true
|
|
54
|
-
|
|
55
|
-
msg := irc.NewMessage()
|
|
56
|
-
msg.Command = "005"
|
|
57
|
-
msg.Prefix = &c.ServerMessagePrefix
|
|
58
|
-
msg.Params = append(msg.Params, c.IrcState.Nick)
|
|
59
|
-
|
|
60
|
-
if iSupport.HasToken("EXTJWT") {
|
|
61
|
-
c.Log(1, "Upstream already supports EXTJWT, disabling feature")
|
|
62
|
-
c.Features.ExtJwt = false
|
|
63
|
-
} else {
|
|
64
|
-
// Add EXTJWT ISupport token
|
|
65
|
-
msg.Params = append(msg.Params, "EXTJWT=1")
|
|
66
|
-
iSupport.AddToken("EXTJWT=1")
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
msg.Params = append(msg.Params, "are supported by this server")
|
|
70
|
-
if timeTag, ok := c.IrcState.ISupport.Tags["time"]; ok {
|
|
71
|
-
msg.Tags["time"] = timeTag
|
|
72
|
-
}
|
|
73
|
-
if len(msg.Params) > 2 {
|
|
74
|
-
// Extra tokens were added, send the line
|
|
75
|
-
c.SendClientSignal("data", msg.ToLine())
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if pLen > 0 && m.Command == "JOIN" && m.Prefix.Nick == c.IrcState.Nick {
|
|
79
|
-
channel := irc.NewStateChannel(m.GetParam(0, ""))
|
|
80
|
-
c.IrcState.SetChannel(channel)
|
|
81
|
-
}
|
|
82
|
-
if pLen > 0 && m.Command == "PART" && m.Prefix.Nick == c.IrcState.Nick {
|
|
83
|
-
c.IrcState.RemoveChannel(m.GetParam(0, ""))
|
|
84
|
-
}
|
|
85
|
-
if pLen > 0 && m.Command == "QUIT" && m.Prefix.Nick == c.IrcState.Nick {
|
|
86
|
-
c.IrcState.ClearChannels()
|
|
87
|
-
}
|
|
88
|
-
// :server.com 900 m m!m@irc-3jg.1ab.j4ep8h.IP prawnsalad :You are now logged in as prawnsalad
|
|
89
|
-
if pLen > 0 && m.Command == "900" {
|
|
90
|
-
c.IrcState.Account = m.GetParam(2, "")
|
|
91
|
-
}
|
|
92
|
-
// :server.com 901 itsonlybinary itsonlybinary!itsonlybina@user/itsonlybinary :You are now logged out
|
|
93
|
-
if m.Command == "901" {
|
|
94
|
-
c.IrcState.Account = ""
|
|
95
|
-
}
|
|
96
|
-
// :prawnsalad!prawn@kiwiirc/prawnsalad MODE #kiwiirc-dev +oo notprawn kiwi-n75
|
|
97
|
-
if pLen > 0 && m.Command == "MODE" {
|
|
98
|
-
if strings.HasPrefix(m.GetParam(0, ""), "#") {
|
|
99
|
-
channelName := m.GetParam(0, "")
|
|
100
|
-
modes := m.GetParam(1, "")
|
|
101
|
-
|
|
102
|
-
channel := c.IrcState.GetChannel(channelName)
|
|
103
|
-
if channel != nil {
|
|
104
|
-
channel = irc.NewStateChannel(channelName)
|
|
105
|
-
c.IrcState.SetChannel(channel)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
adding := false
|
|
109
|
-
paramIdx := 1
|
|
110
|
-
for i := 0; i < len(modes); i++ {
|
|
111
|
-
mode := string(modes[i])
|
|
112
|
-
|
|
113
|
-
if mode == "+" {
|
|
114
|
-
adding = true
|
|
115
|
-
} else if mode == "-" {
|
|
116
|
-
adding = false
|
|
117
|
-
} else {
|
|
118
|
-
paramIdx++
|
|
119
|
-
param := m.GetParam(paramIdx, "")
|
|
120
|
-
if strings.EqualFold(param, c.IrcState.Nick) {
|
|
121
|
-
if adding {
|
|
122
|
-
channel.Modes[mode] = ""
|
|
123
|
-
} else {
|
|
124
|
-
delete(channel.Modes, mode)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// If upstream reports that it supports message-tags natively, disable the wrapping of this feature for
|
|
133
|
-
// this client
|
|
134
|
-
if pLen >= 3 &&
|
|
135
|
-
strings.ToUpper(m.Command) == "CAP" &&
|
|
136
|
-
m.GetParamU(1, "") == "LS" {
|
|
137
|
-
// The CAPs could be param 2 or 3 depending on if were using multiple lines to list them all.
|
|
138
|
-
caps := ""
|
|
139
|
-
if pLen >= 4 && m.Params[2] == "*" {
|
|
140
|
-
caps = m.GetParamU(3, "")
|
|
141
|
-
} else {
|
|
142
|
-
caps = m.GetParamU(2, "")
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if containsOneOf(caps, []string{"DRAFT/MESSAGE-TAGS-0.2", "MESSAGE-TAGS"}) {
|
|
146
|
-
c.Log(1, "Upstream already supports Messagetags, disabling feature")
|
|
147
|
-
c.Features.Messagetags = false
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// Inject message-tags cap into the last line of IRCd capabilities
|
|
151
|
-
if c.Features.Messagetags && m.Params[2] != "*" {
|
|
152
|
-
m.Params[2] += " message-tags"
|
|
153
|
-
data = m.ToLine()
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// If we requested message-tags, make sure to include it in the ACK when
|
|
158
|
-
// the IRCd sends the ACK through
|
|
159
|
-
if m != nil &&
|
|
160
|
-
client.RequestedMessageTagsCap != "" &&
|
|
161
|
-
strings.ToUpper(m.Command) == "CAP" &&
|
|
162
|
-
m.GetParamU(1, "") == "ACK" &&
|
|
163
|
-
!strings.Contains(m.GetParamU(2, ""), "MESSAGE-TAGS") {
|
|
164
|
-
|
|
165
|
-
m.Params[2] += " " + client.RequestedMessageTagsCap
|
|
166
|
-
data = m.ToLine()
|
|
167
|
-
|
|
168
|
-
client.RequestedMessageTagsCap = ""
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if m != nil && client.Features.Messagetags && c.Gateway.messageTags.CanMessageContainClientTags(m) {
|
|
172
|
-
// If we have any message tags stored for this message from a previous PRIVMSG sent
|
|
173
|
-
// by a client, add them back in
|
|
174
|
-
mTags, mTagsExists := c.Gateway.messageTags.GetTagsFromMessage(client, m.Prefix.Nick, m)
|
|
175
|
-
if mTagsExists {
|
|
176
|
-
for k, v := range mTags.Tags {
|
|
177
|
-
m.Tags[k] = v
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
data = m.ToLine()
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return data
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/*
|
|
188
|
-
* ProcessLineFromClient
|
|
189
|
-
* Processes and makes any changes to a line of data sent from a client
|
|
190
|
-
*/
|
|
191
|
-
func (c *Client) ProcessLineFromClient(line string) (string, error) {
|
|
192
|
-
message, err := irc.ParseLine(line)
|
|
193
|
-
// Just pass any random data upstream
|
|
194
|
-
if err != nil {
|
|
195
|
-
return line, nil
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
maybeConnectUpstream := func() {
|
|
199
|
-
verified := false
|
|
200
|
-
if c.RequiresVerification && !c.Verified {
|
|
201
|
-
verified = false
|
|
202
|
-
} else {
|
|
203
|
-
verified = true
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if !c.UpstreamStarted && c.IrcState.Username != "" && c.IrcState.Nick != "" && verified {
|
|
207
|
-
c.connectUpstream()
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if !c.Verified && strings.ToUpper(message.Command) == "CAPTCHA" {
|
|
212
|
-
verified := false
|
|
213
|
-
if len(message.Params) >= 1 {
|
|
214
|
-
captcha := recaptcha.R{
|
|
215
|
-
URL: c.Gateway.Config.ReCaptchaURL,
|
|
216
|
-
Secret: c.Gateway.Config.ReCaptchaSecret,
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
verified = captcha.VerifyResponse(message.Params[0])
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if !verified {
|
|
223
|
-
c.SendIrcError("Invalid captcha")
|
|
224
|
-
c.SendClientSignal("state", "closed", "bad_captcha")
|
|
225
|
-
c.StartShutdown("unverifed")
|
|
226
|
-
} else {
|
|
227
|
-
c.Verified = true
|
|
228
|
-
maybeConnectUpstream()
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return "", nil
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// NICK <nickname>
|
|
235
|
-
if strings.ToUpper(message.Command) == "NICK" && !c.UpstreamStarted {
|
|
236
|
-
if len(message.Params) > 0 {
|
|
237
|
-
c.IrcState.Nick = message.Params[0]
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if !c.UpstreamStarted {
|
|
241
|
-
maybeConnectUpstream()
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// USER <username> <hostname> <servername> <realname>
|
|
246
|
-
if strings.ToUpper(message.Command) == "USER" && !c.UpstreamStarted {
|
|
247
|
-
if len(message.Params) < 4 {
|
|
248
|
-
return line, errors.New("Invalid USER line")
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if c.Gateway.Config.ClientUsername != "" {
|
|
252
|
-
message.Params[0] = makeClientReplacements(c.Gateway.Config.ClientUsername, c)
|
|
253
|
-
}
|
|
254
|
-
if c.Gateway.Config.ClientRealname != "" {
|
|
255
|
-
message.Params[3] = makeClientReplacements(c.Gateway.Config.ClientRealname, c)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
line = message.ToLine()
|
|
259
|
-
|
|
260
|
-
c.IrcState.Username = message.Params[0]
|
|
261
|
-
c.IrcState.RealName = message.Params[3]
|
|
262
|
-
|
|
263
|
-
maybeConnectUpstream()
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if strings.ToUpper(message.Command) == "ENCODING" {
|
|
267
|
-
if len(message.Params) > 0 {
|
|
268
|
-
encoding, _ := charset.Lookup(message.Params[0])
|
|
269
|
-
if encoding == nil {
|
|
270
|
-
c.Log(1, "Requested unknown encoding, %s", message.Params[0])
|
|
271
|
-
} else {
|
|
272
|
-
c.Encoding = message.Params[0]
|
|
273
|
-
c.Log(1, "Set encoding to %s", message.Params[0])
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Don't send the ENCODING command upstream
|
|
278
|
-
return "", nil
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
if strings.ToUpper(message.Command) == "HOST" && !c.UpstreamStarted {
|
|
282
|
-
// HOST irc.network.net:6667
|
|
283
|
-
// HOST irc.network.net:+6667
|
|
284
|
-
|
|
285
|
-
if !c.Gateway.Config.Gateway {
|
|
286
|
-
return "", nil
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if len(message.Params) == 0 {
|
|
290
|
-
return "", nil
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
addr := message.Params[0]
|
|
294
|
-
if addr == "" {
|
|
295
|
-
c.SendIrcError("Missing host")
|
|
296
|
-
c.StartShutdown("missing_host")
|
|
297
|
-
return "", nil
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Parse host:+port into the c.dest* vars
|
|
301
|
-
portSep := strings.LastIndex(addr, ":")
|
|
302
|
-
if portSep == -1 {
|
|
303
|
-
c.DestHost = addr
|
|
304
|
-
c.DestPort = 6667
|
|
305
|
-
c.DestTLS = false
|
|
306
|
-
} else {
|
|
307
|
-
c.DestHost = addr[0:portSep]
|
|
308
|
-
portParam := addr[portSep+1:]
|
|
309
|
-
if len(portParam) > 0 && portParam[0:1] == "+" {
|
|
310
|
-
c.DestTLS = true
|
|
311
|
-
c.DestPort, err = strconv.Atoi(portParam[1:])
|
|
312
|
-
if err != nil {
|
|
313
|
-
c.DestPort = 6697
|
|
314
|
-
}
|
|
315
|
-
} else {
|
|
316
|
-
c.DestPort, err = strconv.Atoi(portParam[0:])
|
|
317
|
-
if err != nil {
|
|
318
|
-
c.DestPort = 6667
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Don't send the HOST command upstream
|
|
324
|
-
return "", nil
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// If the client supports CAP, assume the client also supports parsing MessageTags
|
|
328
|
-
// When upstream replies with its CAP listing, we check if message-tags is supported by the IRCd already and if so,
|
|
329
|
-
// we disable this feature flag again to use the IRCds native support.
|
|
330
|
-
if strings.ToUpper(message.Command) == "CAP" && len(message.Params) > 0 && strings.ToUpper(message.Params[0]) == "LS" {
|
|
331
|
-
c.Log(1, "Enabling client Messagetags feature")
|
|
332
|
-
c.Features.Messagetags = true
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// If we are wrapping the Messagetags feature, make sure the clients REQ message-tags doesn't
|
|
336
|
-
// get sent upstream
|
|
337
|
-
if c.Features.Messagetags && strings.ToUpper(message.Command) == "CAP" && message.GetParamU(0, "") == "REQ" {
|
|
338
|
-
reqCaps := strings.ToLower(message.GetParam(1, ""))
|
|
339
|
-
capsThatEnableMessageTags := []string{"message-tags", "account-tag", "server-time", "batch"}
|
|
340
|
-
|
|
341
|
-
if strings.Contains(reqCaps, "message-tags") {
|
|
342
|
-
// Rebuild the list of requested caps, without message-tags
|
|
343
|
-
caps := strings.Split(reqCaps, " ")
|
|
344
|
-
newCaps := []string{}
|
|
345
|
-
for _, cap := range caps {
|
|
346
|
-
if !strings.Contains(strings.ToLower(cap), "message-tags") {
|
|
347
|
-
newCaps = append(newCaps, cap)
|
|
348
|
-
} else {
|
|
349
|
-
c.RequestedMessageTagsCap = cap
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
if len(newCaps) == 0 {
|
|
354
|
-
// The only requested CAP was our emulated message-tags
|
|
355
|
-
// the server will not be sending an ACK so we need to send our own
|
|
356
|
-
c.SendClientSignal("data", "CAP * ACK :"+c.RequestedMessageTagsCap)
|
|
357
|
-
return "", nil
|
|
358
|
-
}
|
|
359
|
-
message.Params[1] = strings.Join(newCaps, " ")
|
|
360
|
-
line = message.ToLine()
|
|
361
|
-
} else if !containsOneOf(reqCaps, capsThatEnableMessageTags) {
|
|
362
|
-
// Didn't request anything that needs message-tags cap so disable it
|
|
363
|
-
c.Features.Messagetags = false
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if c.Features.Messagetags && message.Command == "TAGMSG" {
|
|
368
|
-
if len(message.Params) == 0 {
|
|
369
|
-
return "", nil
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// We can't be 100% sure what this users correct mask is, so just send the nick
|
|
373
|
-
message.Prefix.Nick = c.IrcState.Nick
|
|
374
|
-
message.Prefix.Hostname = ""
|
|
375
|
-
message.Prefix.Username = ""
|
|
376
|
-
|
|
377
|
-
thisHost := strings.ToLower(c.UpstreamConfig.Hostname)
|
|
378
|
-
target := message.Params[0]
|
|
379
|
-
for val := range c.Gateway.Clients.IterBuffered() {
|
|
380
|
-
curClient := val.Val.(*Client)
|
|
381
|
-
sameHost := strings.ToLower(curClient.UpstreamConfig.Hostname) == thisHost
|
|
382
|
-
if !sameHost {
|
|
383
|
-
continue
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Only send the message on to either the target nick, or the clients in a set channel
|
|
387
|
-
if !strings.EqualFold(target, curClient.IrcState.Nick) && !curClient.IrcState.HasChannel(target) {
|
|
388
|
-
continue
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
curClient.SendClientSignal("data", message.ToLine())
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
return "", nil
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Check for any client message tags so that we can store them for replaying to other clients
|
|
398
|
-
if c.Features.Messagetags && c.Gateway.messageTags.CanMessageContainClientTags(message) {
|
|
399
|
-
c.Gateway.messageTags.AddTagsFromMessage(c, c.IrcState.Nick, message)
|
|
400
|
-
// Prevent any client tags heading upstream
|
|
401
|
-
for k := range message.Tags {
|
|
402
|
-
if len(k) > 0 && k[0] == '+' {
|
|
403
|
-
delete(message.Tags, k)
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
line = message.ToLine()
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if c.Features.ExtJwt && strings.ToUpper(message.Command) == "EXTJWT" {
|
|
411
|
-
tokenTarget := message.GetParam(0, "")
|
|
412
|
-
tokenService := message.GetParam(1, "")
|
|
413
|
-
|
|
414
|
-
tokenM := irc.Message{}
|
|
415
|
-
tokenM.Command = "EXTJWT"
|
|
416
|
-
tokenM.Prefix = &c.ServerMessagePrefix
|
|
417
|
-
tokenData := jwt.MapClaims{
|
|
418
|
-
"exp": time.Now().UTC().Add(1 * time.Minute).Unix(),
|
|
419
|
-
"iss": c.UpstreamConfig.Hostname,
|
|
420
|
-
"sub": c.IrcState.Nick,
|
|
421
|
-
"account": c.IrcState.Account,
|
|
422
|
-
"umodes": []string{},
|
|
423
|
-
|
|
424
|
-
// Channel specific claims
|
|
425
|
-
"channel": "",
|
|
426
|
-
"joined": 0,
|
|
427
|
-
"cmodes": []string{},
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
// Use the NetworkCommonAddress if a plugin as assigned one.
|
|
431
|
-
// This allows plugins to associate different upstream hosts to the same network
|
|
432
|
-
if c.UpstreamConfig.NetworkCommonAddress != "" {
|
|
433
|
-
tokenData["iss"] = c.UpstreamConfig.NetworkCommonAddress
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
if tokenTarget == "" || tokenTarget == "*" {
|
|
437
|
-
tokenM.Params = append(tokenM.Params, "*")
|
|
438
|
-
} else {
|
|
439
|
-
targetChan := c.IrcState.GetChannel(tokenTarget)
|
|
440
|
-
if targetChan == nil {
|
|
441
|
-
// Channel does not exist in IRC State, send so such channel message
|
|
442
|
-
failMessage := irc.Message{
|
|
443
|
-
Command: "403", // ERR_NOSUCHCHANNEL
|
|
444
|
-
Prefix: &c.ServerMessagePrefix,
|
|
445
|
-
Params: []string{c.IrcState.Nick, tokenTarget, "No such channel"},
|
|
446
|
-
}
|
|
447
|
-
c.SendClientSignal("data", failMessage.ToLine())
|
|
448
|
-
return "", nil
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
tokenM.Params = append(tokenM.Params, tokenTarget)
|
|
452
|
-
|
|
453
|
-
tokenData["channel"] = targetChan.Name
|
|
454
|
-
tokenData["joined"] = targetChan.Joined.Unix()
|
|
455
|
-
|
|
456
|
-
modes := []string{}
|
|
457
|
-
for mode := range targetChan.Modes {
|
|
458
|
-
modes = append(modes, mode)
|
|
459
|
-
}
|
|
460
|
-
tokenData["cmodes"] = modes
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
if tokenService == "" || tokenService == "*" {
|
|
464
|
-
tokenM.Params = append(tokenM.Params, "*")
|
|
465
|
-
} else {
|
|
466
|
-
c.SendIrcFail("EXTJWT", "NO_SUCH_SERVICE", "No such service")
|
|
467
|
-
return "", nil
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
token := jwt.NewWithClaims(jwt.SigningMethodHS256, tokenData)
|
|
471
|
-
tokenSigned, tokenSignedErr := token.SignedString([]byte(c.Gateway.Config.Secret))
|
|
472
|
-
if tokenSignedErr != nil {
|
|
473
|
-
c.Log(3, "Error creating JWT token. %s", tokenSignedErr.Error())
|
|
474
|
-
c.SendIrcFail("EXTJWT", "UNKNOWN_ERROR", "Failed to generate token")
|
|
475
|
-
return "", nil
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
// Spit token if it exceeds max length
|
|
479
|
-
for len(tokenSigned) > MAX_EXTJWT_SIZE {
|
|
480
|
-
tokenSignedPart := tokenSigned[:MAX_EXTJWT_SIZE]
|
|
481
|
-
tokenSigned = tokenSigned[MAX_EXTJWT_SIZE:]
|
|
482
|
-
|
|
483
|
-
tokenPartM := tokenM
|
|
484
|
-
tokenPartM.Params = append(tokenPartM.Params, "*", tokenSignedPart)
|
|
485
|
-
c.SendClientSignal("data", tokenPartM.ToLine())
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
tokenM.Params = append(tokenM.Params, tokenSigned)
|
|
489
|
-
c.SendClientSignal("data", tokenM.ToLine())
|
|
490
|
-
|
|
491
|
-
return "", nil
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return line, nil
|
|
495
|
-
}
|