diff --git a/clientapi/routing/presence.go b/clientapi/routing/presence.go new file mode 100644 index 000000000..a9cf26856 --- /dev/null +++ b/clientapi/routing/presence.go @@ -0,0 +1,115 @@ +// Copyright 2021 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 ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" + "time" + + "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/dendrite/eduserver/api" + userapi "github.com/matrix-org/dendrite/userapi/api" + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" + "github.com/sirupsen/logrus" +) + +type presenceRequest struct { + Presence string `json:"presence"` + StatusMsg string `json:"status_msg"` +} + +// The new presence state. One of: ["online", "offline", "unavailable"] +var allowedPresence = map[string]bool{ + "online": true, + "offline": true, + "unavailable": true, +} +var allowedStrings []string + +// we only need to do this once +func init() { + allowedStrings = make([]string, len(allowedPresence)) + i := 0 + for k := range allowedPresence { + allowedStrings[i] = k + i++ + } +} + +func SetPresence(req *http.Request, eduAPI api.EDUServerInputAPI, device *userapi.Device) util.JSONResponse { + data, err := ioutil.ReadAll(req.Body) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()), + } + } + defer req.Body.Close() // nolint:errcheck + + // parse the request + var r presenceRequest + err = json.Unmarshal(data, &r) + if err != nil { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON("The request body could not be read: " + err.Error()), + } + } + + // requested new presence is not allowed by the spec + if !allowedPresence[r.Presence] { + return util.JSONResponse{ + Code: http.StatusBadRequest, + JSON: jsonerror.BadJSON( + fmt.Sprintf("The 'presence' field only allows one of: %s [sent: %s]", strings.Join(allowedStrings, ", "), r.Presence), + ), + } + } + + logrus.WithFields(logrus.Fields{ + "userId": device.UserID, + "presence": r.Presence, + "status_msg": r.StatusMsg, + }).Debug("Setting presence for user") + + if err := api.SetPresence( + req.Context(), + eduAPI, + device.ID, + r.Presence, + r.StatusMsg, + gomatrixserverlib.AsTimestamp(time.Now()), + ); err != nil { + return util.ErrorResponse(err) + } + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: struct{}{}, + } +} + +func GetPresence(req *http.Request, eduAPI api.EDUServerInputAPI, userID string) util.JSONResponse { + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: struct{}{}, + } +} diff --git a/clientapi/routing/routing.go b/clientapi/routing/routing.go index 01b9a4057..b94b01277 100644 --- a/clientapi/routing/routing.go +++ b/clientapi/routing/routing.go @@ -895,4 +895,22 @@ func Setup( return SetReceipt(req, eduAPI, device, vars["roomId"], vars["receiptType"], vars["eventId"]) }), ).Methods(http.MethodPost, http.MethodOptions) + r0mux.Handle("/presence/{userId}/status", + httputil.MakeAuthAPI("set_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + if r := rateLimits.rateLimit(req); r != nil { + return *r + } + return SetPresence(req, eduAPI, device) + }), + ).Methods(http.MethodPut, http.MethodOptions) + r0mux.Handle("/presence/{userId}/status", + httputil.MakeAuthAPI("get_presence", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse { + vars, err := httputil.URLDecodeMapValues(mux.Vars(req)) + if err != nil { + return util.ErrorResponse(err) + } + + return GetPresence(req, eduAPI, vars["userId"]) + }), + ).Methods(http.MethodGet, http.MethodOptions) }