forked from Mirrors/gomuks
110 lines
3.8 KiB
Go
110 lines
3.8 KiB
Go
// gomuks - A Matrix client written in Go.
|
|
// Copyright (C) 2025 Tulir Asokan
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package gomuks
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/rs/zerolog/hlog"
|
|
"go.mau.fi/util/dbutil"
|
|
"go.mau.fi/util/exhttp"
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/crypto"
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
func (gmx *Gomuks) ExportKeys(w http.ResponseWriter, r *http.Request) {
|
|
found, correct := gmx.doBasicAuth(r)
|
|
if !found || !correct {
|
|
hlog.FromRequest(r).Debug().Msg("Requesting credentials for key export request")
|
|
w.Header().Set("WWW-Authenticate", `Basic realm="gomuks web" charset="UTF-8"`)
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
err := r.ParseForm()
|
|
if err != nil {
|
|
hlog.FromRequest(r).Err(err).Msg("Failed to parse form")
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
_, _ = w.Write([]byte("Failed to parse form data\n"))
|
|
return
|
|
}
|
|
roomID := id.RoomID(r.PathValue("room_id"))
|
|
var sessions dbutil.RowIter[*crypto.InboundGroupSession]
|
|
filename := "gomuks-keys.txt"
|
|
if roomID == "" {
|
|
sessions = gmx.Client.CryptoStore.GetAllGroupSessions(r.Context())
|
|
} else {
|
|
filename = fmt.Sprintf("gomuks-keys-%s.txt", roomID)
|
|
sessions = gmx.Client.CryptoStore.GetGroupSessionsForRoom(r.Context(), roomID)
|
|
}
|
|
export, err := crypto.ExportKeysIter(r.FormValue("passphrase"), sessions)
|
|
if errors.Is(err, crypto.ErrNoSessionsForExport) {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
_, _ = w.Write([]byte("No keys found\n"))
|
|
return
|
|
} else if err != nil {
|
|
hlog.FromRequest(r).Err(err).Msg("Failed to export keys")
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
_, _ = w.Write([]byte("Failed to export keys (see logs for more details)\n"))
|
|
return
|
|
}
|
|
w.Header().Set("Content-Disposition", mime.FormatMediaType("attachment", map[string]string{"filename": filename}))
|
|
w.Header().Set("Content-Length", strconv.Itoa(len(export)))
|
|
w.WriteHeader(http.StatusOK)
|
|
_, _ = w.Write(export)
|
|
}
|
|
|
|
var badMultipartForm = mautrix.RespError{ErrCode: "FI.MAU.GOMUKS.BAD_FORM_DATA", Err: "Failed to parse form data", StatusCode: http.StatusBadRequest}
|
|
|
|
func (gmx *Gomuks) ImportKeys(w http.ResponseWriter, r *http.Request) {
|
|
err := r.ParseMultipartForm(5 * 1024 * 1024)
|
|
if err != nil {
|
|
badMultipartForm.Write(w)
|
|
return
|
|
}
|
|
export, _, err := r.FormFile("export")
|
|
if err != nil {
|
|
badMultipartForm.WithMessage("Failed to get export file from form: %w", err).Write(w)
|
|
return
|
|
}
|
|
exportData, err := io.ReadAll(export)
|
|
if err != nil {
|
|
badMultipartForm.WithMessage("Failed to read export file: %w", err).Write(w)
|
|
return
|
|
}
|
|
importedCount, totalCount, err := gmx.Client.Crypto.ImportKeys(r.Context(), r.FormValue("passphrase"), exportData)
|
|
if err != nil {
|
|
hlog.FromRequest(r).Err(err).Msg("Failed to import keys")
|
|
mautrix.MUnknown.WithMessage("Failed to import keys: %w", err).Write(w)
|
|
return
|
|
}
|
|
hlog.FromRequest(r).Info().
|
|
Int("imported_count", importedCount).
|
|
Int("total_count", totalCount).
|
|
Msg("Successfully imported keys")
|
|
exhttp.WriteJSONResponse(w, http.StatusOK, map[string]int{
|
|
"imported": importedCount,
|
|
"total": totalCount,
|
|
})
|
|
}
|