Skip to content

Commit

Permalink
v2: add backwards-compatible resolve identifier provisioning API
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Jun 25, 2024
1 parent a74bad4 commit 0b0379a
Show file tree
Hide file tree
Showing 7 changed files with 280 additions and 111 deletions.
133 changes: 133 additions & 0 deletions cmd/mautrix-signal-v2/legacyprovision.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// mautrix-signal - A Matrix-Signal puppeting bridge.
// Copyright (C) 2024 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 istributed 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 main

import (
"fmt"
"net/http"

"github.com/gorilla/mux"
"github.com/rs/zerolog"

"go.mau.fi/mautrix-signal/legacyprovision"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/bridgev2"
)

func legacyProvLinkNew(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}

func legacyProvLinkWaitScan(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}

func legacyProvLinkWaitAccount(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotImplemented)
}

func legacyProvLogout(w http.ResponseWriter, r *http.Request) {
user := m.Matrix.Provisioning.GetUser(r)
for {
login := user.GetDefaultLogin()
if login == nil {
break
}
login.Logout(r.Context())
}
legacyprovision.JSONResponse(w, http.StatusOK, nil)
}

func legacyResolveIdentifierOrStartChat(w http.ResponseWriter, r *http.Request, create bool) {
login := m.Matrix.Provisioning.GetLoginForRequest(w, r)
if login == nil {
return
}
api := login.Client.(bridgev2.IdentifierResolvingNetworkAPI)
resp, err := api.ResolveIdentifier(r.Context(), mux.Vars(r)["phonenum"], create)
if err != nil {
zerolog.Ctx(r.Context()).Err(err).Msg("Failed to resolve identifier")
legacyprovision.JSONResponse(w, http.StatusInternalServerError, &legacyprovision.Error{
Error: fmt.Sprintf("Failed to resolve identifier: %v", err),
ErrCode: "M_UNKNOWN",
})
return
} else if resp == nil {
legacyprovision.JSONResponse(w, http.StatusNotFound, &legacyprovision.Error{
ErrCode: mautrix.MNotFound.ErrCode,
Error: "User not found on Signal",
})
return
}
status := http.StatusOK
apiResp := &legacyprovision.ResolveIdentifierResponse{
ChatID: legacyprovision.ResolveIdentifierResponseChatID{
UUID: string(resp.UserID),
Number: "",
},
}
if resp.Ghost != nil {
if resp.UserInfo != nil {
resp.Ghost.UpdateInfo(r.Context(), resp.UserInfo)
}
apiResp.OtherUser = &legacyprovision.ResolveIdentifierResponseOtherUser{
MXID: resp.Ghost.Intent.GetMXID(),
DisplayName: resp.Ghost.Name,
AvatarURL: resp.Ghost.AvatarMXC.ParseOrIgnore(),
}
}
if resp.Chat != nil {
if resp.Chat.Portal == nil {
resp.Chat.Portal, err = m.Bridge.GetPortalByID(r.Context(), resp.Chat.PortalID)
if err != nil {
zerolog.Ctx(r.Context()).Err(err).Msg("Failed to get portal")
legacyprovision.JSONResponse(w, http.StatusInternalServerError, &mautrix.RespError{
Err: "Failed to get portal",
ErrCode: "M_UNKNOWN",
})
return
}
}
if create && resp.Chat.Portal.MXID == "" {
apiResp.JustCreated = true
status = http.StatusCreated
err = resp.Chat.Portal.CreateMatrixRoom(r.Context(), login, resp.Chat.PortalInfo)
if err != nil {
zerolog.Ctx(r.Context()).Err(err).Msg("Failed to create portal room")
legacyprovision.JSONResponse(w, http.StatusInternalServerError, &mautrix.RespError{
Err: "Failed to create portal room",
ErrCode: "M_UNKNOWN",
})
return
}
}
apiResp.RoomID = resp.Chat.Portal.MXID
}
legacyprovision.JSONResponse(w, status, &legacyprovision.Response{
Success: true,
Status: "ok",
ResolveIdentifierResponse: apiResp,
})
}

func legacyProvResolveIdentifier(w http.ResponseWriter, r *http.Request) {
legacyResolveIdentifierOrStartChat(w, r, false)
}

func legacyProvPM(w http.ResponseWriter, r *http.Request) {
legacyResolveIdentifierOrStartChat(w, r, true)
}
12 changes: 12 additions & 0 deletions cmd/mautrix-signal-v2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package main

import (
"net/http"

"maunium.net/go/mautrix/bridgev2/bridgeconfig"
"maunium.net/go/mautrix/bridgev2/matrix/mxmain"

Expand Down Expand Up @@ -53,6 +55,16 @@ func main() {
true,
)
}
m.PostStart = func() {
if m.Matrix.Provisioning != nil {
m.Matrix.Provisioning.Router.HandleFunc("/v2/link/new", legacyProvLinkNew).Methods(http.MethodPost)
m.Matrix.Provisioning.Router.HandleFunc("/v2/link/wait/scan", legacyProvLinkWaitScan).Methods(http.MethodPost)
m.Matrix.Provisioning.Router.HandleFunc("/v2/link/wait/account", legacyProvLinkWaitAccount).Methods(http.MethodPost)
m.Matrix.Provisioning.Router.HandleFunc("/v2/logout", legacyProvLogout).Methods(http.MethodPost)
m.Matrix.Provisioning.Router.HandleFunc("/v2/resolve_identifier/{phonenum}", legacyProvResolveIdentifier).Methods(http.MethodGet)
m.Matrix.Provisioning.Router.HandleFunc("/v2/pm/{phonenum}", legacyProvPM).Methods(http.MethodPost)
}
}
m.InitVersion(Tag, Commit, BuildTime)
m.Run()
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
golang.org/x/net v0.26.0
google.golang.org/protobuf v1.34.2
maunium.net/go/mautrix v0.19.0-beta.1.0.20240625104456-54ff874fac72
maunium.net/go/mautrix v0.19.0-beta.1.0.20240625120422-13b2d6275302
nhooyr.io/websocket v1.8.11
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240625104456-54ff874fac72 h1:66ojF6y1KlyOyPLj6/XVxoj+ex/LmxFoCiR6SzvEFBI=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240625104456-54ff874fac72/go.mod h1:pFbqAannSyJnohVycF4NW3IngBLWUt/f9KYfJcwyQec=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240625120422-13b2d6275302 h1:PbCdso3xltp5ztKXpAW4C4tC8LLgxeHkJ4mF/7HE3Z0=
maunium.net/go/mautrix v0.19.0-beta.1.0.20240625120422-13b2d6275302/go.mod h1:pFbqAannSyJnohVycF4NW3IngBLWUt/f9KYfJcwyQec=
nhooyr.io/websocket v1.8.11 h1:f/qXNc2/3DpoSZkHt1DQu6rj4zGC8JmkkLkWss0MgN0=
nhooyr.io/websocket v1.8.11/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
92 changes: 92 additions & 0 deletions legacyprovision/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// mautrix-signal - A Matrix-Signal puppeting bridge.
// Copyright (C) 2024 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 istributed 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 legacyprovision

import (
"encoding/json"
"net/http"

"maunium.net/go/mautrix/id"
)

func JSONResponse(w http.ResponseWriter, status int, response any) {
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(response)
}

type Error struct {
Success bool `json:"success"`
Error string `json:"error"`
ErrCode string `json:"errcode"`
}

type Response struct {
Success bool `json:"success"`
Status string `json:"status"`

// For response in LinkNew
SessionID string `json:"session_id,omitempty"`
URI string `json:"uri,omitempty"`

// For response in LinkWaitForAccount
UUID string `json:"uuid,omitempty"`
Number string `json:"number,omitempty"`

// For response in ResolveIdentifier
*ResolveIdentifierResponse
}

type WhoAmIResponse struct {
Permissions int `json:"permissions"`
MXID string `json:"mxid"`
Signal *WhoAmIResponseSignal `json:"signal,omitempty"`
}

type WhoAmIResponseSignal struct {
Number string `json:"number"`
UUID string `json:"uuid"`
Name string `json:"name"`
Ok bool `json:"ok"`
}

type ResolveIdentifierResponse struct {
RoomID id.RoomID `json:"room_id"`
ChatID ResolveIdentifierResponseChatID `json:"chat_id"`
JustCreated bool `json:"just_created"`
OtherUser *ResolveIdentifierResponseOtherUser `json:"other_user,omitempty"`
}

type ResolveIdentifierResponseChatID struct {
UUID string `json:"uuid"`
Number string `json:"number"`
}

type ResolveIdentifierResponseOtherUser struct {
MXID id.UserID `json:"mxid"`
DisplayName string `json:"displayname"`
AvatarURL id.ContentURI `json:"avatar_url"`
}

type LinkWaitForScanRequest struct {
SessionID string `json:"session_id"`
}

type LinkWaitForAccountRequest struct {
SessionID string `json:"session_id"`
DeviceName string `json:"device_name"` // TODO this seems to not be used anywhere
}
3 changes: 1 addition & 2 deletions pkg/connector/chatinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package connector
import (
"context"
"crypto/sha256"
"errors"
"fmt"
"strconv"
"strings"
Expand Down Expand Up @@ -148,7 +147,7 @@ func (s *SignalClient) ResolveIdentifier(ctx context.Context, number string, cre
aci = resp[e164Number].ACI
pni = resp[e164Number].PNI
if aci == uuid.Nil && pni == uuid.Nil {
return nil, errors.New("user not found on Signal")
return nil, nil
}
recipient, err = s.Client.Store.RecipientStore.UpdateRecipientE164(ctx, aci, pni, e164String)
if err != nil {
Expand Down
Loading

0 comments on commit 0b0379a

Please sign in to comment.