From 43747af9d4ce5461f29410e568fa47901ea9f90b Mon Sep 17 00:00:00 2001 From: Bernhard B Date: Sun, 2 Jan 2022 14:52:45 +0100 Subject: [PATCH] added search endpoint * check whether one or more phone numbers are registered with the signal service. see #50 --- src/api/api.go | 36 +++++++++++++++++++++++ src/client/client.go | 67 +++++++++++++++++++++++++++++++++++++++++++ src/docs/docs.go | 60 ++++++++++++++++++++++++++++++++++++++ src/docs/swagger.json | 60 ++++++++++++++++++++++++++++++++++++++ src/docs/swagger.yaml | 39 +++++++++++++++++++++++++ src/main.go | 8 ++++++ 6 files changed, 270 insertions(+) diff --git a/src/api/api.go b/src/api/api.go index 4bb27d9..dfb3bf8 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -110,6 +110,11 @@ var connectionUpgrader = websocket.Upgrader{ }, } +type SearchResponse struct { + Number string `json:"number"` + Registered bool `json:"registered"` +} + type Api struct { signalClient *client.SignalClient } @@ -1050,3 +1055,34 @@ func (a *Api) SendStopTyping(c *gin.Context) { } c.Status(http.StatusNoContent) } + +// @Summary Check if one or more phone numbers are registered with the Signal Service. +// @Tags Search +// @Description Check if one or more phone numbers are registered with the Signal Service. +// @Accept json +// @Produce json +// @Param numbers query []string true "Numbers to check" collectionFormat(multi) +// @Success 204 {object} SearchResponse +// @Failure 400 {object} Error +// @Router /v1/search [get] +func (a *Api) SearchForNumbers(c *gin.Context) { + query := c.Request.URL.Query() + if _, ok := query["numbers"]; !ok { + c.JSON(400, Error{Msg: "Please provide numbers to query for"}) + return + } + + searchResults, err := a.signalClient.SearchForNumbers(query["numbers"]) + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + + searchResponse := []SearchResponse{} + for _, val := range searchResults { + entry := SearchResponse{Number: val.Number, Registered: val.Registered} + searchResponse = append(searchResponse, entry) + } + + c.JSON(200, searchResponse) +} diff --git a/src/client/client.go b/src/client/client.go index 76bcdfe..9ea1996 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -122,6 +122,11 @@ type About struct { Version string `json:"version"` } +type SearchResultEntry struct { + Number string `json:"number"` + Registered bool `json:"registered"` +} + func cleanupTmpFiles(paths []string) { for _, path := range paths { os.Remove(path) @@ -514,6 +519,14 @@ func (s *SignalClient) getJsonRpc2Client(number string) (*JsonRpc2Client, error) return nil, errors.New("Number not registered with JSON-RPC") } +func (s *SignalClient) getJsonRpc2Clients() ([]*JsonRpc2Client) { + jsonRpc2Clients := []*JsonRpc2Client{} + for _, client := range s.jsonRpc2Clients { + jsonRpc2Clients = append(jsonRpc2Clients, client) + } + return jsonRpc2Clients +} + func (s *SignalClient) SendV2(number string, message string, recps []string, base64Attachments []string) (*[]SendResponse, error) { if len(recps) == 0 { return nil, errors.New("Please provide at least one recipient") @@ -1155,3 +1168,57 @@ func (s *SignalClient) SendStopTyping(number string, recipient string) error { return err } + +func (s *SignalClient) SearchForNumbers(numbers []string) ([]SearchResultEntry, error) { + searchResultEntries := []SearchResultEntry{} + + var err error + var rawData string + if s.signalCliMode == JsonRpc { + type Request struct { + Numbers []string `json:"recipient"` + } + request := Request{Numbers: numbers} + + jsonRpc2Clients := s.getJsonRpc2Clients() + if len(jsonRpc2Clients) == 0 { + return searchResultEntries, errors.New("No JsonRpc2Client registered!") + } + for _, jsonRpc2Client := range jsonRpc2Clients { + rawData, err = jsonRpc2Client.getRaw("getUserStatus", request) + if err == nil { //getUserStatus doesn't need an account to work, so try all the registered acounts and stop until we succeed + break + } + } + + if err != nil { + return searchResultEntries, err + } + } else { + cmd := []string{"--config", s.signalCliConfig, "--output", "json", "getUserStatus"} + cmd = append(cmd, numbers...) + rawData, err = runSignalCli(true, cmd, "", s.signalCliMode) + } + + if err != nil { + return searchResultEntries, err + } + + type SignalCliResponse struct { + Number string `json:"number"` + IsRegistered bool `json:"isRegistered"` + } + + var resp []SignalCliResponse + err = json.Unmarshal([]byte(rawData), &resp) + if err != nil { + return searchResultEntries, err + } + + for _, val := range resp { + searchResultEntry := SearchResultEntry{Number: val.Number, Registered: val.IsRegistered} + searchResultEntries = append(searchResultEntries, searchResultEntry) + } + + return searchResultEntries, err +} diff --git a/src/docs/docs.go b/src/docs/docs.go index fd787a9..8a4551d 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -913,6 +913,48 @@ var doc = `{ } } }, + "/v1/search": { + "get": { + "description": "Check if one or more phone numbers are registered with the Signal Service.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Search" + ], + "summary": "Check if one or more phone numbers are registered with the Signal Service.", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Numbers to check", + "name": "numbers", + "in": "query", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/api.SearchResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/v1/send": { "post": { "description": "Send a signal message", @@ -1197,6 +1239,17 @@ var doc = `{ } } }, + "api.SearchResponse": { + "type": "object", + "properties": { + "number": { + "type": "string" + }, + "registered": { + "type": "boolean" + } + } + }, "api.SendMessageResponse": { "type": "object", "properties": { @@ -1295,6 +1348,9 @@ var doc = `{ "mode": { "type": "string" }, + "version": { + "type": "string" + }, "versions": { "type": "array", "items": { @@ -1394,6 +1450,10 @@ var doc = `{ { "description": "React to messages.", "name": "Reactions" + }, + { + "description": "Search the Signal Service.", + "name": "Search" } ] }` diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 7e0ce5c..8885a3d 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -898,6 +898,48 @@ } } }, + "/v1/search": { + "get": { + "description": "Check if one or more phone numbers are registered with the Signal Service.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Search" + ], + "summary": "Check if one or more phone numbers are registered with the Signal Service.", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "multi", + "description": "Numbers to check", + "name": "numbers", + "in": "query", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/api.SearchResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.Error" + } + } + } + } + }, "/v1/send": { "post": { "description": "Send a signal message", @@ -1182,6 +1224,17 @@ } } }, + "api.SearchResponse": { + "type": "object", + "properties": { + "number": { + "type": "string" + }, + "registered": { + "type": "boolean" + } + } + }, "api.SendMessageResponse": { "type": "object", "properties": { @@ -1280,6 +1333,9 @@ "mode": { "type": "string" }, + "version": { + "type": "string" + }, "versions": { "type": "array", "items": { @@ -1379,6 +1435,10 @@ { "description": "React to messages.", "name": "Reactions" + }, + { + "description": "Search the Signal Service.", + "name": "Search" } ] } \ No newline at end of file diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index 5125e8f..e76c5f9 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -72,6 +72,13 @@ definitions: use_voice: type: boolean type: object + api.SearchResponse: + properties: + number: + type: string + registered: + type: boolean + type: object api.SendMessageResponse: properties: timestamp: @@ -135,6 +142,8 @@ definitions: type: integer mode: type: string + version: + type: string versions: items: type: string @@ -773,6 +782,34 @@ paths: summary: Verify a registered phone number. tags: - Devices + /v1/search: + get: + consumes: + - application/json + description: Check if one or more phone numbers are registered with the Signal Service. + parameters: + - collectionFormat: multi + description: Numbers to check + in: query + items: + type: string + name: numbers + required: true + type: array + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: '#/definitions/api.SearchResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.Error' + summary: Check if one or more phone numbers are registered with the Signal Service. + tags: + - Search /v1/send: post: consumes: @@ -905,3 +942,5 @@ tags: name: Identities - description: React to messages. name: Reactions +- description: Search the Signal Service. + name: Search diff --git a/src/main.go b/src/main.go index 7dd9bbf..31b1303 100644 --- a/src/main.go +++ b/src/main.go @@ -49,6 +49,9 @@ import ( // @tag.name Reactions // @tag.description React to messages. +// @tag.name Search +// @tag.description Search the Signal Service. + // @host 127.0.0.1:8080 // @BasePath / func main() { @@ -204,6 +207,11 @@ func main() { reactions.POST(":number", api.SendReaction) reactions.DELETE(":number", api.RemoveReaction) } + + search := v1.Group("/search") + { + search.GET("", api.SearchForNumbers) + } } v2 := router.Group("/v2")