matrix-org.dendrite/clientapi/routing/userdirectory.go

120 lines
3.2 KiB
Go

// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package routing
import (
"context"
"database/sql"
"fmt"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/roomserver/api"
userapi "github.com/matrix-org/dendrite/userapi/api"
"github.com/matrix-org/gomatrixserverlib"
"github.com/matrix-org/util"
)
type UserDirectoryResponse struct {
Results []authtypes.FullyQualifiedProfile `json:"results"`
Limited bool `json:"limited"`
}
func SearchUserDirectory(
ctx context.Context,
device *userapi.Device,
userAPI userapi.ClientUserAPI,
rsAPI api.ClientRoomserverAPI,
provider userapi.UserDirectoryProvider,
serverName gomatrixserverlib.ServerName,
searchString string,
limit int,
) util.JSONResponse {
if limit < 10 {
limit = 10
}
results := map[string]authtypes.FullyQualifiedProfile{}
response := &UserDirectoryResponse{
Results: []authtypes.FullyQualifiedProfile{},
Limited: false,
}
// First start searching local users.
userReq := &userapi.QuerySearchProfilesRequest{
SearchString: searchString,
Limit: limit,
}
userRes := &userapi.QuerySearchProfilesResponse{}
if err := provider.QuerySearchProfiles(ctx, userReq, userRes); err != nil {
return util.ErrorResponse(fmt.Errorf("userAPI.QuerySearchProfiles: %w", err))
}
for _, user := range userRes.Profiles {
if len(results) == limit {
response.Limited = true
break
}
var userID string
if user.ServerName != "" {
userID = fmt.Sprintf("@%s:%s", user.Localpart, user.ServerName)
} else {
userID = fmt.Sprintf("@%s:%s", user.Localpart, serverName)
}
if _, ok := results[userID]; !ok {
results[userID] = authtypes.FullyQualifiedProfile{
UserID: userID,
DisplayName: user.DisplayName,
AvatarURL: user.AvatarURL,
}
}
}
// Then, if we have enough room left in the response,
// start searching for known users from joined rooms.
if len(results) <= limit {
stateReq := &api.QueryKnownUsersRequest{
UserID: device.UserID,
SearchString: searchString,
Limit: limit - len(results),
}
stateRes := &api.QueryKnownUsersResponse{}
if err := rsAPI.QueryKnownUsers(ctx, stateReq, stateRes); err != nil && err != sql.ErrNoRows {
return util.ErrorResponse(fmt.Errorf("rsAPI.QueryKnownUsers: %w", err))
}
for _, user := range stateRes.Users {
if len(results) == limit {
response.Limited = true
break
}
if _, ok := results[user.UserID]; !ok {
results[user.UserID] = user
}
}
}
for _, result := range results {
response.Results = append(response.Results, result)
}
return util.JSONResponse{
Code: 200,
JSON: response,
}
}