Compare commits
17 commits
c055342313
...
600930d466
Author | SHA1 | Date | |
---|---|---|---|
Gabriel Simmer | 600930d466 | ||
Gabriel Simmer | d21c5be4ae | ||
Gabriel Simmer | a38931bde1 | ||
Gabriel Simmer | 566c122b5e | ||
Gabriel Simmer | faeb46d188 | ||
bb234c8348 | |||
07502541e4 | |||
3667102e72 | |||
d65c1654b8 | |||
a3716dc2d2 | |||
4682bbef66 | |||
9a36e6730d | |||
29bdc1aa45 | |||
0ced56a155 | |||
d5dd194b01 | |||
ae203388e1 | |||
965ce9cdb9 |
|
@ -1,2 +0,0 @@
|
||||||
[b4]
|
|
||||||
send-series-to = ~emersion/soju-dev@lists.sr.ht
|
|
|
@ -4,7 +4,7 @@ packages:
|
||||||
- scdoc
|
- scdoc
|
||||||
- postgresql
|
- postgresql
|
||||||
sources:
|
sources:
|
||||||
- https://git.sr.ht/~emersion/soju
|
- https://codeberg.org/emersion/soju.git
|
||||||
tasks:
|
tasks:
|
||||||
- build: |
|
- build: |
|
||||||
cd soju
|
cd soju
|
||||||
|
|
19
Containerfile
Normal file
19
Containerfile
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
FROM docker.io/golang:1.21.6-bookworm as build
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=0 \
|
||||||
|
GOOS=linux \
|
||||||
|
GOFLAGS="-tags=moderncsqlite"
|
||||||
|
|
||||||
|
WORKDIR /build
|
||||||
|
|
||||||
|
COPY . /build
|
||||||
|
|
||||||
|
RUN make soju
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
COPY --from=build /build/soju /app/soju
|
||||||
|
COPY --from=build /build/sojuctl /app/sojuctl
|
||||||
|
COPY --from=build /build/sojudb /app/sojudb
|
||||||
|
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
|
||||||
|
CMD ["/app/soju"]
|
9
Makefile
9
Makefile
|
@ -11,8 +11,8 @@ RUNDIR ?= /run
|
||||||
sharedstatedir := /var/lib
|
sharedstatedir := /var/lib
|
||||||
config_path := $(SYSCONFDIR)/soju/config
|
config_path := $(SYSCONFDIR)/soju/config
|
||||||
admin_socket_path := $(RUNDIR)/soju/admin
|
admin_socket_path := $(RUNDIR)/soju/admin
|
||||||
goldflags := -X 'git.sr.ht/~emersion/soju/config.DefaultPath=$(config_path)' \
|
goldflags := -X 'codeberg.org/emersion/soju/config.DefaultPath=$(config_path)' \
|
||||||
-X 'git.sr.ht/~emersion/soju/config.DefaultUnixAdminPath=$(admin_socket_path)'
|
-X 'codeberg.org/emersion/soju/config.DefaultUnixAdminPath=$(admin_socket_path)'
|
||||||
goflags := $(GOFLAGS) -ldflags="$(goldflags)"
|
goflags := $(GOFLAGS) -ldflags="$(goldflags)"
|
||||||
commands := soju sojuctl sojudb
|
commands := soju sojuctl sojudb
|
||||||
man_pages := doc/soju.1 doc/sojuctl.1
|
man_pages := doc/soju.1 doc/sojuctl.1
|
||||||
|
@ -38,4 +38,9 @@ install:
|
||||||
cp -f $(man_pages) $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
cp -f $(man_pages) $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
|
||||||
[ -f $(DESTDIR)$(config_path) ] || cp -f config.in $(DESTDIR)$(config_path)
|
[ -f $(DESTDIR)$(config_path) ] || cp -f config.in $(DESTDIR)$(config_path)
|
||||||
|
|
||||||
|
container:
|
||||||
|
buildah manifest create soju-multiarch
|
||||||
|
buildah bud --tag soju:latest --manifest soju-multiarch --arch amd64 .
|
||||||
|
buildah bud --tag soju:latest --manifest soju-multiarch --arch arm64 .
|
||||||
|
|
||||||
.PHONY: soju sojudb sojuctl clean install
|
.PHONY: soju sojudb sojuctl clean install
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
# [soju]
|
# [soju]
|
||||||
|
|
||||||
[![builds.sr.ht status](https://builds.sr.ht/~emersion/soju/commits/master.svg)](https://builds.sr.ht/~emersion/soju/commits/master?)
|
|
||||||
|
|
||||||
soju is a user-friendly IRC bouncer. soju connects to upstream IRC servers on
|
soju is a user-friendly IRC bouncer. soju connects to upstream IRC servers on
|
||||||
behalf of the user to provide extra functionality. soju supports many features
|
behalf of the user to provide extra functionality. soju supports many features
|
||||||
such as multiple users, numerous [IRCv3] extensions, chat history playback and
|
such as multiple users, numerous [IRCv3] extensions, chat history playback and
|
||||||
|
@ -35,8 +33,8 @@ build with PAM authentication support, set `GOFLAGS="-tags=pam"`.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Send patches on the [mailing list] or on [GitHub], report bugs on the
|
Send patches on [Codeberg] or on [GitHub], report bugs on the [issue tracker].
|
||||||
[issue tracker]. Discuss in [#soju on Libera Chat][IRC channel].
|
Discuss in [#soju on Libera Chat][IRC channel].
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@ -47,7 +45,7 @@ Copyright (C) 2020 The soju Contributors
|
||||||
[soju]: https://soju.im
|
[soju]: https://soju.im
|
||||||
[Getting started]: doc/getting-started.md
|
[Getting started]: doc/getting-started.md
|
||||||
[Man page]: https://soju.im/doc/soju.1.html
|
[Man page]: https://soju.im/doc/soju.1.html
|
||||||
[mailing list]: https://lists.sr.ht/~emersion/soju-dev
|
[Codeberg]: https://codeberg.org/emersion/soju
|
||||||
[GitHub]: https://github.com/emersion/soju
|
[GitHub]: https://github.com/emersion/soju
|
||||||
[issue tracker]: https://todo.sr.ht/~emersion/soju
|
[issue tracker]: https://todo.sr.ht/~emersion/soju
|
||||||
[IRC channel]: ircs://irc.libera.chat/#soju
|
[IRC channel]: ircs://irc.libera.chat/#soju
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Authenticator interface{}
|
type Authenticator interface{}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type internal struct{}
|
type internal struct{}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type oauth2 struct {
|
type oauth2 struct {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/msteinert/pam/v2"
|
"github.com/msteinert/pam/v2"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pamAuth struct{}
|
type pamAuth struct{}
|
||||||
|
|
|
@ -22,12 +22,12 @@ import (
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju"
|
"codeberg.org/emersion/soju"
|
||||||
"git.sr.ht/~emersion/soju/auth"
|
"codeberg.org/emersion/soju/auth"
|
||||||
"git.sr.ht/~emersion/soju/config"
|
"codeberg.org/emersion/soju/config"
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/fileupload"
|
"codeberg.org/emersion/soju/fileupload"
|
||||||
"git.sr.ht/~emersion/soju/identd"
|
"codeberg.org/emersion/soju/identd"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TCP keep-alive interval for downstream TCP connections
|
// TCP keep-alive interval for downstream TCP connections
|
||||||
|
@ -113,6 +113,7 @@ func loadConfig() (*config.Server, *soju.Config, error) {
|
||||||
MOTD: motd,
|
MOTD: motd,
|
||||||
Auth: auth,
|
Auth: auth,
|
||||||
FileUploader: fileUploader,
|
FileUploader: fileUploader,
|
||||||
|
FileCdn: raw.FileCdn,
|
||||||
}
|
}
|
||||||
return raw, cfg, nil
|
return raw, cfg, nil
|
||||||
}
|
}
|
||||||
|
@ -151,7 +152,6 @@ func main() {
|
||||||
srv := soju.NewServer(db)
|
srv := soju.NewServer(db)
|
||||||
srv.SetConfig(serverCfg)
|
srv.SetConfig(serverCfg)
|
||||||
srv.Logger = soju.NewLogger(log.Writer(), debug)
|
srv.Logger = soju.NewLogger(log.Writer(), debug)
|
||||||
|
|
||||||
fileUploadHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
fileUploadHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
cfg := srv.Config()
|
cfg := srv.Config()
|
||||||
h := fileupload.Handler{
|
h := fileupload.Handler{
|
||||||
|
@ -159,6 +159,7 @@ func main() {
|
||||||
DB: db,
|
DB: db,
|
||||||
Auth: cfg.Auth,
|
Auth: cfg.Auth,
|
||||||
HTTPOrigins: cfg.HTTPOrigins,
|
HTTPOrigins: cfg.HTTPOrigins,
|
||||||
|
Cdn: cfg.FileCdn,
|
||||||
}
|
}
|
||||||
h.ServeHTTP(w, r)
|
h.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/config"
|
"codeberg.org/emersion/soju/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `usage: sojuctl [-config path] <command>
|
const usage = `usage: sojuctl [-config path] <command>
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/config"
|
"codeberg.org/emersion/soju/config"
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `usage: sojudb [-config path] <action> [options...]
|
const usage = `usage: sojudb [-config path] <action> [options...]
|
||||||
|
|
|
@ -83,6 +83,7 @@ type Server struct {
|
||||||
MsgStore MsgStore
|
MsgStore MsgStore
|
||||||
Auth Auth
|
Auth Auth
|
||||||
FileUpload *FileUpload
|
FileUpload *FileUpload
|
||||||
|
FileCdn string
|
||||||
|
|
||||||
HTTPOrigins []string
|
HTTPOrigins []string
|
||||||
HTTPIngress string
|
HTTPIngress string
|
||||||
|
@ -130,6 +131,7 @@ func Load(filename string) (*Server, error) {
|
||||||
Log []string `scfg:"log"`
|
Log []string `scfg:"log"`
|
||||||
Auth []string `scfg:"auth"`
|
Auth []string `scfg:"auth"`
|
||||||
FileUpload []string `scfg:"file-upload"`
|
FileUpload []string `scfg:"file-upload"`
|
||||||
|
FileCdn string `scfg:"file-cdn"`
|
||||||
HTTPOrigin []string `scfg:"http-origin"`
|
HTTPOrigin []string `scfg:"http-origin"`
|
||||||
HTTPIngress string `scfg:"http-ingress"`
|
HTTPIngress string `scfg:"http-ingress"`
|
||||||
AcceptProxyIP []string `scfg:"accept-proxy-ip"`
|
AcceptProxyIP []string `scfg:"accept-proxy-ip"`
|
||||||
|
@ -214,11 +216,17 @@ func Load(filename string) (*Server, error) {
|
||||||
if source == "" {
|
if source == "" {
|
||||||
return nil, fmt.Errorf("directive file-upload: driver %q requires a source", driver)
|
return nil, fmt.Errorf("directive file-upload: driver %q requires a source", driver)
|
||||||
}
|
}
|
||||||
|
case "s3":
|
||||||
|
if source == "" {
|
||||||
|
return nil, fmt.Errorf("directive file-upload: driver %q requires a source", driver)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("directive file-upload: unknown driver %q", driver)
|
return nil, fmt.Errorf("directive file-upload: unknown driver %q", driver)
|
||||||
}
|
}
|
||||||
srv.FileUpload = &FileUpload{driver, source}
|
srv.FileUpload = &FileUpload{driver, source}
|
||||||
}
|
}
|
||||||
|
srv.FileCdn = raw.FileCdn
|
||||||
for _, origin := range raw.HTTPOrigin {
|
for _, origin := range raw.HTTPOrigin {
|
||||||
if _, err := path.Match(origin, origin); err != nil {
|
if _, err := path.Match(origin, origin); err != nil {
|
||||||
return nil, fmt.Errorf("directive http-origin: %v", err)
|
return nil, fmt.Errorf("directive http-origin: %v", err)
|
||||||
|
|
9
conn.go
9
conn.go
|
@ -3,7 +3,6 @@ package soju
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -158,7 +157,7 @@ func newConn(srv *Server, ic ircConn, options *connOptions) *conn {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := c.conn.Close(); err != nil && !errors.Is(err, net.ErrClosed) {
|
if err := c.Close(); err != nil && !errors.Is(err, net.ErrClosed) {
|
||||||
c.logger.Printf("failed to close connection: %v", err)
|
c.logger.Printf("failed to close connection: %v", err)
|
||||||
} else {
|
} else {
|
||||||
c.logger.Debugf("connection closed")
|
c.logger.Debugf("connection closed")
|
||||||
|
@ -185,7 +184,7 @@ func (c *conn) Close() error {
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
||||||
if c.closed {
|
if c.closed {
|
||||||
return fmt.Errorf("connection already closed")
|
return net.ErrClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
err := c.conn.Close()
|
err := c.conn.Close()
|
||||||
|
@ -195,6 +194,10 @@ func (c *conn) Close() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read reads an incoming message. It must be called from a single goroutine
|
||||||
|
// at a time.
|
||||||
|
//
|
||||||
|
// io.EOF is returned when there are no more messages to read.
|
||||||
func (c *conn) ReadMessage() (*irc.Message, error) {
|
func (c *conn) ReadMessage() (*irc.Message, error) {
|
||||||
msg, err := c.conn.ReadMessage()
|
msg, err := c.conn.ReadMessage()
|
||||||
if errors.Is(err, net.ErrClosed) {
|
if errors.Is(err, net.ErrClosed) {
|
||||||
|
|
|
@ -70,12 +70,6 @@ outgoing messages:
|
||||||
/set irc_reconnect_rejoin off
|
/set irc_reconnect_rejoin off
|
||||||
/set net_throttle off
|
/set net_throttle off
|
||||||
|
|
||||||
Older Hexchat versions (without the [hexchat password length fix]) do not
|
|
||||||
support long passwords, which include personal access tokens from sourcehut with
|
|
||||||
limited scope. To work around this issue for sourcehut, [generate a sourcehut
|
|
||||||
personal access token] without limiting the grant (by not selecting any
|
|
||||||
permissions).
|
|
||||||
|
|
||||||
# [irssi]
|
# [irssi]
|
||||||
|
|
||||||
To connect irssi to a network, for example Libera Chat:
|
To connect irssi to a network, for example Libera Chat:
|
||||||
|
@ -120,6 +114,5 @@ See `/help cap` for more information.
|
||||||
[read_marker.py]: https://weechat.org/scripts/source/read_marker.py.html/
|
[read_marker.py]: https://weechat.org/scripts/source/read_marker.py.html/
|
||||||
[Hexchat]: https://hexchat.github.io/
|
[Hexchat]: https://hexchat.github.io/
|
||||||
[hexchat password length fix]: https://github.com/hexchat/hexchat/commit/778047bc65e529804c3342ee0f3a8d5d7550fde5
|
[hexchat password length fix]: https://github.com/hexchat/hexchat/commit/778047bc65e529804c3342ee0f3a8d5d7550fde5
|
||||||
[generate a sourcehut personal access token]: https://meta.sr.ht/oauth2/personal-token
|
|
||||||
[Emacs]: https://www.gnu.org/software/emacs/
|
[Emacs]: https://www.gnu.org/software/emacs/
|
||||||
[irssi]: https://irssi.org/
|
[irssi]: https://irssi.org/
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `usage: migrate-db <source database> <destination database>
|
const usage = `usage: migrate-db <source database> <destination database>
|
||||||
|
|
|
@ -14,9 +14,9 @@ import (
|
||||||
|
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/msgstore"
|
"codeberg.org/emersion/soju/msgstore"
|
||||||
"git.sr.ht/~emersion/soju/msgstore/znclog"
|
"codeberg.org/emersion/soju/msgstore/znclog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `usage: migrate-logs <source logs> <destination database>
|
const usage = `usage: migrate-logs <source logs> <destination database>
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/config"
|
"codeberg.org/emersion/soju/config"
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `usage: znc-import [options...] <znc config path>
|
const usage = `usage: znc-import [options...] <znc config path>
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
promcollectors "github.com/prometheus/client_golang/prometheus/collectors"
|
promcollectors "github.com/prometheus/client_golang/prometheus/collectors"
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
promcollectors "github.com/prometheus/client_golang/prometheus/collectors"
|
promcollectors "github.com/prometheus/client_golang/prometheus/collectors"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
|
@ -166,6 +166,10 @@ The following directives are supported:
|
||||||
Supported drivers:
|
Supported drivers:
|
||||||
|
|
||||||
- _fs_ stores uploaded files on disk. _source_ is required.
|
- _fs_ stores uploaded files on disk. _source_ is required.
|
||||||
|
- _s3_ stores uploaded files in an s3-compatible api. _source_ is required and should be <s3 endpoint>/<bucket name>.
|
||||||
|
|
||||||
|
*file-cdn* <url>
|
||||||
|
Set the base url/cdn for file uploads, useful if using s3.
|
||||||
|
|
||||||
*http-origin* <patterns...>
|
*http-origin* <patterns...>
|
||||||
List of allowed HTTP origins for WebSocket listeners. The parameters are
|
List of allowed HTTP origins for WebSocket listeners. The parameters are
|
||||||
|
@ -296,7 +300,7 @@ character.
|
||||||
of an IRC server:
|
of an IRC server:
|
||||||
|
|
||||||
```
|
```
|
||||||
openssl s_client -connect irc.example.org:6697 </dev/null 2>/dev/null | openssl x509 -fingerprint -sha512 -noout -in /dev/stdin
|
openssl s_client -connect irc.example.org:6697 -verify_quiet </dev/null | openssl x509 -fingerprint -sha512 -noout -in /dev/stdin
|
||||||
```
|
```
|
||||||
|
|
||||||
*-nick* <nickname>
|
*-nick* <nickname>
|
||||||
|
@ -556,7 +560,7 @@ character.
|
||||||
|
|
||||||
Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other
|
Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other
|
||||||
open-source contributors. For more information about soju development, see
|
open-source contributors. For more information about soju development, see
|
||||||
<https://sr.ht/~emersion/soju>.
|
<https://soju.im>.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ file. sojuctl needs to be run with write permissions on the soju admin socket.
|
||||||
|
|
||||||
Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other
|
Maintained by Simon Ser <contact@emersion.fr>, who is assisted by other
|
||||||
open-source contributors. For more information about soju development, see
|
open-source contributors. For more information about soju development, see
|
||||||
<https://sr.ht/~emersion/soju>.
|
<https://soju.im>.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,10 @@ import (
|
||||||
"github.com/emersion/go-sasl"
|
"github.com/emersion/go-sasl"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/auth"
|
"codeberg.org/emersion/soju/auth"
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/msgstore"
|
"codeberg.org/emersion/soju/msgstore"
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ircError struct {
|
type ircError struct {
|
||||||
|
@ -328,7 +328,7 @@ func serverSASLMechanisms(srv *Server) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type downstreamConn struct {
|
type downstreamConn struct {
|
||||||
conn
|
*conn
|
||||||
|
|
||||||
id uint64
|
id uint64
|
||||||
|
|
||||||
|
@ -363,7 +363,7 @@ func newDownstreamConn(srv *Server, ic ircConn, id uint64) *downstreamConn {
|
||||||
options := connOptions{Logger: logger}
|
options := connOptions{Logger: logger}
|
||||||
cm := xirc.CaseMappingASCII
|
cm := xirc.CaseMappingASCII
|
||||||
dc := &downstreamConn{
|
dc := &downstreamConn{
|
||||||
conn: *newConn(srv, ic, &options),
|
conn: newConn(srv, ic, &options),
|
||||||
id: id,
|
id: id,
|
||||||
nick: "*",
|
nick: "*",
|
||||||
nickCM: "*",
|
nickCM: "*",
|
||||||
|
@ -2142,7 +2142,6 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||||
// Clients will use the first mask to match RPL_ENDOFWHO
|
// Clients will use the first mask to match RPL_ENDOFWHO
|
||||||
endOfWhoToken := msg.Params[0]
|
endOfWhoToken := msg.Params[0]
|
||||||
|
|
||||||
// TODO: add support for WHOX mask2
|
|
||||||
mask := msg.Params[0]
|
mask := msg.Params[0]
|
||||||
var options string
|
var options string
|
||||||
if len(msg.Params) > 1 {
|
if len(msg.Params) > 1 {
|
||||||
|
@ -2154,8 +2153,10 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||||
// TODO: support mixed bouncer/upstream WHO queries
|
// TODO: support mixed bouncer/upstream WHO queries
|
||||||
maskCM := dc.casemap(mask)
|
maskCM := dc.casemap(mask)
|
||||||
if dc.network == nil && maskCM == dc.nickCM {
|
if dc.network == nil && maskCM == dc.nickCM {
|
||||||
// TODO: support AWAY (H/G) in self WHO reply
|
|
||||||
flags := "H"
|
flags := "H"
|
||||||
|
if dc.away != nil {
|
||||||
|
flags = "G"
|
||||||
|
}
|
||||||
if dc.user.Admin {
|
if dc.user.Admin {
|
||||||
flags += "*"
|
flags += "*"
|
||||||
}
|
}
|
||||||
|
@ -2230,6 +2231,23 @@ func (dc *downstreamConn) handleMessageRegistered(ctx context.Context, msg *irc.
|
||||||
}
|
}
|
||||||
if uc.isChannel(mask) {
|
if uc.isChannel(mask) {
|
||||||
info.Channel = mask
|
info.Channel = mask
|
||||||
|
|
||||||
|
// Set channel membership prefixes from cached NAMES reply
|
||||||
|
ch := uc.channels.Get(info.Channel)
|
||||||
|
memberships := ch.Members.Get(info.Nickname)
|
||||||
|
prefixes := formatMemberPrefix(*memberships, dc)
|
||||||
|
|
||||||
|
// Channel membership prefixes are listed after away status ('G'/'H')
|
||||||
|
// and optional server operator indicator ('*')
|
||||||
|
i := strings.IndexFunc(info.Flags, func(f rune) bool {
|
||||||
|
return f != 'G' && f != 'H' && f != '*'
|
||||||
|
})
|
||||||
|
|
||||||
|
if i == -1 {
|
||||||
|
info.Flags += prefixes
|
||||||
|
} else {
|
||||||
|
info.Flags = info.Flags[:i] + prefixes + info.Flags[i:]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
|
dc.SendMessage(ctx, xirc.GenerateWHOXReply(fields, &info))
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/auth"
|
"codeberg.org/emersion/soju/auth"
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxSize = 50 * 1024 * 1024 // 50 MiB
|
const maxSize = 50 * 1024 * 1024 // 50 MiB
|
||||||
|
@ -58,13 +58,15 @@ var primaryExts = map[string]string{
|
||||||
|
|
||||||
type Uploader interface {
|
type Uploader interface {
|
||||||
load(filename string) (basename string, modTime time.Time, content io.ReadSeekCloser, err error)
|
load(filename string) (basename string, modTime time.Time, content io.ReadSeekCloser, err error)
|
||||||
store(r io.Reader, username, mimeType, basename string) (outFilename string, err error)
|
store(r io.Reader, username, mimeType, basename string, contentLength int64) (outFilename string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(driver, source string) (Uploader, error) {
|
func New(driver, source string) (Uploader, error) {
|
||||||
switch driver {
|
switch driver {
|
||||||
case "fs":
|
case "fs":
|
||||||
return &fs{source}, nil
|
return &fs{source}, nil
|
||||||
|
case "s3":
|
||||||
|
return &s3{source}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unknown file upload driver %q", driver)
|
return nil, fmt.Errorf("unknown file upload driver %q", driver)
|
||||||
}
|
}
|
||||||
|
@ -75,6 +77,7 @@ type Handler struct {
|
||||||
Auth auth.Authenticator
|
Auth auth.Authenticator
|
||||||
DB database.Database
|
DB database.Database
|
||||||
HTTPOrigins []string
|
HTTPOrigins []string
|
||||||
|
Cdn string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) checkOrigin(reqOrigin string) bool {
|
func (h *Handler) checkOrigin(reqOrigin string) bool {
|
||||||
|
@ -151,6 +154,12 @@ func (h *Handler) fetch(resp http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.Cdn != "" {
|
||||||
|
resp.Header().Set("Location", h.Cdn+"/"+filename)
|
||||||
|
resp.WriteHeader(http.StatusCreated)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
basename, modTime, content, err := h.Uploader.load(filename)
|
basename, modTime, content, err := h.Uploader.load(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(resp, "failed to open file", http.StatusNotFound)
|
http.Error(resp, "failed to open file", http.StatusNotFound)
|
||||||
|
@ -276,13 +285,18 @@ func (h *Handler) store(resp http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := &limitedReader{r: req.Body, n: maxSize}
|
r := &limitedReader{r: req.Body, n: maxSize}
|
||||||
outFilename, err := h.Uploader.store(r, username, mimeType, basename)
|
outFilename, err := h.Uploader.store(r, username, mimeType, basename, req.ContentLength)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
http.Error(resp, "failed to write file", http.StatusInternalServerError)
|
http.Error(resp, "failed to write file", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.Header().Set("Location", "/uploads/"+outFilename)
|
if h.Cdn != "" {
|
||||||
|
resp.Header().Set("Location", h.Cdn+"/"+outFilename)
|
||||||
|
} else {
|
||||||
|
resp.Header().Set("Location", "/uploads/"+outFilename)
|
||||||
|
}
|
||||||
resp.WriteHeader(http.StatusCreated)
|
resp.WriteHeader(http.StatusCreated)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ func (fs *fs) load(filename string) (basename string, modTime time.Time, content
|
||||||
return basename, fi.ModTime(), f, nil
|
return basename, fi.ModTime(), f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *fs) store(r io.Reader, username, mimeType, origBasename string) (outFilename string, err error) {
|
func (fs *fs) store(r io.Reader, username, mimeType, origBasename string, contentLength int64) (outFilename string, err error) {
|
||||||
origBasename = filepath.Base(origBasename)
|
origBasename = filepath.Base(origBasename)
|
||||||
|
|
||||||
var suffix string
|
var suffix string
|
||||||
|
|
55
fileupload/s3.go
Normal file
55
fileupload/s3.go
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package fileupload
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/aws/aws-sdk-go-v2/aws"
|
||||||
|
"github.com/aws/aws-sdk-go-v2/config"
|
||||||
|
awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type s3 struct {
|
||||||
|
endpoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
// load implements Uploader.
|
||||||
|
func (s *s3) load(filename string) (basename string, modTime time.Time, content io.ReadSeekCloser, err error) {
|
||||||
|
panic("unimplemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// store implements Uploader.
|
||||||
|
func (s *s3) store(r io.Reader, username string, mimeType string, basename string, contentLength int64) (outFilename string, err error) {
|
||||||
|
split := strings.Split(s.endpoint, "/")
|
||||||
|
bucket := split[len(split)-1]
|
||||||
|
filename := url.PathEscape(username) + "/" + uuid.NewString()
|
||||||
|
|
||||||
|
endpointResolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
|
||||||
|
return aws.Endpoint{
|
||||||
|
URL: s.endpoint,
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
cfg, err := config.LoadDefaultConfig(context.TODO(),
|
||||||
|
config.WithEndpointResolverWithOptions(endpointResolver),
|
||||||
|
config.WithRegion("auto"),
|
||||||
|
)
|
||||||
|
|
||||||
|
s3client := awss3.NewFromConfig(cfg)
|
||||||
|
_, err = s3client.PutObject(context.TODO(), &awss3.PutObjectInput{
|
||||||
|
Bucket: &bucket,
|
||||||
|
Key: &filename,
|
||||||
|
ContentType: &mimeType,
|
||||||
|
ContentLength: &contentLength,
|
||||||
|
Body: r,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to upload to s3 api: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename, nil
|
||||||
|
}
|
25
go.mod
25
go.mod
|
@ -1,4 +1,4 @@
|
||||||
module git.sr.ht/~emersion/soju
|
module codeberg.org/emersion/soju
|
||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
|
@ -21,6 +21,29 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.30.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.23 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.13 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.15 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.13 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 // indirect
|
||||||
|
github.com/aws/smithy-go v1.20.3 // indirect
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/aws/aws-sdk-go v1.54.11
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.27.23
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.57.1
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
|
43
go.sum
43
go.sum
|
@ -7,6 +7,44 @@ git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9 h1:Ahny8Ud1LjVMMA
|
||||||
git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA=
|
git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA=
|
||||||
github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k=
|
github.com/SherClockHolmes/webpush-go v1.3.0 h1:CAu3FvEE9QS4drc3iKNgpBWFfGqNthKlZhp5QpYnu6k=
|
||||||
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw=
|
github.com/SherClockHolmes/webpush-go v1.3.0/go.mod h1:AxRHmJuYwKGG1PVgYzToik1lphQvDnqFYDqimHvwhIw=
|
||||||
|
github.com/aws/aws-sdk-go v1.54.11 h1:Zxuv/R+IVS0B66yz4uezhxH9FN9/G2nbxejYqAMFjxk=
|
||||||
|
github.com/aws/aws-sdk-go v1.54.11/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.30.1 h1:4y/5Dvfrhd1MxRDD77SrfsDaj8kUkkljU7XE83NPV+o=
|
||||||
|
github.com/aws/aws-sdk-go-v2 v1.30.1/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.27.23 h1:Cr/gJEa9NAS7CDAjbnB7tHYb3aLZI2gVggfmSAasDac=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.27.23/go.mod h1:WMMYHqLCFu5LH05mFOF5tsq1PGEMfKbu083VKqLCd0o=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.23 h1:G1CfmLVoO2TdQ8z9dW+JBc/r8+MqyPQhXCafNZcXVZo=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.23/go.mod h1:V/DvSURn6kKgcuKEk4qwSwb/fZ2d++FFARtWSbXnLqY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 h1:Aznqksmd6Rfv2HQN9cpqIV/lQRMaIpJkLLaJ1ZI76no=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9/go.mod h1:WQr3MY7AxGNxaqAtsDWn+fBxmd4XvLkzeqQ8P1VM0/w=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 h1:5SAoZ4jYpGH4721ZNoS1znQrhOfZinOhc4XuTXx/nVc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13/go.mod h1:+rdA6ZLpaSeM7tSg/B0IEDinCIBJGmW8rKDFkYpP04g=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 h1:WIijqeaAO7TYFLbhsZmi2rgLEAtWOC1LhxCAVTJlSKw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13/go.mod h1:i+kbfa76PQbWw/ULoWnp51EYVWH4ENln76fLQE3lXT8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.13 h1:THZJJ6TU/FOiM7DZFnisYV9d49oxXWUzsVIMTuf3VNU=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.13/go.mod h1:VISUTg6n+uBaYIWPBaIG0jk7mbBxm7DUqBtU2cUDDWI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.15 h1:2jyRZ9rVIMisyQRnhSS/SqlckveoxXneIumECVFP91Y=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.15/go.mod h1:bDRG3m382v1KJBk1cKz7wIajg87/61EiiymEyfLvAe0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 h1:I9zMeF107l0rJrpnHpjEiiTSCKYAIw8mALiXcPsGBiA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15/go.mod h1:9xWJ3Q/S6Ojusz1UIkfycgD1mGirJfLLKqq3LPT7WN8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.13 h1:Eq2THzHt6P41mpjS2sUzz/3dJYFRqdWZ+vQaEMm98EM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.13/go.mod h1:FgwTca6puegxgCInYwGjmd4tB9195Dd6LCuA+8MjpWw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.57.1 h1:aHPtNY87GZ214N4rShgIo+5JQz7ICrJ50i17JbueUTw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.57.1/go.mod h1:hdV0NTYd0RwV4FvNKhKUNbPLZoq9CTr/lke+3I7aCAI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 h1:p1GahKIjyMDZtiKoIn0/jAj/TkMzfzndDv5+zi2Mhgc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.22.1/go.mod h1:/vWdhoIoYA5hYoPZ6fm7Sv4d8701PiG5VKe8/pPJL60=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.1 h1:lCEv9f8f+zJ8kcFeAjRZsekLd/x5SAm96Cva+VbUdo8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.1/go.mod h1:xyFHA4zGxgYkdD73VeezHt3vSKEG9EmFnGwoKlP00u4=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 h1:+woJ607dllHJQtsnJLi52ycuqHMwlW+Wqm2Ppsfp4nQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.30.1/go.mod h1:jiNR3JqT15Dm+QWq2SRgh0x0bCNSRP2L25+CqPNpJlQ=
|
||||||
|
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
|
||||||
|
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
@ -26,6 +64,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
@ -109,6 +151,7 @@ google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/irc.v4 v4.0.0 h1:5jsLkU2Tg+R2nGNqmkGCrciasyi4kNkDXhyZD+C31yY=
|
gopkg.in/irc.v4 v4.0.0 h1:5jsLkU2Tg+R2nGNqmkGCrciasyi4kNkDXhyZD+C31yY=
|
||||||
gopkg.in/irc.v4 v4.0.0/go.mod h1:BfjDz9MmuWW6OZY7iq4naOhudO8+QQCdO4Ko18jcsRE=
|
gopkg.in/irc.v4 v4.0.0/go.mod h1:BfjDz9MmuWW6OZY7iq4naOhudO8+QQCdO4Ko18jcsRE=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
15
irc.go
15
irc.go
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: generalize and move helpers to the xirc package
|
// TODO: generalize and move helpers to the xirc package
|
||||||
|
@ -194,6 +194,19 @@ func formatMemberPrefix(ms xirc.MembershipSet, dc *downstreamConn) string {
|
||||||
return string(prefixes)
|
return string(prefixes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove channel membership prefixes from flags
|
||||||
|
func stripMemberPrefixes(flags string, uc *upstreamConn) string {
|
||||||
|
return strings.Map(func(r rune) rune {
|
||||||
|
for _, v := range uc.availableMemberships {
|
||||||
|
if byte(r) == v.Prefix {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}, flags)
|
||||||
|
}
|
||||||
|
|
||||||
func parseMessageParams(msg *irc.Message, out ...*string) error {
|
func parseMessageParams(msg *irc.Message, out ...*string) error {
|
||||||
if len(msg.Params) < len(out) {
|
if len(msg.Params) < len(out) {
|
||||||
return newNeedMoreParamsError(msg.Command)
|
return newNeedMoreParamsError(msg.Command)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~sircmpwn/go-bare"
|
"git.sr.ht/~sircmpwn/go-bare"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
)
|
)
|
||||||
|
|
|
@ -14,9 +14,9 @@ import (
|
||||||
"git.sr.ht/~sircmpwn/go-bare"
|
"git.sr.ht/~sircmpwn/go-bare"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/msgstore/znclog"
|
"codeberg.org/emersion/soju/msgstore/znclog"
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"git.sr.ht/~sircmpwn/go-bare"
|
"git.sr.ht/~sircmpwn/go-bare"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
const messageRingBufferCap = 4096
|
const messageRingBufferCap = 4096
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
"git.sr.ht/~sircmpwn/go-bare"
|
"git.sr.ht/~sircmpwn/go-bare"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoadMessageOptions struct {
|
type LoadMessageOptions struct {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
|
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var timestampPrefixLen = len("[01:02:03] ")
|
var timestampPrefixLen = len("[01:02:03] ")
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MarshalLine(msg *irc.Message, t time.Time) string {
|
func MarshalLine(msg *irc.Message, t time.Time) string {
|
||||||
|
|
11
server.go
11
server.go
|
@ -20,11 +20,11 @@ import (
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
"nhooyr.io/websocket"
|
"nhooyr.io/websocket"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/auth"
|
"codeberg.org/emersion/soju/auth"
|
||||||
"git.sr.ht/~emersion/soju/config"
|
"codeberg.org/emersion/soju/config"
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/fileupload"
|
"codeberg.org/emersion/soju/fileupload"
|
||||||
"git.sr.ht/~emersion/soju/identd"
|
"codeberg.org/emersion/soju/identd"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -152,6 +152,7 @@ type Config struct {
|
||||||
EnableUsersOnAuth bool
|
EnableUsersOnAuth bool
|
||||||
Auth auth.Authenticator
|
Auth auth.Authenticator
|
||||||
FileUploader fileupload.Uploader
|
FileUploader fileupload.Uploader
|
||||||
|
FileCdn string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
|
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testServerPrefix = &irc.Prefix{Name: "soju-test-server"}
|
var testServerPrefix = &irc.Prefix{Name: "soju-test-server"}
|
||||||
|
|
|
@ -17,7 +17,7 @@ import (
|
||||||
|
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
const serviceNick = "BouncerServ"
|
const serviceNick = "BouncerServ"
|
||||||
|
|
13
upstream.go
13
upstream.go
|
@ -21,8 +21,8 @@ import (
|
||||||
"github.com/emersion/go-sasl"
|
"github.com/emersion/go-sasl"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
)
|
)
|
||||||
|
|
||||||
// permanentUpstreamCaps is the static list of upstream capabilities always
|
// permanentUpstreamCaps is the static list of upstream capabilities always
|
||||||
|
@ -191,7 +191,7 @@ type pendingUpstreamCommand struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type upstreamConn struct {
|
type upstreamConn struct {
|
||||||
conn
|
*conn
|
||||||
|
|
||||||
network *network
|
network *network
|
||||||
user *user
|
user *user
|
||||||
|
@ -353,7 +353,7 @@ func connectToUpstream(ctx context.Context, network *network) (*upstreamConn, er
|
||||||
|
|
||||||
cm := stdCaseMapping
|
cm := stdCaseMapping
|
||||||
uc := &upstreamConn{
|
uc := &upstreamConn{
|
||||||
conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options),
|
conn: newConn(network.user.srv, newNetIRCConn(netConn), &options),
|
||||||
network: network,
|
network: network,
|
||||||
user: network.user,
|
user: network.user,
|
||||||
channels: xirc.NewCaseMappingMap[*upstreamChannel](cm),
|
channels: xirc.NewCaseMappingMap[*upstreamChannel](cm),
|
||||||
|
@ -1541,7 +1541,7 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
|
||||||
Hostname: host,
|
Hostname: host,
|
||||||
Server: server,
|
Server: server,
|
||||||
Nickname: nick,
|
Nickname: nick,
|
||||||
Flags: flags,
|
Flags: stripMemberPrefixes(flags, uc),
|
||||||
Realname: realname,
|
Realname: realname,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1564,13 +1564,14 @@ func (uc *upstreamConn) handleMessage(ctx context.Context, msg *irc.Message) err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if uc.shouldCacheUserInfo(info.Nickname) {
|
if uc.shouldCacheUserInfo(info.Nickname) {
|
||||||
uc.cacheUserInfo(info.Nickname, &upstreamUser{
|
uc.cacheUserInfo(info.Nickname, &upstreamUser{
|
||||||
Nickname: info.Nickname,
|
Nickname: info.Nickname,
|
||||||
Username: info.Username,
|
Username: info.Username,
|
||||||
Hostname: info.Hostname,
|
Hostname: info.Hostname,
|
||||||
Server: info.Server,
|
Server: info.Server,
|
||||||
Flags: info.Flags,
|
Flags: stripMemberPrefixes(info.Flags, uc),
|
||||||
Account: info.Account,
|
Account: info.Account,
|
||||||
Realname: info.Realname,
|
Realname: info.Realname,
|
||||||
})
|
})
|
||||||
|
|
6
user.go
6
user.go
|
@ -14,13 +14,13 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/xirc"
|
"codeberg.org/emersion/soju/xirc"
|
||||||
|
|
||||||
"github.com/SherClockHolmes/webpush-go"
|
"github.com/SherClockHolmes/webpush-go"
|
||||||
"gopkg.in/irc.v4"
|
"gopkg.in/irc.v4"
|
||||||
|
|
||||||
"git.sr.ht/~emersion/soju/database"
|
"codeberg.org/emersion/soju/database"
|
||||||
"git.sr.ht/~emersion/soju/msgstore"
|
"codeberg.org/emersion/soju/msgstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserUpdateFunc func(record *database.User) error
|
type UserUpdateFunc func(record *database.User) error
|
||||||
|
|
|
@ -206,25 +206,25 @@ func GenerateNamesReply(channel string, status ChannelStatus, members []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateSASL(resp []byte) []*irc.Message {
|
func GenerateSASL(resp []byte) []*irc.Message {
|
||||||
|
encoded := base64.StdEncoding.EncodeToString(resp)
|
||||||
|
|
||||||
// <= instead of < because we need to send a final empty response if
|
// <= instead of < because we need to send a final empty response if
|
||||||
// the last chunk is exactly 400 bytes long
|
// the last chunk is exactly 400 bytes long
|
||||||
var msgs []*irc.Message
|
var msgs []*irc.Message
|
||||||
for i := 0; i <= len(resp); i += MaxSASLLength {
|
for i := 0; i <= len(encoded); i += MaxSASLLength {
|
||||||
j := i + MaxSASLLength
|
j := i + MaxSASLLength
|
||||||
if j > len(resp) {
|
if j > len(encoded) {
|
||||||
j = len(resp)
|
j = len(encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk := resp[i:j]
|
chunk := encoded[i:j]
|
||||||
|
if chunk == "" {
|
||||||
var respStr = "+"
|
chunk = "+"
|
||||||
if len(chunk) != 0 {
|
|
||||||
respStr = base64.StdEncoding.EncodeToString(chunk)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs = append(msgs, &irc.Message{
|
msgs = append(msgs, &irc.Message{
|
||||||
Command: "AUTHENTICATE",
|
Command: "AUTHENTICATE",
|
||||||
Params: []string{respStr},
|
Params: []string{chunk},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return msgs
|
return msgs
|
||||||
|
|
Loading…
Reference in a new issue