Bugfix for support attachment base64 and custom filename.

Introduce tests for attachment data parsing. Use tests at build stage.
This commit is contained in:
zeetabit
2022-07-31 13:45:29 +02:00
parent c52fa0e53c
commit 270278ca30
8 changed files with 169 additions and 43 deletions

View File

@@ -118,7 +118,7 @@ COPY src/go.mod /tmp/signal-cli-rest-api-src/
COPY src/go.sum /tmp/signal-cli-rest-api-src/ COPY src/go.sum /tmp/signal-cli-rest-api-src/
# build signal-cli-rest-api # build signal-cli-rest-api
RUN cd /tmp/signal-cli-rest-api-src && swag init && go build RUN cd /tmp/signal-cli-rest-api-src && swag init && go test ./client -v && go build
# build supervisorctl_config_creator # build supervisorctl_config_creator
RUN cd /tmp/signal-cli-rest-api-src/scripts && go build -o jsonrpc2-helper RUN cd /tmp/signal-cli-rest-api-src/scripts && go build -o jsonrpc2-helper

View File

@@ -69,7 +69,7 @@ type RegisterNumberRequest struct {
} }
type UnregisterNumberRequest struct { type UnregisterNumberRequest struct {
DeleteAccount bool `json:"delete_account" example:"false"` DeleteAccount bool `json:"delete_account" example:"false"`
DeleteLocalData bool `json:"delete_local_data" example:"false"` DeleteLocalData bool `json:"delete_local_data" example:"false"`
} }
@@ -88,7 +88,7 @@ type SendMessageV1 struct {
Number string `json:"number"` Number string `json:"number"`
Recipients []string `json:"recipients"` Recipients []string `json:"recipients"`
Message string `json:"message"` Message string `json:"message"`
Base64Attachment string `json:"base64_attachment"` Base64Attachment string `json:"base64_attachment" example:"'<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'"`
IsGroup bool `json:"is_group"` IsGroup bool `json:"is_group"`
} }
@@ -96,7 +96,7 @@ type SendMessageV2 struct {
Number string `json:"number"` Number string `json:"number"`
Recipients []string `json:"recipients"` Recipients []string `json:"recipients"`
Message string `json:"message"` Message string `json:"message"`
Base64Attachments []string `json:"base64_attachments"` Base64Attachments []string `json:"base64_attachments" example:"<BASE64 ENCODED DATA>,data:<MIME-TYPE>;base64<comma><BASE64 ENCODED DATA>,data:<MIME-TYPE>;filename=<FILENAME>;base64<comma><BASE64 ENCODED DATA>"`
} }
type TypingIndicatorRequest struct { type TypingIndicatorRequest struct {

View File

@@ -28,7 +28,7 @@ func NewAttachmentEntry(attachmentData string) *AttachmentEntry {
func (attachmentEntry *AttachmentEntry) extractMetaData(attachmentData string) { func (attachmentEntry *AttachmentEntry) extractMetaData(attachmentData string) {
base64FlagIndex := strings.LastIndex(attachmentData, "base64,") base64FlagIndex := strings.LastIndex(attachmentData, "base64,")
if !strings.Contains(attachmentData, "data:") && base64FlagIndex == -1 { if !strings.Contains(attachmentData, "data:") || base64FlagIndex == -1 {
attachmentEntry.Base64 = attachmentData attachmentEntry.Base64 = attachmentData
return return
} }
@@ -77,12 +77,15 @@ func (attachmentEntry *AttachmentEntry) isWithMetaData() bool {
} }
func (attachmentEntry *AttachmentEntry) toDataForSignal() string { func (attachmentEntry *AttachmentEntry) toDataForSignal() string {
result := "" if len(attachmentEntry.FilePath) > 0 {
if !attachmentEntry.isWithMetaData() && len(attachmentEntry.FilePath) > 0 {
return attachmentEntry.FilePath return attachmentEntry.FilePath
} }
result = "data:" + attachmentEntry.MimeInfo if !attachmentEntry.isWithMetaData() {
return attachmentEntry.Base64
}
result := "data:" + attachmentEntry.MimeInfo
if len(attachmentEntry.FileName) > 0 { if len(attachmentEntry.FileName) > 0 {
result = result + ";filename=" + attachmentEntry.FileName result = result + ";filename=" + attachmentEntry.FileName

View File

@@ -0,0 +1,72 @@
package client
import (
"strings"
"testing"
)
func Test_Attachment_ExtractMetadata_ShouldPrepareDataFor_toDataForSignal(t *testing.T) {
testCases := []struct {
nameTest string
inputData string
resultIsWithMetaData bool
base64Expected string
fileNameExpected string
mimeInfoExpected string
toDataForSignal string
}{
{
"BC base64 - compatibility", "MTIzNDU=", false, "MTIzNDU=", "", "", "MTIzNDU=",
},
{
"+base64 -data -filename", "base64,MTIzNDU=", false, "base64,MTIzNDU=", "", "", "base64,MTIzNDU=",
},
{
"+base64 +data -filename", "data:someData;base64,MTIzNDU=", true, "MTIzNDU=", "", "someData", "data:someData;base64,MTIzNDU=",
},
{
"+base64 -data +filename", "filename=file.name;base64,MTIzNDU=", false, "filename=file.name;base64,MTIzNDU=", "", "", "filename=file.name;base64,MTIzNDU=",
},
{
"+base64 +data +filename", "data:someData;filename=file.name;base64,MTIzNDU=", true, "MTIzNDU=", "file.name", "someData", "data:someData;filename=file.name;base64,MTIzNDU=",
},
{
"-base64 -data -filename", "INVALIDMTIzNDU=", false, "INVALIDMTIzNDU=", "", "", "INVALIDMTIzNDU=",
},
{
"-base64 +data -filename", "data:someData;INVALIDMTIzNDU=", false, "INVALIDMTIzNDU=", "", "", "data:someData;INVALIDMTIzNDU=",
},
{
"-base64 -data +filename", "filename=file.name;INVALIDMTIzNDU=", false, "filename=file.name;INVALIDMTIzNDU=", "", "", "filename=file.name;INVALIDMTIzNDU=",
},
{
"-base64 +data +filename", "data:someData;filename=file.name;INVALIDMTIzNDU=", false, "data:someData;filename=file.name;INVALIDMTIzNDU=", "", "", "data:someData;filename=file.name;INVALIDMTIzNDU=",
},
}
for _, tt := range testCases {
t.Run(tt.nameTest, func(t *testing.T) {
attachmentEntry := NewAttachmentEntry(tt.inputData)
if attachmentEntry.isWithMetaData() != tt.resultIsWithMetaData {
t.Errorf("isWithMetaData() got \"%v\", want \"%v\"", attachmentEntry.isWithMetaData(), tt.resultIsWithMetaData)
}
if strings.Compare(attachmentEntry.Base64, tt.base64Expected) != 0 {
t.Errorf("Base64 got \"%v\", want \"%v\"", attachmentEntry.Base64, tt.base64Expected)
}
if strings.Compare(attachmentEntry.FileName, tt.fileNameExpected) != 0 {
t.Errorf("FileName got \"%v\", want \"%v\"", attachmentEntry.FileName, tt.fileNameExpected)
}
if strings.Compare(attachmentEntry.MimeInfo, tt.mimeInfoExpected) != 0 {
t.Errorf("MimeInfo got \"%v\", want \"%v\"", attachmentEntry.MimeInfo, tt.mimeInfoExpected)
}
if strings.Compare(attachmentEntry.toDataForSignal(), tt.toDataForSignal) != 0 {
t.Errorf("toDataForSignal() got \"%v\", want \"%v\"", attachmentEntry.toDataForSignal(), tt.toDataForSignal)
}
})
}
}

View File

@@ -461,7 +461,7 @@ func (s *SignalClient) UnregisterNumber(number string, deleteAccount bool, delet
command := []string{"--config", s.signalCliConfig, "-a", number, "deleteLocalAccountData"} command := []string{"--config", s.signalCliConfig, "-a", number, "deleteLocalAccountData"}
_, err2 := s.cliClient.Execute(true, command, "") _, err2 := s.cliClient.Execute(true, command, "")
if (err2 != nil) && (err != nil) { if (err2 != nil) && (err != nil) {
err = fmt.Errorf("%w (%w)", err, err2) err = fmt.Errorf("%w (%s)", err, err2.Error())
} else if (err2 != nil) && (err == nil) { } else if (err2 != nil) && (err == nil) {
err = err2 err = err2
} }

View File

@@ -241,7 +241,9 @@ var doc = `{
} }
], ],
"responses": { "responses": {
"200": {}, "200": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -281,7 +283,9 @@ var doc = `{
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -323,7 +327,9 @@ var doc = `{
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -365,7 +371,9 @@ var doc = `{
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -1200,7 +1208,9 @@ var doc = `{
} }
], ],
"responses": { "responses": {
"201": {}, "201": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -1472,7 +1482,9 @@ var doc = `{
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -1687,7 +1699,7 @@ var doc = `{
"properties": { "properties": {
"base64_attachment": { "base64_attachment": {
"type": "string", "type": "string",
"example": "<BASE64 ENCODED DATA> or 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' or 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'" "example": "'\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;base64,\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64,\u003cBASE64 ENCODED DATA\u003e'"
}, },
"is_group": { "is_group": {
"type": "boolean" "type": "boolean"
@@ -1712,9 +1724,13 @@ var doc = `{
"base64_attachments": { "base64_attachments": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string", "type": "string"
"example": "<BASE64 ENCODED DATA> or 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' or 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'" },
} "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"
]
}, },
"message": { "message": {
"type": "string" "type": "string"

View File

@@ -225,7 +225,9 @@
} }
], ],
"responses": { "responses": {
"200": {}, "200": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -265,7 +267,9 @@
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -307,7 +311,9 @@
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -349,7 +355,9 @@
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -1184,7 +1192,9 @@
} }
], ],
"responses": { "responses": {
"201": {}, "201": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -1456,7 +1466,9 @@
} }
], ],
"responses": { "responses": {
"204": {}, "204": {
"description": ""
},
"400": { "400": {
"description": "Bad Request", "description": "Bad Request",
"schema": { "schema": {
@@ -1671,7 +1683,7 @@
"properties": { "properties": {
"base64_attachment": { "base64_attachment": {
"type": "string", "type": "string",
"example": "<BASE64 ENCODED DATA> or 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' or 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'" "example": "'\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;base64,\u003cBASE64 ENCODED DATA\u003e' OR 'data:\u003cMIME-TYPE\u003e;filename=\u003cFILENAME\u003e;base64,\u003cBASE64 ENCODED DATA\u003e'"
}, },
"is_group": { "is_group": {
"type": "boolean" "type": "boolean"
@@ -1696,9 +1708,13 @@
"base64_attachments": { "base64_attachments": {
"type": "array", "type": "array",
"items": { "items": {
"type": "string", "type": "string"
"example": "<BASE64 ENCODED DATA> or 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' or 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'" },
} "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"
]
}, },
"message": { "message": {
"type": "string" "type": "string"

View File

@@ -106,8 +106,10 @@ definitions:
api.SendMessageV1: api.SendMessageV1:
properties: properties:
base64_attachment: base64_attachment:
example: '''<BASE64 ENCODED DATA>'' OR ''data:<MIME-TYPE>;base64,<BASE64 ENCODED
DATA>'' OR ''data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED
DATA>'''
type: string type: string
example: "<BASE64 ENCODED DATA> or 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' or 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'"
is_group: is_group:
type: boolean type: boolean
message: message:
@@ -122,9 +124,12 @@ definitions:
api.SendMessageV2: api.SendMessageV2:
properties: properties:
base64_attachments: base64_attachments:
example:
- <BASE64 ENCODED DATA>
- data:<MIME-TYPE>;base64<comma><BASE64 ENCODED DATA>
- data:<MIME-TYPE>;filename=<FILENAME>;base64<comma><BASE64 ENCODED DATA>
items: items:
type: string type: string
example: "<BASE64 ENCODED DATA> or 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' or 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'"
type: array type: array
message: message:
type: string type: string
@@ -391,7 +396,8 @@ paths:
produces: produces:
- application/json - application/json
responses: responses:
"200": {} "200":
description: ""
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
@@ -418,7 +424,8 @@ paths:
produces: produces:
- application/json - application/json
responses: responses:
"204": {} "204":
description: ""
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
@@ -446,19 +453,22 @@ paths:
produces: produces:
- application/json - application/json
responses: responses:
"204": {} "204":
description: ""
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/api.Error' $ref: '#/definitions/api.Error'
summary: Updates the info associated to a number on the contact list. If the contact doesnt exist yet, it will be added. summary: Updates the info associated to a number on the contact list. If the
contact doesnt exist yet, it will be added.
tags: tags:
- Contacts - Contacts
/v1/devices/{number}: /v1/devices/{number}:
post: post:
consumes: consumes:
- application/json - application/json
description: Links another device to this device. Only works, if this is the master device. description: Links another device to this device. Only works, if this is the
master device.
parameters: parameters:
- description: Registered Phone Number - description: Registered Phone Number
in: path in: path
@@ -474,7 +484,8 @@ paths:
produces: produces:
- application/json - application/json
responses: responses:
"204": {} "204":
description: ""
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
@@ -846,7 +857,8 @@ paths:
- Identities - Identities
/v1/identities/{number}/trust/{numberToTrust}: /v1/identities/{number}/trust/{numberToTrust}:
put: put:
description: Trust an identity. When 'trust_all_known_keys' is set to' true', all known keys of this user are trusted. **This is only recommended for testing.** description: Trust an identity. When 'trust_all_known_keys' is set to' true',
all known keys of this user are trusted. **This is only recommended for testing.**
parameters: parameters:
- description: Input Data - description: Input Data
in: body in: body
@@ -981,7 +993,9 @@ paths:
get: get:
consumes: consumes:
- application/json - application/json
description: Receives Signal Messages from the Signal Network. If you are running the docker container in normal/native mode, this is a GET endpoint. In json-rpc mode this is a websocket endpoint. description: Receives Signal Messages from the Signal Network. If you are running
the docker container in normal/native mode, this is a GET endpoint. In json-rpc
mode this is a websocket endpoint.
parameters: parameters:
- description: Registered Phone Number - description: Registered Phone Number
in: path in: path
@@ -1027,7 +1041,8 @@ paths:
produces: produces:
- application/json - application/json
responses: responses:
"201": {} "201":
description: ""
"400": "400":
description: Bad Request description: Bad Request
schema: schema:
@@ -1074,7 +1089,8 @@ paths:
get: get:
consumes: consumes:
- application/json - application/json
description: Check if one or more phone numbers are registered with the Signal Service. description: Check if one or more phone numbers are registered with the Signal
Service.
parameters: parameters:
- collectionFormat: multi - collectionFormat: multi
description: Numbers to check description: Numbers to check
@@ -1192,7 +1208,9 @@ paths:
post: post:
consumes: consumes:
- application/json - application/json
description: Disables push support for this device. **WARNING:** If *delete_account* is set to *true*, the account will be deleted from the Signal Server. This cannot be undone without loss. description: Disables push support for this device. **WARNING:** If *delete_account*
is set to *true*, the account will be deleted from the Signal Server. This
cannot be undone without loss.
parameters: parameters:
- description: Registered Phone Number - description: Registered Phone Number
in: path in: path
@@ -1207,7 +1225,8 @@ paths:
produces: produces:
- application/json - application/json
responses: responses:
"204": {} "204":
description: ""
"400": "400":
description: Bad Request description: Bad Request
schema: schema: