Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Messages limited to 2000 bytes, where is this limit defined? #1598

Closed
Thireus opened this issue Sep 28, 2024 · 5 comments
Closed

Messages limited to 2000 bytes, where is this limit defined? #1598

Thireus opened this issue Sep 28, 2024 · 5 comments
Labels
Milestone

Comments

@Thireus
Copy link

Thireus commented Sep 28, 2024

It would appear that historically messages were limited to 2000 bytes. This is no longer the case as clients now accept up to 65536 bytes: For ref. signalapp/Signal-Android@ccfcfa7 & signalapp/Signal-Android#5146

signal-cli-native appears to enforce this limit somehow, but I'm not able to find where that limit is defined. I also looked into https://github.com/signalapp/libsignal. Messages that are over 2,000 bytes get timed right at this limit.

Any idea?
I can confirm that my clients can receive more than 2000 bytes of message when the message is sent using signald which uses https://github.com/Turasa/libsignal-service-java but also when using official native clients for iOS/Android.

There was mention of this issue here as well: #6, but it's unclear how to turn messages into a text attachment. Is there a special kind of attachment to use that the clients would then render as if it was a message?

@Thireus
Copy link
Author

Thireus commented Sep 28, 2024

Not sure how come I've missed it... Isn't it these line?

if (message.messageText().length() > 2000) {

messageBuilder.withBody(message.messageText().substring(0, 2000));

Can we raise it to meet the new 65536 bytes limit please @AsamK ?

@Thireus
Copy link
Author

Thireus commented Sep 28, 2024

I understand this piece of code is supposed to create a special attachment with mime LONG_TEXT ("text/x-signal-plain"). But strangely that doesn't seem to work (my clients don't appear to receive it or process it). All I receive is the trimmed message.

final var textAttachment = AttachmentUtils.createAttachmentStream(streamDetails,
Optional.empty(),
uploadSpec);
messageBuilder.withBody(message.messageText().substring(0, 2000));
additionalAttachments.add(context.getAttachmentHelper().uploadAttachment(textAttachment));

@piechologist
Copy link

I'd rather have a higher limit too.

2000 chars is not that much and it's hard to tell if the message will get truncated because the use of UTF-whats-so-ever.

Attachments are inconvenient in Signal Desktop as they must be downloaded first and then opened in a second step.

A high message limit would mitigate those issues.

@Thireus
Copy link
Author

Thireus commented Sep 28, 2024

I managed to find a workaround by manually creating the "text/x-signal-plain" attachment and I can confirm it is working as expected (clients process it as text, even can apply style to it). The message can be empty as it will be replaced by the attachment text. This is exactly what the code here should be doing, so not sure why it does not work...

final var textAttachment = AttachmentUtils.createAttachmentStream(streamDetails,
Optional.empty(),
uploadSpec);
messageBuilder.withBody(message.messageText().substring(0, 2000));
additionalAttachments.add(context.getAttachmentHelper().uploadAttachment(textAttachment));

Here is a PoC with test.txt containing a large text:

import base64
import mimetypes

def get_base64_and_mimetype(file_path):
    # Add webp mime type manually if it's missing
    mimetypes.add_type('image/webp', '.webp')
    
    # Determine the mimetype based on the file extension
    mime_type, _ = mimetypes.guess_type(file_path)
    
    # Read the file in binary mode and encode it in base64
    with open(file_path, 'rb') as file:
        encoded_string = base64.b64encode(file.read()).decode('utf-8')
    
    return encoded_string, mime_type

base64_string, mime_type = get_base64_and_mimetype("test.txt")

import requests
import uuid

# Construct the JSON-RPC request using data URI cf. https://github.com/AsamK/signal-cli/blob/dbbc3fbd71faf18e73ad409a3a7b308ce56cd52d/man/signal-cli.1.adoc#L24
res = requests.post(
    "http://xxxxx:xxxx/api/v1/rpc",
    json={
        "jsonrpc": "2.0",
        "method": "send",
        "id": str(uuid.uuid4()),  # Generate a new UUID for the request
        "params": {
            "account": "+xxxx",
            "recipients": ["+xxxx"],
            "message": "This message will be replaced once the attachment gets downloaded",
            "textStyle": ["5:4:MONOSPACE", "10:4:SPOILER"],
            "attachments": [
                f"data:text/x-signal-plain;filename=test.txt;base64,{base64_string}"
            ]
        }
    }
)

@AsamK AsamK closed this as completed in fab1b96 Sep 29, 2024
@AsamK
Copy link
Owner

AsamK commented Sep 29, 2024

Signal-Android has the same logic to split messages larger than 2000 UTF-16 chars: MAX_PRIMARY_SIZE
Splitting is implemented here: https://github.com/signalapp/Signal-Android/blob/main/app/src/main/java/org/thoughtcrime/securesms/util/MessageUtil.java#L28

There was a bug in the signal-cli implementation, that the text attachment was only sent when there were other attachments as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants