Compare commits
No commits in common. "trunk" and "v1.4.0" have entirely different histories.
21
.drone.yml
21
.drone.yml
|
@ -5,20 +5,20 @@ name: linux-test
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: fetch
|
- name: fetch
|
||||||
image: golang:1.21.5
|
image: golang
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
commands:
|
commands:
|
||||||
|
#- go get -u golang.org/x/lint/golint
|
||||||
- go install honnef.co/go/tools/cmd/staticcheck@latest
|
- go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||||
- go install go.uber.org/nilaway/cmd/nilaway@latest
|
|
||||||
- git fetch --tags
|
- git fetch --tags
|
||||||
- go get
|
- go get
|
||||||
- echo `git describe --tags` > VERSION
|
- echo `git describe --tags` > VERSION
|
||||||
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
|
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
|
||||||
|
|
||||||
- name: quality
|
- name: quality
|
||||||
image: golang:1.21.5
|
image: golang
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
|
@ -27,7 +27,7 @@ steps:
|
||||||
- staticcheck ./...
|
- staticcheck ./...
|
||||||
|
|
||||||
- name: units-tests
|
- name: units-tests
|
||||||
image: golang:1.21.5
|
image: golang
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
|
@ -35,7 +35,7 @@ steps:
|
||||||
- sh testing/tests.sh
|
- sh testing/tests.sh
|
||||||
|
|
||||||
- name: test-builda-app
|
- name: test-builda-app
|
||||||
image: golang:1.21.5
|
image: golang
|
||||||
volumes:
|
volumes:
|
||||||
- name: deps
|
- name: deps
|
||||||
path: /go
|
path: /go
|
||||||
|
@ -43,8 +43,17 @@ steps:
|
||||||
- cd app
|
- cd app
|
||||||
- go build
|
- go build
|
||||||
|
|
||||||
|
- name: notify-email
|
||||||
|
image: drillster/drone-email
|
||||||
|
settings:
|
||||||
|
host: build.openprivacy.ca
|
||||||
|
port: 25
|
||||||
|
skip_verify: true
|
||||||
|
from: drone@openprivacy.ca
|
||||||
|
when:
|
||||||
|
status: [failure]
|
||||||
|
|
||||||
- name: notify-gogs
|
- name: notify-gogs
|
||||||
pull: if-not-exists
|
|
||||||
image: openpriv/drone-gogs
|
image: openpriv/drone-gogs
|
||||||
when:
|
when:
|
||||||
event: pull_request
|
event: pull_request
|
||||||
|
|
9
LICENSE
9
LICENSE
|
@ -1,9 +0,0 @@
|
||||||
MIT License
|
|
||||||
Copyright (c) 2021 Open Privacy Research Society
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ The app takes the following environment variables
|
||||||
|
|
||||||
## Using the Server
|
## Using the Server
|
||||||
|
|
||||||
When run the app will output standard log lines, one of which will contain the `serverbundle` in purple. This is the part you need to capture and import into a Cwtch client app so you can use the server for hosting groups
|
When run the app will output standard log lines, one of which will contain the `tofubundle` in purple. This is the part you need to capture and import into a Cwtch client app so you can use the server for hosting groups
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
|
@ -42,8 +42,8 @@ or run our prebuild ones with
|
||||||
|
|
||||||
and run it. It stores all Cwtch data in a Volume at `/var/lib/cwtch` so if you want the server data to persist you would run
|
and run it. It stores all Cwtch data in a Volume at `/var/lib/cwtch` so if you want the server data to persist you would run
|
||||||
|
|
||||||
`docker run -v /var/lib/cwtch/server01:/var/lib/cwtch openpriv/cwtch-server`
|
`docker run openpriv/cwtch-server -v /var/lib/cwtch/server01:/var/lib/cwtch`
|
||||||
|
|
||||||
to create a persistent container you might try a command like:
|
to create a persistent container you might try a command like:
|
||||||
|
|
||||||
`docker run --name cwtch -v /var/lib/cwtch/server01:/var/lib/cwtch --restart always openpriv/cwtch-server`
|
`docker run openpriv/cwtch-server --name cwtch -v /var/lib/cwtch/server01:/var/lib/cwtch --restart always`
|
||||||
|
|
35
app/main.go
35
app/main.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
_ "github.com/mattn/go-sqlite3" // sqlite3 driver
|
_ "github.com/mattn/go-sqlite3" // sqlite3 driver
|
||||||
|
"io/ioutil"
|
||||||
mrand "math/rand"
|
mrand "math/rand"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -36,6 +37,7 @@ func main() {
|
||||||
if configDir == "" {
|
if configDir == "" {
|
||||||
configDir = *flagDir
|
configDir = *flagDir
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(os.Args) == 2 && os.Args[1] == "gen1" {
|
if len(os.Args) == 2 && os.Args[1] == "gen1" {
|
||||||
config := new(cwtchserver.Config)
|
config := new(cwtchserver.Config)
|
||||||
id, pk := primitives.InitializeEphemeralIdentity()
|
id, pk := primitives.InitializeEphemeralIdentity()
|
||||||
|
@ -44,6 +46,7 @@ func main() {
|
||||||
config.PublicKey = id.PublicKey()
|
config.PublicKey = id.PublicKey()
|
||||||
config.TokenServerPrivateKey = tpk
|
config.TokenServerPrivateKey = tpk
|
||||||
config.TokenServerPublicKey = tid.PublicKey()
|
config.TokenServerPublicKey = tid.PublicKey()
|
||||||
|
config.MaxBufferLines = 100000
|
||||||
config.ServerReporting = cwtchserver.Reporting{
|
config.ServerReporting = cwtchserver.Reporting{
|
||||||
LogMetricsToFile: true,
|
LogMetricsToFile: true,
|
||||||
}
|
}
|
||||||
|
@ -65,8 +68,8 @@ func main() {
|
||||||
}
|
}
|
||||||
serverConfig.ServerReporting.LogMetricsToFile = !disableMetrics
|
serverConfig.ServerReporting.LogMetricsToFile = !disableMetrics
|
||||||
// we don't need real randomness for the port, just to avoid a possible conflict...
|
// we don't need real randomness for the port, just to avoid a possible conflict...
|
||||||
r := mrand.New(mrand.NewSource(int64(time.Now().Nanosecond())))
|
mrand.Seed(int64(time.Now().Nanosecond()))
|
||||||
controlPort := r.Intn(1000) + 9052
|
controlPort := mrand.Intn(1000) + 9052
|
||||||
|
|
||||||
// generate a random password
|
// generate a random password
|
||||||
key := make([]byte, 64)
|
key := make([]byte, 64)
|
||||||
|
@ -76,8 +79,9 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
os.MkdirAll("tordir/tor", 0700)
|
os.MkdirAll("tordir/tor", 0700)
|
||||||
tor.NewTorrc().WithHashedPassword(base64.StdEncoding.EncodeToString(key)).WithControlPort(controlPort).WithSocksPort(controlPort + 1).Build("./tordir/tor/torrc")
|
tor.NewTorrc().WithHashedPassword(base64.StdEncoding.EncodeToString(key)).WithControlPort(controlPort).Build("./tordir/tor/torrc")
|
||||||
acn, err := tor.NewTorACNWithAuth("tordir", "", "tordir/tor", controlPort, tor.HashedPasswordAuthenticator{Password: base64.StdEncoding.EncodeToString(key)})
|
acn, err := tor.NewTorACNWithAuth("tordir", "", controlPort, tor.HashedPasswordAuthenticator{Password: base64.StdEncoding.EncodeToString(key)})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("\nError connecting to Tor: %v\n", err)
|
log.Errorf("\nError connecting to Tor: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -92,7 +96,7 @@ func main() {
|
||||||
|
|
||||||
if *flagExportServer {
|
if *flagExportServer {
|
||||||
// Todo: change all to server export
|
// Todo: change all to server export
|
||||||
os.WriteFile(path.Join(serverConfig.ConfigDir, "serverbundle"), []byte(server.ServerBundle()), 0600)
|
ioutil.WriteFile(path.Join(serverConfig.ConfigDir, "serverbundle"), []byte(server.TofuBundle()), 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graceful Stop
|
// Graceful Stop
|
||||||
|
@ -105,27 +109,8 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
running := false
|
server.Run(acn)
|
||||||
lastStatus := -2
|
|
||||||
for {
|
for {
|
||||||
status, msg := acn.GetBootstrapStatus()
|
|
||||||
if status == 100 && !running {
|
|
||||||
log.Infoln("ACN is online, Running Server")
|
|
||||||
server.Run(acn)
|
|
||||||
running = true
|
|
||||||
}
|
|
||||||
if status != 100 {
|
|
||||||
if running {
|
|
||||||
log.Infoln("ACN is offline, Stopping Server")
|
|
||||||
server.Stop()
|
|
||||||
running = false
|
|
||||||
} else {
|
|
||||||
if lastStatus != status {
|
|
||||||
log.Infof("ACN booting... Status %v%%: %v\n", status, msg)
|
|
||||||
lastStatus = status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
22
go.mod
22
go.mod
|
@ -1,23 +1,13 @@
|
||||||
module git.openprivacy.ca/cwtch.im/server
|
module git.openprivacy.ca/cwtch.im/server
|
||||||
|
|
||||||
go 1.20
|
go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cwtch.im/cwtch v0.27.0
|
cwtch.im/cwtch v0.12.2
|
||||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0
|
git.openprivacy.ca/cwtch.im/tapir v0.4.9
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0
|
git.openprivacy.ca/openprivacy/connectivity v1.5.0
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3
|
git.openprivacy.ca/openprivacy/log v1.0.3
|
||||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c
|
github.com/gtank/ristretto255 v0.1.2
|
||||||
github.com/mattn/go-sqlite3 v1.14.7
|
github.com/mattn/go-sqlite3 v1.14.7
|
||||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
filippo.io/edwards25519 v1.0.0 // indirect
|
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.5 // indirect
|
|
||||||
github.com/gtank/merlin v0.1.1 // indirect
|
|
||||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
|
|
||||||
go.etcd.io/bbolt v1.3.6 // indirect
|
|
||||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b // indirect
|
|
||||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
70
go.sum
70
go.sum
|
@ -1,47 +1,71 @@
|
||||||
cwtch.im/cwtch v0.27.0 h1:MkNIZ+pT5ZwiGlKHk20GMUTlzBsmCWJEibbj6hr+WHw=
|
cwtch.im/cwtch v0.12.2 h1:I+ndKadCRCITw4SPbd+1cpRv+z/7iHjjTUv8OzRwTrE=
|
||||||
cwtch.im/cwtch v0.27.0/go.mod h1:A3i92aFuhyHI2DYO2Qnvl5iqEw0Cox22pdiypHnMOy4=
|
cwtch.im/cwtch v0.12.2/go.mod h1:QpTkQK7MqNt0dQK9/pBk5VpkvFhy6xuoxJIn401B8fM=
|
||||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
|
||||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0 h1:TtnKjxitkIDMM7Qn0n/u+mOHRLJzuQUYjYRu5n0/QFY=
|
git.openprivacy.ca/cwtch.im/tapir v0.4.9 h1:LXonlztwvI1F1++0IyomIcDH1/Bxzo+oN8YjGonNvjM=
|
||||||
git.openprivacy.ca/cwtch.im/tapir v0.6.0/go.mod h1:iQIq4y7N+DuP3CxyG66WNEC/d6vzh+wXvvOmelB+KoY=
|
git.openprivacy.ca/cwtch.im/tapir v0.4.9/go.mod h1:p4bHo3DAO8wwimU6JAeZXbfPQ4jnoA2bV+4YvknWTNQ=
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.5 h1:DJs5gqw3SkvLSgRDvroqJxZ7F+YsbxbBRg5t0rU5gYE=
|
git.openprivacy.ca/openprivacy/bine v0.0.4 h1:CO7EkGyz+jegZ4ap8g5NWRuDHA/56KKvGySR6OBPW+c=
|
||||||
git.openprivacy.ca/openprivacy/bine v0.0.5/go.mod h1:fwdeq6RO08WDkV0k7HfArsjRvurVULoUQmT//iaABZM=
|
git.openprivacy.ca/openprivacy/bine v0.0.4/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0 h1:roASjaFtQLu+HdH5fa2wx6F00NL3YsUTlmXBJh8aLZk=
|
git.openprivacy.ca/openprivacy/connectivity v1.5.0 h1:ZxsR/ZaVKXIkD2x6FlajZn62ciNQjamrI4i/5xIpdoQ=
|
||||||
git.openprivacy.ca/openprivacy/connectivity v1.11.0/go.mod h1:OQO1+7OIz/jLxDrorEMzvZA6SEbpbDyLGpjoFqT3z1Y=
|
git.openprivacy.ca/openprivacy/connectivity v1.5.0/go.mod h1:UjQiGBnWbotmBzIw59B8H6efwDadjkKzm3RPT1UaIRw=
|
||||||
|
git.openprivacy.ca/openprivacy/log v1.0.2 h1:HLP4wsw4ljczFAelYnbObIs821z+jgMPCe8uODPnGQM=
|
||||||
|
git.openprivacy.ca/openprivacy/log v1.0.2/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
git.openprivacy.ca/openprivacy/log v1.0.3 h1:E/PMm4LY+Q9s3aDpfySfEDq/vYQontlvNj/scrPaga0=
|
||||||
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
git.openprivacy.ca/openprivacy/log v1.0.3/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c h1:gkfmnY4Rlt3VINCo4uKdpvngiibQyoENVj5Q88sxXhE=
|
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
|
||||||
github.com/gtank/ristretto255 v0.1.3-0.20210930101514-6bb39798585c/go.mod h1:tDPFhGdt3hJWqtKwx57i9baiB1Cj0yAg22VOPUqm5vY=
|
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
|
||||||
|
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
|
github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA=
|
||||||
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
|
||||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
||||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b h1:QrHweqAtyJ9EwCaGHBu1fghwxIPiopAHV06JlXrMHjk=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b/go.mod h1:xxLb2ip6sSUts3g1irPVHyk/DGslwQsNOo9I7smJfNU=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
|
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
|
||||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
|
||||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo=
|
|
||||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
|
||||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44 h1:Bli41pIlzTzf3KEY06n+xnzK/BESIg2ze4Pgfh/aI8c=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
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/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||||
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=
|
||||||
|
|
|
@ -9,14 +9,9 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fields must be in this order because go compiler has problems with 64bit fields on 32 bit arches (arm32 raspberry pi):
|
|
||||||
//
|
|
||||||
// https://git.openprivacy.ca/cwtch.im/server/pulls/30
|
|
||||||
// https://github.com/golang/go/issues/599
|
|
||||||
// https://github.com/census-instrumentation/opencensus-go/issues/587
|
|
||||||
type counter struct {
|
type counter struct {
|
||||||
count uint64
|
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
|
count uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counter providers a threadsafe counter to use for storing long running counts
|
// Counter providers a threadsafe counter to use for storing long running counts
|
||||||
|
|
26
server.go
26
server.go
|
@ -113,7 +113,7 @@ func (s *server) Run(acn connectivity.ACN) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
s.messageStore, err = storage.InitializeSqliteMessageStore(path.Join(s.config.ConfigDir, "cwtch.messages"), s.config.GetMaxMessages(), s.incMessageCount)
|
s.messageStore, err = storage.InitializeSqliteMessageStore(path.Join(s.config.ConfigDir, "cwtch.messages"), s.incMessageCount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not open database: %v", err)
|
return fmt.Errorf("could not open database: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ func (s *server) CheckStatus() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop turns off the server so it cannot receive connections and frees most resourses.
|
// Stop turns off the server so it cannot receive connections and frees most resourses.
|
||||||
// The server is still in a reRunable state and tokenServer still has an active persistence
|
// The server is still in a reRunable state and tokenServer still has an active persistance
|
||||||
func (s *server) Stop() {
|
func (s *server) Stop() {
|
||||||
log.Infof("Shutting down server")
|
log.Infof("Shutting down server")
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
|
@ -176,7 +176,7 @@ func (s *server) Stop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy frees the last of the resources the server has active (tokenServer persistence) leaving it un-re-runable and completely shutdown
|
// Destroy frees the last of the resources the server has active (toklenServer persistance) leaving it un-re-runable and completely shutdown
|
||||||
func (s *server) Destroy() {
|
func (s *server) Destroy() {
|
||||||
s.Stop()
|
s.Stop()
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
|
@ -193,13 +193,10 @@ type Statistics struct {
|
||||||
// GetStatistics is a stub method for providing some high level information about
|
// GetStatistics is a stub method for providing some high level information about
|
||||||
// the server operation to bundling applications (e.g. the UI)
|
// the server operation to bundling applications (e.g. the UI)
|
||||||
func (s *server) GetStatistics() Statistics {
|
func (s *server) GetStatistics() Statistics {
|
||||||
if s.running {
|
return Statistics{
|
||||||
return Statistics{
|
TotalMessages: s.messageStore.MessagesCount(),
|
||||||
TotalMessages: s.messageStore.MessagesCount(),
|
TotalConnections: s.service.Metrics().ConnectionCount,
|
||||||
TotalConnections: s.service.Metrics().ConnectionCount,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Statistics{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) Delete(password string) error {
|
func (s *server) Delete(password string) error {
|
||||||
|
@ -246,17 +243,6 @@ func (s *server) SetAttribute(key, val string) {
|
||||||
s.config.SetAttribute(key, val)
|
s.config.SetAttribute(key, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMessageCap gets a server's MaxStorageMBs value
|
|
||||||
func (s *server) GetMaxStorageMBs() int {
|
|
||||||
return s.config.GetMaxMessageMBs()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMaxStorageMBs sets a server's MaxStorageMBs and sets MaxMessages for storage (which can trigger a prune)
|
|
||||||
func (s *server) SetMaxStorageMBs(val int) {
|
|
||||||
s.config.SetMaxMessageMBs(val)
|
|
||||||
s.messageStore.SetMessageCap(s.config.GetMaxMessages())
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMonitorLogging turns on or off the monitor logging suite, and logging to a file in the server dir
|
// SetMonitorLogging turns on or off the monitor logging suite, and logging to a file in the server dir
|
||||||
func (s *server) SetMonitorLogging(do bool) {
|
func (s *server) SetMonitorLogging(do bool) {
|
||||||
s.config.ServerReporting.LogMetricsToFile = do
|
s.config.ServerReporting.LogMetricsToFile = do
|
||||||
|
|
|
@ -2,19 +2,23 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
v1 "cwtch.im/cwtch/storage/v1"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"git.openprivacy.ca/cwtch.im/server/storage"
|
|
||||||
"git.openprivacy.ca/cwtch.im/tapir/primitives"
|
"git.openprivacy.ca/cwtch.im/tapir/primitives"
|
||||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"github.com/gtank/ristretto255"
|
"github.com/gtank/ristretto255"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// SaltFile is the standard filename to store an encrypted config's SALT under beside it
|
||||||
|
SaltFile = "SALT"
|
||||||
|
|
||||||
// AttrAutostart is the attribute key for autostart setting
|
// AttrAutostart is the attribute key for autostart setting
|
||||||
AttrAutostart = "autostart"
|
AttrAutostart = "autostart"
|
||||||
|
|
||||||
|
@ -41,15 +45,13 @@ type Reporting struct {
|
||||||
LogMetricsToFile bool `json:"logMetricsToFile"`
|
LogMetricsToFile bool `json:"logMetricsToFile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// messages are ~4kb of storage
|
|
||||||
const MessagesPerMB = 250
|
|
||||||
|
|
||||||
// Config is a struct for storing basic server configuration
|
// Config is a struct for storing basic server configuration
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ConfigDir string `json:"-"`
|
ConfigDir string `json:"-"`
|
||||||
FilePath string `json:"-"`
|
FilePath string `json:"-"`
|
||||||
Encrypted bool `json:"-"`
|
Encrypted bool `json:"-"`
|
||||||
key [32]byte
|
key [32]byte
|
||||||
|
MaxBufferLines int `json:"maxBufferLines"`
|
||||||
|
|
||||||
PublicKey ed25519.PublicKey `json:"publicKey"`
|
PublicKey ed25519.PublicKey `json:"publicKey"`
|
||||||
PrivateKey ed25519.PrivateKey `json:"privateKey"`
|
PrivateKey ed25519.PrivateKey `json:"privateKey"`
|
||||||
|
@ -63,12 +65,8 @@ type Config struct {
|
||||||
|
|
||||||
Attributes map[string]string `json:"attributes"`
|
Attributes map[string]string `json:"attributes"`
|
||||||
|
|
||||||
// messages are ~4kb of storage
|
|
||||||
// -1 == infinite
|
|
||||||
MaxStorageMBs int `json:"maxStorageMBs"`
|
|
||||||
|
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
encFileStore storage.FileStore
|
encFileStore v1.FileStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identity returns an encapsulation of the servers keys
|
// Identity returns an encapsulation of the servers keys
|
||||||
|
@ -90,11 +88,11 @@ func initDefaultConfig(configDir, filename string, encrypted bool) *Config {
|
||||||
config.PublicKey = id.PublicKey()
|
config.PublicKey = id.PublicKey()
|
||||||
config.TokenServerPrivateKey = tpk
|
config.TokenServerPrivateKey = tpk
|
||||||
config.TokenServerPublicKey = tid.PublicKey()
|
config.TokenServerPublicKey = tid.PublicKey()
|
||||||
|
config.MaxBufferLines = 100000
|
||||||
config.ServerReporting = Reporting{
|
config.ServerReporting = Reporting{
|
||||||
LogMetricsToFile: false,
|
LogMetricsToFile: false,
|
||||||
}
|
}
|
||||||
config.Attributes[AttrAutostart] = "false"
|
config.Attributes[AttrAutostart] = "false"
|
||||||
config.MaxStorageMBs = -1
|
|
||||||
|
|
||||||
k := new(ristretto255.Scalar)
|
k := new(ristretto255.Scalar)
|
||||||
b := make([]byte, 64)
|
b := make([]byte, 64)
|
||||||
|
@ -103,7 +101,7 @@ func initDefaultConfig(configDir, filename string, encrypted bool) *Config {
|
||||||
// unable to generate secure random numbers
|
// unable to generate secure random numbers
|
||||||
panic("unable to generate secure random numbers")
|
panic("unable to generate secure random numbers")
|
||||||
}
|
}
|
||||||
k.SetUniformBytes(b)
|
k.FromUniformBytes(b)
|
||||||
config.TokenServiceK = *k
|
config.TokenServiceK = *k
|
||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
@ -125,13 +123,13 @@ func CreateConfig(configDir, filename string, encrypted bool, password string, d
|
||||||
config := initDefaultConfig(configDir, filename, encrypted)
|
config := initDefaultConfig(configDir, filename, encrypted)
|
||||||
config.ServerReporting.LogMetricsToFile = defaultLogToFile
|
config.ServerReporting.LogMetricsToFile = defaultLogToFile
|
||||||
if encrypted {
|
if encrypted {
|
||||||
key, _, err := storage.InitV1Directory(configDir, password)
|
key, _, err := v1.InitV1Directory(configDir, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("could not create server directory: %s", err)
|
log.Errorf("could not create server directory: %s", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
config.key = key
|
config.key = key
|
||||||
config.encFileStore = storage.NewFileStore(configDir, ServerConfigFile, key)
|
config.encFileStore = v1.NewFileStore(configDir, ServerConfigFile, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Save()
|
config.Save()
|
||||||
|
@ -144,12 +142,12 @@ func LoadConfig(configDir, filename string, encrypted bool, password string) (*C
|
||||||
var raw []byte
|
var raw []byte
|
||||||
var err error
|
var err error
|
||||||
if encrypted {
|
if encrypted {
|
||||||
salt, err := os.ReadFile(path.Join(configDir, storage.SaltFile))
|
salt, err := ioutil.ReadFile(path.Join(configDir, SaltFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
config.key = storage.CreateKey(password, salt)
|
config.key = v1.CreateKey(password, salt)
|
||||||
config.encFileStore = storage.NewFileStore(configDir, ServerConfigFile, config.key)
|
config.encFileStore = v1.NewFileStore(configDir, ServerConfigFile, config.key)
|
||||||
raw, err = config.encFileStore.Read()
|
raw, err = config.encFileStore.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Not an error to log as load config is called blindly across all dirs with a password to see what it applies to
|
// Not an error to log as load config is called blindly across all dirs with a password to see what it applies to
|
||||||
|
@ -157,7 +155,7 @@ func LoadConfig(configDir, filename string, encrypted bool, password string) (*C
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
raw, err = os.ReadFile(path.Join(configDir, filename))
|
raw, err = ioutil.ReadFile(path.Join(configDir, filename))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -181,18 +179,18 @@ func (config *Config) Save() error {
|
||||||
if config.Encrypted {
|
if config.Encrypted {
|
||||||
return config.encFileStore.Write(bytes)
|
return config.encFileStore.Write(bytes)
|
||||||
}
|
}
|
||||||
return os.WriteFile(path.Join(config.ConfigDir, config.FilePath), bytes, 0600)
|
return ioutil.WriteFile(path.Join(config.ConfigDir, config.FilePath), bytes, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckPassword returns true if the given password produces the same key as the current stored key, otherwise false.
|
// CheckPassword returns true if the given password produces the same key as the current stored key, otherwise false.
|
||||||
func (config *Config) CheckPassword(checkpass string) bool {
|
func (config *Config) CheckPassword(checkpass string) bool {
|
||||||
config.lock.Lock()
|
config.lock.Lock()
|
||||||
defer config.lock.Unlock()
|
defer config.lock.Unlock()
|
||||||
salt, err := os.ReadFile(path.Join(config.ConfigDir, storage.SaltFile))
|
salt, err := ioutil.ReadFile(path.Join(config.ConfigDir, SaltFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
oldkey := storage.CreateKey(checkpass, salt[:])
|
oldkey := v1.CreateKey(checkpass, salt[:])
|
||||||
return oldkey == config.key
|
return oldkey == config.key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,26 +215,3 @@ func (config *Config) GetAttribute(key string) string {
|
||||||
defer config.lock.Unlock()
|
defer config.lock.Unlock()
|
||||||
return config.Attributes[key]
|
return config.Attributes[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMaxMessages returns the config setting for Max messages converting from MaxMB to messages
|
|
||||||
// or -1 for infinite
|
|
||||||
func (config *Config) GetMaxMessages() int {
|
|
||||||
config.lock.Lock()
|
|
||||||
defer config.lock.Unlock()
|
|
||||||
if config.MaxStorageMBs == -1 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return config.MaxStorageMBs * MessagesPerMB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *Config) GetMaxMessageMBs() int {
|
|
||||||
config.lock.Lock()
|
|
||||||
defer config.lock.Unlock()
|
|
||||||
return config.MaxStorageMBs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (config *Config) SetMaxMessageMBs(newval int) {
|
|
||||||
config.lock.Lock()
|
|
||||||
defer config.lock.Unlock()
|
|
||||||
config.MaxStorageMBs = newval
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cwtch.im/cwtch/model"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/cwtch.im/server/storage"
|
|
||||||
"git.openprivacy.ca/openprivacy/connectivity"
|
"git.openprivacy.ca/openprivacy/connectivity"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"os"
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -47,7 +47,7 @@ func NewServers(acn connectivity.ACN, directory string) Servers {
|
||||||
func (s *servers) LoadServers(password string) ([]string, error) {
|
func (s *servers) LoadServers(password string) ([]string, error) {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
dirs, err := os.ReadDir(s.directory)
|
dirs, err := ioutil.ReadDir(s.directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error: cannot read server directory: %v", err)
|
return nil, fmt.Errorf("error: cannot read server directory: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ func (s *servers) LoadServers(password string) ([]string, error) {
|
||||||
|
|
||||||
// CreateServer creates a new server and stores it, also returns an interface to it
|
// CreateServer creates a new server and stores it, also returns an interface to it
|
||||||
func (s *servers) CreateServer(password string) (Server, error) {
|
func (s *servers) CreateServer(password string) (Server, error) {
|
||||||
newLocalID := storage.GenerateRandomID()
|
newLocalID := model.GenerateRandomID()
|
||||||
directory := path.Join(s.directory, newLocalID)
|
directory := path.Join(s.directory, newLocalID)
|
||||||
config, err := CreateConfig(directory, ServerConfigFile, true, password, false)
|
config, err := CreateConfig(directory, ServerConfigFile, true, password, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,156 +0,0 @@
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"errors"
|
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
|
||||||
"golang.org/x/crypto/nacl/secretbox"
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
|
||||||
"golang.org/x/crypto/sha3"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SaltFile is the standard filename to store an encrypted config's SALT under beside it
|
|
||||||
const SaltFile = "SALT"
|
|
||||||
|
|
||||||
const version = "1"
|
|
||||||
const versionFile = "VERSION"
|
|
||||||
|
|
||||||
// GenerateRandomID generates a random 16 byte hex id code
|
|
||||||
func GenerateRandomID() string {
|
|
||||||
randBytes := make([]byte, 16)
|
|
||||||
rand.Read(randBytes)
|
|
||||||
return hex.EncodeToString(randBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitV1Directory generates a key and salt from a password, writes a SALT and VERSION file and returns the key and salt
|
|
||||||
func InitV1Directory(directory, password string) ([32]byte, [128]byte, error) {
|
|
||||||
os.Mkdir(directory, 0700)
|
|
||||||
|
|
||||||
key, salt, err := CreateKeySalt(password)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Could not create key for profile store from password: %v\n", err)
|
|
||||||
return [32]byte{}, [128]byte{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = os.WriteFile(path.Join(directory, versionFile), []byte(version), 0600); err != nil {
|
|
||||||
log.Errorf("Could not write version file: %v", err)
|
|
||||||
return [32]byte{}, [128]byte{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = os.WriteFile(path.Join(directory, SaltFile), salt[:], 0600); err != nil {
|
|
||||||
log.Errorf("Could not write salt file: %v", err)
|
|
||||||
return [32]byte{}, [128]byte{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return key, salt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fileStore stores a cwtchPeer in an encrypted file
|
|
||||||
type fileStore struct {
|
|
||||||
directory string
|
|
||||||
filename string
|
|
||||||
key [32]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileStore is a primitive around storing encrypted files
|
|
||||||
type FileStore interface {
|
|
||||||
Write([]byte) error
|
|
||||||
Read() ([]byte, error)
|
|
||||||
Delete()
|
|
||||||
ChangeKey(newkey [32]byte)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFileStore instantiates a fileStore given a filename and a password
|
|
||||||
func NewFileStore(directory string, filename string, key [32]byte) FileStore {
|
|
||||||
filestore := new(fileStore)
|
|
||||||
filestore.key = key
|
|
||||||
filestore.filename = filename
|
|
||||||
filestore.directory = directory
|
|
||||||
return filestore
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKeySalt derives a key and salt from a password: returns key, salt, err
|
|
||||||
func CreateKeySalt(password string) ([32]byte, [128]byte, error) {
|
|
||||||
var salt [128]byte
|
|
||||||
if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil {
|
|
||||||
log.Errorf("Cannot read from random: %v\n", err)
|
|
||||||
return [32]byte{}, salt, err
|
|
||||||
}
|
|
||||||
dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512)
|
|
||||||
|
|
||||||
var dkr [32]byte
|
|
||||||
copy(dkr[:], dk)
|
|
||||||
return dkr, salt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateKey derives a key from a password and salt
|
|
||||||
func CreateKey(password string, salt []byte) [32]byte {
|
|
||||||
dk := pbkdf2.Key([]byte(password), salt, 4096, 32, sha3.New512)
|
|
||||||
|
|
||||||
var dkr [32]byte
|
|
||||||
copy(dkr[:], dk)
|
|
||||||
return dkr
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptFileData encrypts the data with the supplied key
|
|
||||||
func EncryptFileData(data []byte, key [32]byte) ([]byte, error) {
|
|
||||||
var nonce [24]byte
|
|
||||||
|
|
||||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
|
||||||
log.Errorf("Cannot read from random: %v\n", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
encrypted := secretbox.Seal(nonce[:], data, &nonce, &key)
|
|
||||||
return encrypted, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptFile decrypts the passed ciphertext with the supplied key.
|
|
||||||
func DecryptFile(ciphertext []byte, key [32]byte) ([]byte, error) {
|
|
||||||
var decryptNonce [24]byte
|
|
||||||
copy(decryptNonce[:], ciphertext[:24])
|
|
||||||
decrypted, ok := secretbox.Open(nil, ciphertext[24:], &decryptNonce, &key)
|
|
||||||
if ok {
|
|
||||||
return decrypted, nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("failed to decrypt")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadEncryptedFile reads data from an encrypted file in directory with key
|
|
||||||
func ReadEncryptedFile(directory, filename string, key [32]byte) ([]byte, error) {
|
|
||||||
encryptedbytes, err := os.ReadFile(path.Join(directory, filename))
|
|
||||||
if err == nil {
|
|
||||||
return DecryptFile(encryptedbytes, key)
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// write serializes a cwtchPeer to a file
|
|
||||||
func (fps *fileStore) Write(data []byte) error {
|
|
||||||
encryptedbytes, err := EncryptFileData(data, fps.key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.WriteFile(path.Join(fps.directory, fps.filename), encryptedbytes, 0600)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fps *fileStore) Read() ([]byte, error) {
|
|
||||||
return ReadEncryptedFile(fps.directory, fps.filename, fps.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fps *fileStore) Delete() {
|
|
||||||
err := os.Remove(path.Join(fps.directory, fps.filename))
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Deleting file %v\n", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fps *fileStore) ChangeKey(newkey [32]byte) {
|
|
||||||
fps.key = newkey
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/log"
|
"git.openprivacy.ca/openprivacy/log"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MessageStoreInterface defines an interface to interact with a store of cwtch messages.
|
// MessageStoreInterface defines an interface to interact with a store of cwtch messages.
|
||||||
|
@ -15,26 +14,17 @@ type MessageStoreInterface interface {
|
||||||
FetchMessages() []*groups.EncryptedGroupMessage
|
FetchMessages() []*groups.EncryptedGroupMessage
|
||||||
MessagesCount() int
|
MessagesCount() int
|
||||||
FetchMessagesFrom(signature []byte) []*groups.EncryptedGroupMessage
|
FetchMessagesFrom(signature []byte) []*groups.EncryptedGroupMessage
|
||||||
SetMessageCap(newcap int)
|
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SqliteMessageStore is an sqlite3 backed message store
|
// SqliteMessageStore is an sqlite3 backed message store
|
||||||
type SqliteMessageStore struct {
|
type SqliteMessageStore struct {
|
||||||
incMessageCounterFn func()
|
incMessageCounterFn func()
|
||||||
messageCap int
|
database *sql.DB
|
||||||
|
|
||||||
messageCount int
|
|
||||||
countLock sync.Mutex
|
|
||||||
|
|
||||||
database *sql.DB
|
|
||||||
|
|
||||||
// Some prepared queries...
|
// Some prepared queries...
|
||||||
preparedInsertStatement *sql.Stmt // A Stmt is safe for concurrent use by multiple goroutines.
|
preparedInsertStatement *sql.Stmt // A Stmt is safe for concurrent use by multiple goroutines.
|
||||||
preparedFetchFromQuery *sql.Stmt
|
preparedFetchFromQuery *sql.Stmt
|
||||||
preparedFetchQuery *sql.Stmt
|
|
||||||
preparedCountQuery *sql.Stmt
|
|
||||||
preparedPruneStatement *sql.Stmt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the underlying sqlite3 database to further changes
|
// Close closes the underlying sqlite3 database to further changes
|
||||||
|
@ -44,13 +34,6 @@ func (s *SqliteMessageStore) Close() {
|
||||||
s.database.Close()
|
s.database.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqliteMessageStore) SetMessageCap(newcap int) {
|
|
||||||
s.countLock.Lock()
|
|
||||||
defer s.countLock.Unlock()
|
|
||||||
s.messageCap = newcap
|
|
||||||
s.checkPruneMessages()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMessage implements the MessageStoreInterface AddMessage for sqlite message store
|
// AddMessage implements the MessageStoreInterface AddMessage for sqlite message store
|
||||||
func (s *SqliteMessageStore) AddMessage(message groups.EncryptedGroupMessage) {
|
func (s *SqliteMessageStore) AddMessage(message groups.EncryptedGroupMessage) {
|
||||||
if s.incMessageCounterFn != nil {
|
if s.incMessageCounterFn != nil {
|
||||||
|
@ -66,29 +49,10 @@ func (s *SqliteMessageStore) AddMessage(message groups.EncryptedGroupMessage) {
|
||||||
log.Errorf("%v %q", stmt, err)
|
log.Errorf("%v %q", stmt, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.countLock.Lock()
|
|
||||||
defer s.countLock.Unlock()
|
|
||||||
s.messageCount++
|
|
||||||
s.checkPruneMessages()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqliteMessageStore) checkPruneMessages() {
|
func (s SqliteMessageStore) MessagesCount() int {
|
||||||
if s.messageCap != -1 && s.messageCount > s.messageCap {
|
rows, err := s.database.Query("SELECT COUNT(*) from messages")
|
||||||
log.Debugf("Message Count: %d / Message Cap: %d, message cap exceeded, pruning oldest 10%...", s.messageCount, s.messageCap)
|
|
||||||
// Delete 10% of messages (and any overage if the cap was adjusted lower)
|
|
||||||
delCount := (s.messageCount - s.messageCap) + s.messageCap/10
|
|
||||||
stmt, err := s.preparedPruneStatement.Exec(delCount)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%v %q", stmt, err)
|
|
||||||
}
|
|
||||||
s.messageCount -= delCount
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SqliteMessageStore) MessagesCount() int {
|
|
||||||
rows, err := s.preparedCountQuery.Query()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%v", err)
|
log.Errorf("%v", err)
|
||||||
return -1
|
return -1
|
||||||
|
@ -111,8 +75,8 @@ func (s *SqliteMessageStore) MessagesCount() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchMessages implements the MessageStoreInterface FetchMessages for sqlite message store
|
// FetchMessages implements the MessageStoreInterface FetchMessages for sqlite message store
|
||||||
func (s *SqliteMessageStore) FetchMessages() []*groups.EncryptedGroupMessage {
|
func (s SqliteMessageStore) FetchMessages() []*groups.EncryptedGroupMessage {
|
||||||
rows, err := s.preparedFetchQuery.Query()
|
rows, err := s.database.Query("SELECT id, signature,ciphertext from messages")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%v", err)
|
log.Errorf("%v", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -122,7 +86,7 @@ func (s *SqliteMessageStore) FetchMessages() []*groups.EncryptedGroupMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FetchMessagesFrom implements the MessageStoreInterface FetchMessagesFrom for sqlite message store
|
// FetchMessagesFrom implements the MessageStoreInterface FetchMessagesFrom for sqlite message store
|
||||||
func (s *SqliteMessageStore) FetchMessagesFrom(signature []byte) []*groups.EncryptedGroupMessage {
|
func (s SqliteMessageStore) FetchMessagesFrom(signature []byte) []*groups.EncryptedGroupMessage {
|
||||||
|
|
||||||
// If signature is empty then treat this as a complete sync request
|
// If signature is empty then treat this as a complete sync request
|
||||||
if len(signature) == 0 {
|
if len(signature) == 0 {
|
||||||
|
@ -168,7 +132,7 @@ func (s *SqliteMessageStore) compileRows(rows *sql.Rows) []*groups.EncryptedGrou
|
||||||
|
|
||||||
// InitializeSqliteMessageStore creates a database `dbfile` with the necessary tables (if it doesn't already exist)
|
// InitializeSqliteMessageStore creates a database `dbfile` with the necessary tables (if it doesn't already exist)
|
||||||
// and returns an open database
|
// and returns an open database
|
||||||
func InitializeSqliteMessageStore(dbfile string, messageCap int, incMessageCounterFn func()) (*SqliteMessageStore, error) {
|
func InitializeSqliteMessageStore(dbfile string, incMessageCounterFn func()) (*SqliteMessageStore, error) {
|
||||||
db, err := sql.Open("sqlite3", dbfile)
|
db, err := sql.Open("sqlite3", dbfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("database %v cannot be created or opened %v", dbfile, err)
|
log.Errorf("database %v cannot be created or opened %v", dbfile, err)
|
||||||
|
@ -185,7 +149,6 @@ func InitializeSqliteMessageStore(dbfile string, messageCap int, incMessageCount
|
||||||
slms := new(SqliteMessageStore)
|
slms := new(SqliteMessageStore)
|
||||||
slms.database = db
|
slms.database = db
|
||||||
slms.incMessageCounterFn = incMessageCounterFn
|
slms.incMessageCounterFn = incMessageCounterFn
|
||||||
slms.messageCap = messageCap
|
|
||||||
|
|
||||||
sqlStmt = `INSERT INTO messages(signature, ciphertext) values (?,?);`
|
sqlStmt = `INSERT INTO messages(signature, ciphertext) values (?,?);`
|
||||||
stmt, err := slms.database.Prepare(sqlStmt)
|
stmt, err := slms.database.Prepare(sqlStmt)
|
||||||
|
@ -195,41 +158,12 @@ func InitializeSqliteMessageStore(dbfile string, messageCap int, incMessageCount
|
||||||
}
|
}
|
||||||
slms.preparedInsertStatement = stmt
|
slms.preparedInsertStatement = stmt
|
||||||
|
|
||||||
sqlStmt = "SELECT id, signature,ciphertext from messages"
|
query, err := slms.database.Prepare("SELECT id, signature,ciphertext FROM messages WHERE id>=(SELECT id FROM messages WHERE signature=(?));")
|
||||||
query, err := slms.database.Prepare(sqlStmt)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("%q: %s", err, sqlStmt)
|
log.Errorf("%v", err)
|
||||||
return nil, fmt.Errorf("%s: %q", sqlStmt, err)
|
|
||||||
}
|
|
||||||
slms.preparedFetchQuery = query
|
|
||||||
|
|
||||||
sqlStmt = "SELECT id, signature,ciphertext FROM messages WHERE id>=(SELECT id FROM messages WHERE signature=(?));"
|
|
||||||
query, err = slms.database.Prepare(sqlStmt)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%q: %s", err, sqlStmt)
|
|
||||||
return nil, fmt.Errorf("%s: %q", sqlStmt, err)
|
return nil, fmt.Errorf("%s: %q", sqlStmt, err)
|
||||||
}
|
}
|
||||||
slms.preparedFetchFromQuery = query
|
slms.preparedFetchFromQuery = query
|
||||||
|
|
||||||
sqlStmt = "SELECT COUNT(*) from messages"
|
|
||||||
stmt, err = slms.database.Prepare(sqlStmt)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%q: %s", err, sqlStmt)
|
|
||||||
return nil, fmt.Errorf("%s: %q", sqlStmt, err)
|
|
||||||
}
|
|
||||||
slms.preparedCountQuery = stmt
|
|
||||||
|
|
||||||
sqlStmt = "DELETE FROM messages WHERE id IN (SELECT id FROM messages ORDER BY id ASC LIMIT (?))"
|
|
||||||
stmt, err = slms.database.Prepare(sqlStmt)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("%q: %s", err, sqlStmt)
|
|
||||||
return nil, fmt.Errorf("%s: %q", sqlStmt, err)
|
|
||||||
}
|
|
||||||
slms.preparedPruneStatement = stmt
|
|
||||||
|
|
||||||
slms.messageCount = slms.MessagesCount()
|
|
||||||
|
|
||||||
slms.checkPruneMessages()
|
|
||||||
|
|
||||||
return slms, nil
|
return slms, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestMessageStore(t *testing.T) {
|
||||||
os.Remove(filename)
|
os.Remove(filename)
|
||||||
log.SetLevel(log.LevelDebug)
|
log.SetLevel(log.LevelDebug)
|
||||||
counter := metrics.NewCounter()
|
counter := metrics.NewCounter()
|
||||||
db, err := InitializeSqliteMessageStore(filename, -1, func() { counter.Add(1) })
|
db, err := InitializeSqliteMessageStore(filename, func() { counter.Add(1) })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error: %v", err)
|
t.Fatalf("Error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,16 @@ go list ./... | xargs go vet
|
||||||
echo ""
|
echo ""
|
||||||
echo "Linting:"
|
echo "Linting:"
|
||||||
|
|
||||||
go list ./... | staticcheck ./...
|
staticcheck ./...
|
||||||
|
|
||||||
echo "Running nilaway..."
|
|
||||||
nilaway -include-pkgs="cwtch.im/server,cwtch.im/cwtch,cwtch.im/tapir,git.openprivacy.ca/openprivacy/connectivity" -exclude-file-docstrings="nolint:nilaway" ./...
|
|
||||||
|
|
||||||
|
|
||||||
echo "Time to format"
|
echo "Time to format"
|
||||||
gofmt -l -s -w .
|
gofmt -l -s -w .
|
||||||
|
|
||||||
|
# ineffassign (https://github.com/gordonklaus/ineffassign)
|
||||||
|
echo "Checking for ineffectual assignment of errors (unchecked errors...)"
|
||||||
|
ineffassign .
|
||||||
|
|
||||||
# misspell (https://github.com/client9/misspell/cmd/misspell)
|
# misspell (https://github.com/client9/misspell/cmd/misspell)
|
||||||
echo "Checking for misspelled words..."
|
echo "Checking for misspelled words..."
|
||||||
misspell . | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"
|
misspell . | grep -v "vendor/" | grep -v "go.sum" | grep -v ".idea"
|
||||||
|
|
Loading…
Reference in New Issue