serverless-ircd 0.2.0 → 0.4.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.
Files changed (221) hide show
  1. package/.github/workflows/ci.yml +2 -2
  2. package/.github/workflows/deploy-aws.yml +1 -3
  3. package/.github/workflows/deploy-cf-tcp.yml +87 -0
  4. package/.github/workflows/deploy-cf.yml +1 -1
  5. package/.node-version +1 -0
  6. package/.nvmrc +1 -0
  7. package/CHANGELOG.md +258 -18
  8. package/README.md +72 -30
  9. package/apps/aws-stack/README.md +2 -2
  10. package/apps/aws-stack/bin/aws.ts +7 -0
  11. package/apps/aws-stack/package.json +4 -4
  12. package/apps/aws-stack/src/aws-stack.ts +118 -6
  13. package/apps/aws-stack/tests/stack.test.ts +98 -3
  14. package/apps/cf-tcp-container/Dockerfile +69 -0
  15. package/apps/cf-tcp-container/package.json +34 -0
  16. package/apps/cf-tcp-container/src/config-loader.ts +145 -0
  17. package/apps/cf-tcp-container/src/container-do.ts +38 -0
  18. package/apps/cf-tcp-container/src/container-server.ts +363 -0
  19. package/apps/cf-tcp-container/src/main.ts +77 -0
  20. package/apps/cf-tcp-container/src/persistence.ts +144 -0
  21. package/apps/cf-tcp-container/src/worker.ts +41 -0
  22. package/apps/cf-tcp-container/terraform/provider.tf +24 -0
  23. package/apps/cf-tcp-container/terraform/spectrum.tf +81 -0
  24. package/apps/cf-tcp-container/tests/config-loader.test.ts +217 -0
  25. package/apps/cf-tcp-container/tests/container-server.test.ts +465 -0
  26. package/apps/cf-tcp-container/tests/persistence.test.ts +227 -0
  27. package/apps/cf-tcp-container/tests/tls-e2e.test.ts +275 -0
  28. package/apps/cf-tcp-container/tsconfig.build.json +17 -0
  29. package/apps/cf-tcp-container/tsconfig.test.json +15 -0
  30. package/apps/cf-tcp-container/vitest.config.ts +26 -0
  31. package/apps/cf-tcp-container/wrangler.toml +63 -0
  32. package/apps/cf-worker/package.json +1 -1
  33. package/apps/cf-worker/scripts/smoke.mjs +0 -0
  34. package/apps/cf-worker/src/worker.ts +8 -2
  35. package/apps/cf-worker/tests/smoke.test.ts +1 -0
  36. package/apps/cf-worker/wrangler.test.toml +11 -1
  37. package/apps/cf-worker/wrangler.toml +69 -19
  38. package/apps/local-cli/package.json +1 -1
  39. package/apps/local-cli/src/config-loader.ts +11 -0
  40. package/apps/local-cli/src/main.ts +1 -1
  41. package/apps/local-cli/src/server.ts +46 -3
  42. package/apps/local-cli/tests/e2e.test.ts +52 -0
  43. package/package.json +19 -16
  44. package/packages/aws-adapter/package.json +3 -3
  45. package/packages/aws-adapter/src/account-store.ts +121 -0
  46. package/packages/aws-adapter/src/admission.ts +74 -0
  47. package/packages/aws-adapter/src/aws-runtime.ts +124 -34
  48. package/packages/aws-adapter/src/cdk-table-defs.ts +5 -1
  49. package/packages/aws-adapter/src/config-loader.ts +62 -0
  50. package/packages/aws-adapter/src/dynamo-account-store.ts +95 -0
  51. package/packages/aws-adapter/src/handlers/connect.ts +64 -8
  52. package/packages/aws-adapter/src/handlers/default.ts +43 -2
  53. package/packages/aws-adapter/src/handlers/disconnect.ts +27 -8
  54. package/packages/aws-adapter/src/handlers/index.ts +120 -9
  55. package/packages/aws-adapter/src/handlers/nlb-stream.ts +481 -0
  56. package/packages/aws-adapter/src/handlers/ping-checker.ts +1 -1
  57. package/packages/aws-adapter/src/index.ts +13 -0
  58. package/packages/aws-adapter/tests/account-store-dynamo.test.ts +182 -0
  59. package/packages/aws-adapter/tests/account-store.test.ts +279 -0
  60. package/packages/aws-adapter/tests/admission.test.ts +70 -0
  61. package/packages/aws-adapter/tests/aws-harness.ts +13 -1
  62. package/packages/aws-adapter/tests/config-loader.test.ts +75 -0
  63. package/packages/aws-adapter/tests/connect.test.ts +78 -0
  64. package/packages/aws-adapter/tests/disconnect-fanout.test.ts +343 -0
  65. package/packages/aws-adapter/tests/gone-exception.test.ts +31 -26
  66. package/packages/aws-adapter/tests/handlers.test.ts +194 -47
  67. package/packages/aws-adapter/tests/nlb-stream.test.ts +478 -0
  68. package/packages/aws-adapter/tests/ping-checker.test.ts +34 -29
  69. package/packages/aws-adapter/tests/sweeper.test.ts +25 -18
  70. package/packages/aws-adapter/tests/transactions.test.ts +25 -20
  71. package/packages/cf-adapter/package.json +1 -1
  72. package/packages/cf-adapter/src/cf-runtime.ts +40 -22
  73. package/packages/cf-adapter/src/channel-do.ts +40 -1
  74. package/packages/cf-adapter/src/channel-registry-do.ts +58 -0
  75. package/packages/cf-adapter/src/config-loader.ts +62 -0
  76. package/packages/cf-adapter/src/connection-do.ts +181 -31
  77. package/packages/cf-adapter/src/d1-account-store.ts +198 -0
  78. package/packages/cf-adapter/src/env.ts +58 -8
  79. package/packages/cf-adapter/src/index.ts +11 -9
  80. package/packages/cf-adapter/tests/cf-harness.ts +11 -1
  81. package/packages/cf-adapter/tests/cf-integration.test.ts +2 -1
  82. package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
  83. package/packages/cf-adapter/tests/config-loader.test.ts +22 -0
  84. package/packages/cf-adapter/tests/connection-do-channel-registration.test.ts +37 -0
  85. package/packages/cf-adapter/tests/connection-do-no-batching-reservation.test.ts +52 -0
  86. package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +166 -0
  87. package/packages/cf-adapter/tests/connection-do.test.ts +40 -0
  88. package/packages/cf-adapter/tests/d1-account-store.test.ts +226 -0
  89. package/packages/cf-adapter/tests/raw-modules.d.ts +11 -0
  90. package/packages/cf-adapter/tests/worker/main.ts +9 -1
  91. package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
  92. package/packages/cf-adapter/wrangler.test.toml +18 -1
  93. package/packages/in-memory-runtime/package.json +1 -1
  94. package/packages/in-memory-runtime/src/in-memory-runtime.ts +14 -28
  95. package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +19 -0
  96. package/packages/irc-core/package.json +8 -2
  97. package/packages/irc-core/scripts/generate-build-info.mjs +31 -0
  98. package/packages/irc-core/src/caps/capabilities.ts +23 -2
  99. package/packages/irc-core/src/case-fold.ts +64 -0
  100. package/packages/irc-core/src/cloak.ts +1 -1
  101. package/packages/irc-core/src/commands/cap.ts +8 -1
  102. package/packages/irc-core/src/commands/index.ts +11 -0
  103. package/packages/irc-core/src/commands/invite.ts +6 -9
  104. package/packages/irc-core/src/commands/ison.ts +61 -0
  105. package/packages/irc-core/src/commands/isupport.ts +1 -1
  106. package/packages/irc-core/src/commands/join.ts +4 -3
  107. package/packages/irc-core/src/commands/kick.ts +4 -11
  108. package/packages/irc-core/src/commands/list.ts +5 -4
  109. package/packages/irc-core/src/commands/mode.ts +5 -9
  110. package/packages/irc-core/src/commands/motd-lines.ts +127 -0
  111. package/packages/irc-core/src/commands/motd.ts +6 -110
  112. package/packages/irc-core/src/commands/oper.ts +151 -0
  113. package/packages/irc-core/src/commands/quit.ts +12 -0
  114. package/packages/irc-core/src/commands/registration.ts +26 -19
  115. package/packages/irc-core/src/commands/sasl.ts +72 -9
  116. package/packages/irc-core/src/commands/server-info.ts +129 -0
  117. package/packages/irc-core/src/commands/tagmsg.ts +205 -0
  118. package/packages/irc-core/src/commands/userhost.ts +84 -0
  119. package/packages/irc-core/src/commands/whowas.ts +113 -0
  120. package/packages/irc-core/src/config.ts +84 -3
  121. package/packages/irc-core/src/credential-hashing.ts +124 -0
  122. package/packages/irc-core/src/effects.ts +6 -29
  123. package/packages/irc-core/src/index.ts +3 -0
  124. package/packages/irc-core/src/ports.ts +240 -7
  125. package/packages/irc-core/src/protocol/numerics.ts +14 -8
  126. package/packages/irc-core/src/types.ts +60 -1
  127. package/packages/irc-core/tests/account-store.test.ts +131 -0
  128. package/packages/irc-core/tests/caps/capabilities.test.ts +4 -3
  129. package/packages/irc-core/tests/case-fold.test.ts +80 -0
  130. package/packages/irc-core/tests/commands/cap.test.ts +33 -1
  131. package/packages/irc-core/tests/commands/ison.test.ts +166 -0
  132. package/packages/irc-core/tests/commands/kick.test.ts +15 -0
  133. package/packages/irc-core/tests/commands/oper.test.ts +257 -0
  134. package/packages/irc-core/tests/commands/quit.test.ts +69 -2
  135. package/packages/irc-core/tests/commands/registration.test.ts +256 -19
  136. package/packages/irc-core/tests/commands/sasl.test.ts +118 -10
  137. package/packages/irc-core/tests/commands/server-info.test.ts +274 -0
  138. package/packages/irc-core/tests/commands/tagmsg.test.ts +662 -0
  139. package/packages/irc-core/tests/commands/userhost.test.ts +264 -0
  140. package/packages/irc-core/tests/commands/who.test.ts +22 -4
  141. package/packages/irc-core/tests/commands/whois.test.ts +8 -1
  142. package/packages/irc-core/tests/commands/whowas.test.ts +312 -0
  143. package/packages/irc-core/tests/config.test.ts +170 -1
  144. package/packages/irc-core/tests/credential-hashing.test.ts +170 -0
  145. package/packages/irc-core/tests/effects.test.ts +0 -27
  146. package/packages/irc-core/tests/nick-history-store.test.ts +162 -0
  147. package/packages/irc-core/tests/numerics.test.ts +12 -0
  148. package/packages/irc-core/tests/types.test.ts +35 -1
  149. package/packages/irc-core/tsconfig.build.json +1 -1
  150. package/packages/irc-core/tsconfig.test.json +1 -1
  151. package/packages/irc-server/package.json +1 -1
  152. package/packages/irc-server/src/actor.ts +182 -14
  153. package/packages/irc-server/src/dispatch.ts +0 -3
  154. package/packages/irc-server/src/index.ts +10 -2
  155. package/packages/irc-server/src/routing.ts +21 -0
  156. package/packages/irc-server/src/transport.ts +101 -0
  157. package/packages/irc-server/tests/actor.test.ts +617 -1
  158. package/packages/irc-server/tests/dispatch.test.ts +0 -17
  159. package/packages/irc-server/tests/routing.test.ts +7 -0
  160. package/packages/irc-server/tests/transport.test.ts +230 -0
  161. package/packages/irc-test-support/package.json +1 -1
  162. package/packages/irc-test-support/src/harness.ts +44 -9
  163. package/packages/irc-test-support/src/in-memory-harness.ts +78 -13
  164. package/packages/irc-test-support/src/index.ts +3 -0
  165. package/packages/irc-test-support/src/scenarios.ts +132 -2
  166. package/packages/irc-test-support/tests/in-memory-harness.test.ts +2 -1
  167. package/packages/irc-test-support/tests/in-memory-scenarios.test.ts +23 -9
  168. package/pnpm-workspace.yaml +9 -0
  169. package/tools/ci-hardening/package.json +1 -1
  170. package/tools/package.json +4 -0
  171. package/tools/seed-aws-accounts.ts +79 -0
  172. package/tools/seed-cf-accounts.ts +104 -0
  173. package/tools/tcp-ws-forwarder/package.json +1 -1
  174. package/AGENTS.md +0 -5
  175. package/MOTD.txt +0 -3
  176. package/PLAN-FIXES.md +0 -358
  177. package/PLAN.md +0 -420
  178. package/dashboards/cloudwatch-irc.json +0 -139
  179. package/docs/AWS-Adapter-Architecture.md +0 -494
  180. package/docs/AWS-Deployment.md +0 -1107
  181. package/docs/Cloudflare-Deployment-Guide.md +0 -660
  182. package/docs/Home.md +0 -1
  183. package/docs/Observability.md +0 -87
  184. package/docs/PlanIRCv3Websocket.md +0 -489
  185. package/docs/PlanWebClient.md +0 -451
  186. package/docs/Release-Process.md +0 -440
  187. package/progress.md +0 -107
  188. package/tickets.md +0 -2485
  189. package/webircgateway/LICENSE +0 -201
  190. package/webircgateway/Makefile +0 -44
  191. package/webircgateway/README.md +0 -134
  192. package/webircgateway/config.conf.example +0 -135
  193. package/webircgateway/go.mod +0 -16
  194. package/webircgateway/go.sum +0 -89
  195. package/webircgateway/main.go +0 -118
  196. package/webircgateway/pkg/dnsbl/dnsbl.go +0 -121
  197. package/webircgateway/pkg/identd/identd.go +0 -86
  198. package/webircgateway/pkg/identd/rpcclient.go +0 -59
  199. package/webircgateway/pkg/irc/isupport.go +0 -56
  200. package/webircgateway/pkg/irc/message.go +0 -217
  201. package/webircgateway/pkg/irc/state.go +0 -79
  202. package/webircgateway/pkg/proxy/proxy.go +0 -129
  203. package/webircgateway/pkg/proxy/server.go +0 -237
  204. package/webircgateway/pkg/recaptcha/recaptcha.go +0 -59
  205. package/webircgateway/pkg/webircgateway/client.go +0 -741
  206. package/webircgateway/pkg/webircgateway/client_command_handlers.go +0 -495
  207. package/webircgateway/pkg/webircgateway/config.go +0 -385
  208. package/webircgateway/pkg/webircgateway/gateway.go +0 -278
  209. package/webircgateway/pkg/webircgateway/gateway_utils.go +0 -133
  210. package/webircgateway/pkg/webircgateway/hooks.go +0 -152
  211. package/webircgateway/pkg/webircgateway/letsencrypt.go +0 -41
  212. package/webircgateway/pkg/webircgateway/messagetags.go +0 -103
  213. package/webircgateway/pkg/webircgateway/transport_kiwiirc.go +0 -206
  214. package/webircgateway/pkg/webircgateway/transport_sockjs.go +0 -107
  215. package/webircgateway/pkg/webircgateway/transport_tcp.go +0 -113
  216. package/webircgateway/pkg/webircgateway/transport_websocket.go +0 -126
  217. package/webircgateway/pkg/webircgateway/utils.go +0 -147
  218. package/webircgateway/plugins/example/plugin.go +0 -11
  219. package/webircgateway/plugins/stats/plugin.go +0 -52
  220. package/webircgateway/staticcheck.conf +0 -1
  221. 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
- }