diff --git a/Dockerfile b/Dockerfile index cbe1502..236139a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -ARG SIGNAL_CLI_VERSION=0.13.7 -ARG LIBSIGNAL_CLIENT_VERSION=0.58.0 -ARG SIGNAL_CLI_NATIVE_PACKAGE_VERSION=0.13.7+morph027+1 +ARG SIGNAL_CLI_VERSION=0.13.9 +ARG LIBSIGNAL_CLIENT_VERSION=0.58.2 +ARG SIGNAL_CLI_NATIVE_PACKAGE_VERSION=0.13.9+morph027+1 ARG SWAG_VERSION=1.6.7 ARG GRAALVM_VERSION=21.0.0 diff --git a/ext/libraries/libsignal-client/v0.58.2/arm64/libsignal_jni.so b/ext/libraries/libsignal-client/v0.58.2/arm64/libsignal_jni.so new file mode 100644 index 0000000..579c16e Binary files /dev/null and b/ext/libraries/libsignal-client/v0.58.2/arm64/libsignal_jni.so differ diff --git a/ext/libraries/libsignal-client/v0.58.2/armv7/libsignal_jni.so b/ext/libraries/libsignal-client/v0.58.2/armv7/libsignal_jni.so new file mode 100644 index 0000000..bcb1703 Binary files /dev/null and b/ext/libraries/libsignal-client/v0.58.2/armv7/libsignal_jni.so differ diff --git a/ext/libraries/libsignal-client/v0.58.2/x86-64/libsignal_jni.so b/ext/libraries/libsignal-client/v0.58.2/x86-64/libsignal_jni.so new file mode 100644 index 0000000..7829dd8 Binary files /dev/null and b/ext/libraries/libsignal-client/v0.58.2/x86-64/libsignal_jni.so differ diff --git a/src/api/api.go b/src/api/api.go index c4d2042..d8c2d7f 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "net/http" + "net/url" "strconv" "strings" "sync" @@ -144,8 +145,9 @@ type CreateGroupResponse struct { } type UpdateProfileRequest struct { - Name string `json:"name"` - Base64Avatar string `json:"base64_avatar"` + Name string `json:"name"` + Base64Avatar string `json:"base64_avatar"` + About *string `json:"about"` } type TrustIdentityRequest struct { @@ -231,7 +233,11 @@ func (a *Api) About(c *gin.Context) { // @Param data body RegisterNumberRequest false "Additional Settings" // @Router /v1/register/{number} [post] func (a *Api) RegisterNumber(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } var req RegisterNumberRequest @@ -254,7 +260,7 @@ func (a *Api) RegisterNumber(c *gin.Context) { return } - err := a.signalClient.RegisterNumber(number, req.UseVoice, req.Captcha) + err = a.signalClient.RegisterNumber(number, req.UseVoice, req.Captcha) if err != nil { c.JSON(400, gin.H{"error": err.Error()}) return @@ -273,7 +279,11 @@ func (a *Api) RegisterNumber(c *gin.Context) { // @Param data body UnregisterNumberRequest false "Additional Settings" // @Router /v1/unregister/{number} [post] func (a *Api) UnregisterNumber(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } deleteAccount := false deleteLocalData := false @@ -291,7 +301,7 @@ func (a *Api) UnregisterNumber(c *gin.Context) { deleteLocalData = req.DeleteLocalData } - err := a.signalClient.UnregisterNumber(number, deleteAccount, deleteLocalData) + err = a.signalClient.UnregisterNumber(number, deleteAccount, deleteLocalData) if err != nil { c.JSON(400, gin.H{"error": err.Error()}) return @@ -311,7 +321,11 @@ func (a *Api) UnregisterNumber(c *gin.Context) { // @Param token path string true "Verification Code" // @Router /v1/register/{number}/verify/{token} [post] func (a *Api) VerifyRegisteredNumber(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } token := c.Param("token") pin := "" @@ -338,7 +352,7 @@ func (a *Api) VerifyRegisteredNumber(c *gin.Context) { return } - err := a.signalClient.VerifyRegisteredNumber(number, token, pin) + err = a.signalClient.VerifyRegisteredNumber(number, token, pin) if err != nil { c.JSON(400, gin.H{"error": err.Error()}) return @@ -565,7 +579,11 @@ func StringToBool(input string) bool { // @Param send_read_receipts query string false "Specify whether read receipts should be sent when receiving messages" (default: false)" // @Router /v1/receive/{number} [get] func (a *Api) Receive(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if a.signalClient.GetSignalCliMode() == client.JsonRpc { ws, err := connectionUpgrader.Upgrade(c.Writer, c.Request, nil) @@ -632,10 +650,14 @@ func (a *Api) Receive(c *gin.Context) { // @Param number path string true "Registered Phone Number" // @Router /v1/groups/{number} [post] func (a *Api) CreateGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } var req CreateGroupRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -690,7 +712,11 @@ func (a *Api) CreateGroup(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid}/members [post] func (a *Api) AddMembersToGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -703,7 +729,7 @@ func (a *Api) AddMembersToGroup(c *gin.Context) { } var req ChangeGroupMembersRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -735,7 +761,11 @@ func (a *Api) AddMembersToGroup(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid}/members [delete] func (a *Api) RemoveMembersFromGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -748,7 +778,7 @@ func (a *Api) RemoveMembersFromGroup(c *gin.Context) { } var req ChangeGroupMembersRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -780,7 +810,11 @@ func (a *Api) RemoveMembersFromGroup(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid}/admins [post] func (a *Api) AddAdminsToGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -793,7 +827,7 @@ func (a *Api) AddAdminsToGroup(c *gin.Context) { } var req ChangeGroupAdminsRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -825,7 +859,11 @@ func (a *Api) AddAdminsToGroup(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid}/admins [delete] func (a *Api) RemoveAdminsFromGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -838,7 +876,7 @@ func (a *Api) RemoveAdminsFromGroup(c *gin.Context) { } var req ChangeGroupAdminsRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -868,7 +906,11 @@ func (a *Api) RemoveAdminsFromGroup(c *gin.Context) { // @Param number path string true "Registered Phone Number" // @Router /v1/groups/{number} [get] func (a *Api) GetGroups(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } groups, err := a.signalClient.GetGroups(number) if err != nil { @@ -890,7 +932,11 @@ func (a *Api) GetGroups(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid} [get] func (a *Api) GetGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } groupId := c.Param("groupid") groupEntry, err := a.signalClient.GetGroup(number, groupId) @@ -918,7 +964,11 @@ func (a *Api) GetGroup(c *gin.Context) { // @Router /v1/groups/{number}/{groupid} [delete] func (a *Api) DeleteGroup(c *gin.Context) { base64EncodedGroupId := c.Param("groupid") - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if base64EncodedGroupId == "" { c.JSON(400, Error{Msg: "Please specify a group id"}) @@ -1095,7 +1145,11 @@ func (a *Api) ServeAttachment(c *gin.Context) { // @Param number path string true "Registered Phone Number" // @Router /v1/profiles/{number} [put] func (a *Api) UpdateProfile(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) @@ -1103,7 +1157,7 @@ func (a *Api) UpdateProfile(c *gin.Context) { } var req UpdateProfileRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) log.Error(err.Error()) @@ -1115,7 +1169,7 @@ func (a *Api) UpdateProfile(c *gin.Context) { return } - err = a.signalClient.UpdateProfile(number, req.Name, req.Base64Avatar) + err = a.signalClient.UpdateProfile(number, req.Name, req.Base64Avatar, req.About) if err != nil { c.JSON(400, Error{Msg: err.Error()}) return @@ -1142,7 +1196,11 @@ func (a *Api) Health(c *gin.Context) { // @Param number path string true "Registered Phone Number" // @Router /v1/identities/{number} [get] func (a *Api) ListIdentities(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) @@ -1168,7 +1226,11 @@ func (a *Api) ListIdentities(c *gin.Context) { // @Param numberToTrust path string true "Number To Trust" // @Router /v1/identities/{number}/trust/{numberToTrust} [put] func (a *Api) TrustIdentity(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) @@ -1182,7 +1244,7 @@ func (a *Api) TrustIdentity(c *gin.Context) { } var req TrustIdentityRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) log.Error(err.Error()) @@ -1279,7 +1341,11 @@ func (a *Api) GetConfiguration(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid}/block [post] func (a *Api) BlockGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -1312,7 +1378,11 @@ func (a *Api) BlockGroup(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid}/join [post] func (a *Api) JoinGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -1372,7 +1442,11 @@ func (a *Api) JoinGroupByInviteLink(c *gin.Context) { // @Param groupid path string true "Group ID" // @Router /v1/groups/{number}/{groupid}/quit [post] func (a *Api) QuitGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -1405,7 +1479,11 @@ func (a *Api) QuitGroup(c *gin.Context) { // @Param data body UpdateGroupRequest true "Input Data" // @Router /v1/groups/{number}/{groupid} [put] func (a *Api) UpdateGroup(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -1442,6 +1520,7 @@ func (a *Api) UpdateGroup(c *gin.Context) { // @Success 204 {string} OK // @Failure 400 {object} Error // @Param data body Reaction true "Reaction" +// @Param number path string true "Registered phone number" // @Router /v1/reactions/{number} [post] func (a *Api) SendReaction(c *gin.Context) { var req Reaction @@ -1452,7 +1531,11 @@ func (a *Api) SendReaction(c *gin.Context) { return } - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if req.Recipient == "" { c.JSON(400, Error{Msg: "Couldn't process request - recipient missing"}) @@ -1490,6 +1573,7 @@ func (a *Api) SendReaction(c *gin.Context) { // @Success 204 {string} OK // @Failure 400 {object} Error // @Param data body Reaction true "Reaction" +// @Param number path string true "Registered phone number" // @Router /v1/reactions/{number} [delete] func (a *Api) RemoveReaction(c *gin.Context) { var req Reaction @@ -1500,7 +1584,11 @@ func (a *Api) RemoveReaction(c *gin.Context) { return } - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if req.Recipient == "" { c.JSON(400, Error{Msg: "Couldn't process request - recipient missing"}) @@ -1533,6 +1621,7 @@ func (a *Api) RemoveReaction(c *gin.Context) { // @Success 204 {string} OK // @Failure 400 {object} Error // @Param data body Receipt true "Receipt" +// @Param number path string true "Registered phone number" // @Router /v1/receipts/{number} [post] func (a *Api) SendReceipt(c *gin.Context) { var req Receipt @@ -1543,7 +1632,11 @@ func (a *Api) SendReceipt(c *gin.Context) { return } - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) @@ -1593,7 +1686,11 @@ func (a *Api) SendStartTyping(c *gin.Context) { return } - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -1626,7 +1723,11 @@ func (a *Api) SendStopTyping(c *gin.Context) { return } - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -1657,7 +1758,11 @@ func (a *Api) SearchForNumbers(c *gin.Context) { return } - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } searchResults, err := a.signalClient.SearchForNumbers(number, query["numbers"]) if err != nil { @@ -1685,14 +1790,18 @@ func (a *Api) SearchForNumbers(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/contacts/{number} [put] func (a *Api) UpdateContact(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } var req UpdateContactRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -1722,14 +1831,18 @@ func (a *Api) UpdateContact(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/devices/{number} [post] func (a *Api) AddDevice(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } var req AddDeviceRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -1754,14 +1867,18 @@ func (a *Api) AddDevice(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/configuration/{number}/settings [post] func (a *Api) SetTrustMode(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } var req TrustModeRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -1793,13 +1910,16 @@ func (a *Api) SetTrustMode(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/configuration/{number}/settings [get] func (a *Api) GetTrustMode(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } - var err error trustMode := TrustModeResponse{} trustMode.TrustMode, err = utils.TrustModeToString(a.signalClient.GetTrustMode(number)) if err != nil { @@ -1821,13 +1941,17 @@ func (a *Api) GetTrustMode(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/contacts/{number}/sync [post] func (a *Api) SendContacts(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } - err := a.signalClient.SendContacts(number) + err = a.signalClient.SendContacts(number) if err != nil { c.JSON(400, Error{Msg: err.Error()}) return @@ -1846,14 +1970,18 @@ func (a *Api) SendContacts(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/accounts/{number}/rate-limit-challenge [post] func (a *Api) SubmitRateLimitChallenge(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } var req RateLimitChallengeRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -1878,14 +2006,18 @@ func (a *Api) SubmitRateLimitChallenge(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/accounts/{number}/settings [put] func (a *Api) UpdateAccountSettings(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } var req UpdateAccountSettingsRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -1912,14 +2044,18 @@ func (a *Api) UpdateAccountSettings(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/accounts/{number}/username [post] func (a *Api) SetUsername(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } var req SetUsernameRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -1943,13 +2079,17 @@ func (a *Api) SetUsername(c *gin.Context) { // @Failure 400 {object} Error // @Router /v1/accounts/{number}/username [delete] func (a *Api) RemoveUsername(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } - err := a.signalClient.RemoveUsername(number) + err = a.signalClient.RemoveUsername(number) if err != nil { c.JSON(400, Error{Msg: err.Error()}) return @@ -1968,7 +2108,11 @@ func (a *Api) RemoveUsername(c *gin.Context) { // @Success 200 {object} []client.ListInstalledStickerPacksResponse // @Router /v1/sticker-packs/{number} [get] func (a *Api) ListInstalledStickerPacks(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return @@ -1994,14 +2138,18 @@ func (a *Api) ListInstalledStickerPacks(c *gin.Context) { // @Param data body AddStickerPackRequest true "Request" // @Router /v1/sticker-packs/{number} [post] func (a *Api) AddStickerPack(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) return } var req AddStickerPackRequest - err := c.BindJSON(&req) + err = c.BindJSON(&req) if err != nil { c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) return @@ -2024,7 +2172,11 @@ func (a *Api) AddStickerPack(c *gin.Context) { // @Param number path string true "Registered Phone Number" // @Router /v1/contacts/{number} [get] func (a *Api) ListContacts(c *gin.Context) { - number := c.Param("number") + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } if number == "" { c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) diff --git a/src/client/client.go b/src/client/client.go index 48397d3..cf852f0 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -462,7 +462,10 @@ func (s *SignalClient) send(signalCliSendRequest ds.SignalCliSendRequest) (*Send request.Attachments = append(request.Attachments, attachmentEntry.toDataForSignal()) } - request.NotifySelf = true + // for backwards compatibility, if flag is not set we'll assume that self notification is desired + if signalCliSendRequest.NotifySelf == nil || *signalCliSendRequest.NotifySelf { + request.NotifySelf = true + } request.Sticker = signalCliSendRequest.Sticker if signalCliSendRequest.Mentions != nil { @@ -1357,7 +1360,7 @@ func (s *SignalClient) GetAttachment(attachment string) ([]byte, error) { return attachmentBytes, nil } -func (s *SignalClient) UpdateProfile(number string, profileName string, base64Avatar string) error { +func (s *SignalClient) UpdateProfile(number string, profileName string, base64Avatar string, about *string) error { var err error var avatarTmpPath string if base64Avatar != "" { @@ -1397,17 +1400,20 @@ func (s *SignalClient) UpdateProfile(number string, profileName string, base64Av if s.signalCliMode == JsonRpc { type Request struct { - Name string `json:"given-name"` - Avatar string `json:"avatar,omitempty"` - RemoveAvatar bool `json:"remove-avatar"` + Name string `json:"given-name"` + Avatar string `json:"avatar,omitempty"` + RemoveAvatar bool `json:"remove-avatar"` + About *string `json:"about,omitempty"` } request := Request{Name: profileName} + request.About = about if base64Avatar == "" { request.RemoveAvatar = true } else { request.Avatar = avatarTmpPath request.RemoveAvatar = false } + jsonRpc2Client, err := s.getJsonRpc2Client() if err != nil { return err @@ -1421,6 +1427,10 @@ func (s *SignalClient) UpdateProfile(number string, profileName string, base64Av cmd = append(cmd, []string{"--avatar", avatarTmpPath}...) } + if about != nil { + cmd = append(cmd, []string{"--about", *about}...) + } + _, err = s.cliClient.Execute(true, cmd, "") } diff --git a/src/docs/README.md b/src/docs/README.md new file mode 100644 index 0000000..eb559cb --- /dev/null +++ b/src/docs/README.md @@ -0,0 +1,13 @@ +These files are generated from the [swaggo/swag](https://github.com/swaggo/swag) tool. + +To regenerate them, run in /src: + +```bash +docker run --rm -v $(pwd):/code ghcr.io/swaggo/swag:latest init +``` + +Or, if you have `swag` installed: + +```bash +swag init +``` \ No newline at end of file diff --git a/src/docs/docs.go b/src/docs/docs.go index 15f1710..f743b6e 100644 --- a/src/docs/docs.go +++ b/src/docs/docs.go @@ -1415,6 +1415,13 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/api.Reaction" } + }, + { + "type": "string", + "description": "Registered phone number", + "name": "number", + "in": "path", + "required": true } ], "responses": { @@ -1453,6 +1460,13 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/api.Reaction" } + }, + { + "type": "string", + "description": "Registered phone number", + "name": "number", + "in": "path", + "required": true } ], "responses": { @@ -1493,6 +1507,13 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/api.Receipt" } + }, + { + "type": "string", + "description": "Registered phone number", + "name": "number", + "in": "path", + "required": true } ], "responses": { @@ -2278,7 +2299,69 @@ const docTemplate = `{ } }, "api.SendMessageV2": { - "type": "object" + "type": "object", + "properties": { + "base64_attachments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "\u003cBASE64 ENCODED DATA\u003e", + "data:\u003cMIME-TYPE\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e", + "data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e" + ] + }, + "edit_timestamp": { + "type": "integer" + }, + "mentions": { + "type": "array", + "items": { + "$ref": "#/definitions/data.MessageMention" + } + }, + "message": { + "type": "string" + }, + "notify_self": { + "type": "boolean" + }, + "number": { + "type": "string" + }, + "quote_author": { + "type": "string" + }, + "quote_mentions": { + "type": "array", + "items": { + "$ref": "#/definitions/data.MessageMention" + } + }, + "quote_message": { + "type": "string" + }, + "quote_timestamp": { + "type": "integer" + }, + "recipients": { + "type": "array", + "items": { + "type": "string" + } + }, + "sticker": { + "type": "string" + }, + "text_mode": { + "type": "string", + "enum": [ + "normal", + "styled" + ] + } + } }, "api.SetUsernameRequest": { "type": "object", @@ -2380,6 +2463,9 @@ const docTemplate = `{ "api.UpdateProfileRequest": { "type": "object", "properties": { + "about": { + "type": "string" + }, "base64_avatar": { "type": "string" }, @@ -2548,6 +2634,20 @@ const docTemplate = `{ "type": "string" } } + }, + "data.MessageMention": { + "type": "object", + "properties": { + "author": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "start": { + "type": "integer" + } + } } }, "tags": [ @@ -2605,9 +2705,9 @@ const docTemplate = `{ // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = &swag.Spec{ Version: "1.0", - Host: "", + Host: "localhost:8080", BasePath: "/", - Schemes: []string{}, + Schemes: []string{"http"}, Title: "Signal Cli REST API", Description: "This is the Signal Cli REST API documentation.", InfoInstanceName: "swagger", diff --git a/src/docs/swagger.json b/src/docs/swagger.json index 1c0ed55..c42cd1f 100644 --- a/src/docs/swagger.json +++ b/src/docs/swagger.json @@ -1,4 +1,7 @@ { + "schemes": [ + "http" + ], "swagger": "2.0", "info": { "description": "This is the Signal Cli REST API documentation.", @@ -6,6 +9,7 @@ "contact": {}, "version": "1.0" }, + "host": "localhost:8080", "basePath": "/", "paths": { "/v1/about": { @@ -1408,6 +1412,13 @@ "schema": { "$ref": "#/definitions/api.Reaction" } + }, + { + "type": "string", + "description": "Registered phone number", + "name": "number", + "in": "path", + "required": true } ], "responses": { @@ -1446,6 +1457,13 @@ "schema": { "$ref": "#/definitions/api.Reaction" } + }, + { + "type": "string", + "description": "Registered phone number", + "name": "number", + "in": "path", + "required": true } ], "responses": { @@ -1486,6 +1504,13 @@ "schema": { "$ref": "#/definitions/api.Receipt" } + }, + { + "type": "string", + "description": "Registered phone number", + "name": "number", + "in": "path", + "required": true } ], "responses": { @@ -2271,7 +2296,69 @@ } }, "api.SendMessageV2": { - "type": "object" + "type": "object", + "properties": { + "base64_attachments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "\u003cBASE64 ENCODED DATA\u003e", + "data:\u003cMIME-TYPE\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e", + "data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64\u003ccomma\u003e\u003cBASE64 ENCODED DATA\u003e" + ] + }, + "edit_timestamp": { + "type": "integer" + }, + "mentions": { + "type": "array", + "items": { + "$ref": "#/definitions/data.MessageMention" + } + }, + "message": { + "type": "string" + }, + "notify_self": { + "type": "boolean" + }, + "number": { + "type": "string" + }, + "quote_author": { + "type": "string" + }, + "quote_mentions": { + "type": "array", + "items": { + "$ref": "#/definitions/data.MessageMention" + } + }, + "quote_message": { + "type": "string" + }, + "quote_timestamp": { + "type": "integer" + }, + "recipients": { + "type": "array", + "items": { + "type": "string" + } + }, + "sticker": { + "type": "string" + }, + "text_mode": { + "type": "string", + "enum": [ + "normal", + "styled" + ] + } + } }, "api.SetUsernameRequest": { "type": "object", @@ -2373,6 +2460,9 @@ "api.UpdateProfileRequest": { "type": "object", "properties": { + "about": { + "type": "string" + }, "base64_avatar": { "type": "string" }, @@ -2541,6 +2631,20 @@ "type": "string" } } + }, + "data.MessageMention": { + "type": "object", + "properties": { + "author": { + "type": "string" + }, + "length": { + "type": "integer" + }, + "start": { + "type": "integer" + } + } } }, "tags": [ diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml index f331ff2..6375112 100644 --- a/src/docs/swagger.yaml +++ b/src/docs/swagger.yaml @@ -163,6 +163,48 @@ definitions: type: array type: object api.SendMessageV2: + properties: + base64_attachments: + example: + - + - data:;base64 + - data:;filename=;base64 + items: + type: string + type: array + edit_timestamp: + type: integer + mentions: + items: + $ref: '#/definitions/data.MessageMention' + type: array + message: + type: string + notify_self: + type: boolean + number: + type: string + quote_author: + type: string + quote_mentions: + items: + $ref: '#/definitions/data.MessageMention' + type: array + quote_message: + type: string + quote_timestamp: + type: integer + recipients: + items: + type: string + type: array + sticker: + type: string + text_mode: + enum: + - normal + - styled + type: string type: object api.SetUsernameRequest: properties: @@ -229,6 +271,8 @@ definitions: type: object api.UpdateProfileRequest: properties: + about: + type: string base64_avatar: type: string name: @@ -339,6 +383,16 @@ definitions: username_link: type: string type: object + data.MessageMention: + properties: + author: + type: string + length: + type: integer + start: + type: integer + type: object +host: localhost:8080 info: contact: {} description: This is the Signal Cli REST API documentation. @@ -1284,6 +1338,11 @@ paths: required: true schema: $ref: '#/definitions/api.Reaction' + - description: Registered phone number + in: path + name: number + required: true + type: string produces: - application/json responses: @@ -1309,6 +1368,11 @@ paths: required: true schema: $ref: '#/definitions/api.Reaction' + - description: Registered phone number + in: path + name: number + required: true + type: string produces: - application/json responses: @@ -1335,6 +1399,11 @@ paths: required: true schema: $ref: '#/definitions/api.Receipt' + - description: Registered phone number + in: path + name: number + required: true + type: string produces: - application/json responses: @@ -1705,6 +1774,8 @@ paths: summary: Send a signal message. tags: - Messages +schemes: +- http swagger: "2.0" tags: - description: Some general endpoints. diff --git a/src/main.go b/src/main.go index 1abd6be..1d6b0b1 100644 --- a/src/main.go +++ b/src/main.go @@ -59,6 +59,8 @@ import ( // @tag.name Sticker Packs // @tag.description List and Install Sticker Packs +// @host localhost:8080 +// @schemes http // @BasePath / func main() { signalCliConfig := flag.String("signal-cli-config", "/home/.local/share/signal-cli/", "Config directory where signal-cli config is stored")