Skip to content

Commit

Permalink
Can receive attachments in sync messages.
Browse files Browse the repository at this point in the history
  • Loading branch information
hoehermann committed Jul 17, 2024
1 parent c48d33b commit 856f62a
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 27 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ A libpurple/Pidgin protocol plugin for Signal (formerly textsecure) using [presa

* Can link as secondary device via QR-Code.
* Receives a simple text message from a contact or a group.
* Receives attachments (only pictures tested for now).
* Displays quotes, reactions and incoming calls.
* Receives attachments (see caveats below).
* Can reply with a simple text message.
* Will add buddies to contact list unconditionally.
* Can list groups as rooms and open the chat.
Expand All @@ -28,26 +29,27 @@ These lists are not exhaustive.

* Add chats to contact list unconditionally.
* Forward all errors to front-end properly.
* Send an attachment (example exists in flare).

##### On Hold

* Mark messages as "read" (currently not implemented in back-end, see https://github.com/whisperfish/presage/issues/141)
* Reply to a specific message (no example exists in back-end)
* Mark messages as "read" (currently not implemented in back-end, see https://github.com/whisperfish/presage/issues/141). At time of writing, notifications on main device are deleted after answering via linked device. So that is working alright.
* Reply to a specific message (no example exists in back-end).

##### "Contributions Welcome"

* Configuration option whether to add contacts to buddy list or not
* Reasonable generation of type declarations
* Stickers, mentions, replies, quotes, styles,…
* Reasonable generation of C headers and rust constants
* Stickers, mentions, replies, styles,…
* Display typing notifications
* Display receipts (not important)
* Support for alternative UIs (Spectrum, Bitlbee)
* Support for adding contacts via phone number (rarely used)
* Support for adding contacts via phone number

#### Known Issues

* Handling errors when sending messages is barely tested.
* Attachment sent to group from main device is not handled.
* Attachments end up in the conversation of the sender, not the destination (espeically confusing when a group chat is involved).

### Building

Expand Down
2 changes: 1 addition & 1 deletion src/c/attachment.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static void presage_xfer_announce(PurpleConnection *connection, const char *who,
void presage_handle_attachment(PurpleConnection *connection, const char *who, uint64_t timestamp, void *blob, uint64_t blobsize, const char *filename) {
g_return_if_fail(connection != NULL);
PurpleAccount *account = purple_connection_get_account(connection);

presage_xfer_announce(connection, who, filename);

PurpleXfer * xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, who);
Expand Down
52 changes: 33 additions & 19 deletions src/rust/src/receive_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ fn print_message<C: presage::store::Store>(

let format_data_message = |thread: &presage::store::Thread, data_message: &presage::libsignal_service::content::DataMessage| {
match data_message {
// Quote
presage::libsignal_service::content::DataMessage {
quote: Some(presage::proto::data_message::Quote {
text: Some(quoted_text),
Expand All @@ -32,6 +33,7 @@ fn print_message<C: presage::store::Store>(
// TODO: add ellipsis if quoted_text contains more than one line
Some(format!("> {firstline}\n\n{body}"))
}
// Reaction
presage::libsignal_service::content::DataMessage {
reaction:
Some(presage::proto::data_message::Reaction {
Expand All @@ -53,15 +55,19 @@ fn print_message<C: presage::store::Store>(
crate::core::purple_debug(account, 2, String::from("message reacted to has no body\n"));
return None;
};

Some(format!("Reacted with {emoji} to message: \"{body}\""))
let firstline = body.split("\n").next().unwrap_or("<message body missing>");
// TODO: add ellipsis if body contains more than one line
Some(format!("Reacted with {emoji} to message „{firstline}“."))
}
// Plan text message
// TODO: resolve mentions
presage::libsignal_service::content::DataMessage {
body: Some(body), ..
} => Some(body.to_string()),
c => {
crate::core::purple_debug(account, 2, format!("Empty data message {c:?}\n"));
// Note: flags: Some(4) with a timestamp (and a profile_key?) may indicate "message sent"
crate::core::purple_debug(account, 2, format!("DataMessage without body {c:?}\n"));
// NOTE: This happens when receiving a file, but not providing a text
// NOTE: flags: Some(4) with a timestamp (and a profile_key?) may indicate "message sent"
// Some("message has been sent".to_string())
None
}
Expand Down Expand Up @@ -134,7 +140,6 @@ fn print_message<C: presage::store::Store>(
message.body = std::ffi::CString::new(body).unwrap().into_raw();
}
};
//println!("{who} in {group} wrote {body}");
crate::bridge::append_message(&message);
}
}
Expand All @@ -151,10 +156,21 @@ async fn process_incoming_message<C: presage::store::Store>(
) {
print_message(manager, content, account);

if let presage::libsignal_service::content::ContentBody::DataMessage(presage::libsignal_service::content::DataMessage { attachments, .. }) = &content.body {
if let presage::libsignal_service::content::ContentBody::DataMessage(presage::libsignal_service::content::DataMessage { attachments, .. })
| presage::libsignal_service::content::ContentBody::SynchronizeMessage(presage::libsignal_service::content::SyncMessage {
sent: Some(presage::proto::sync_message::Sent {
message: Some(presage::libsignal_service::content::DataMessage { attachments, .. }),
..
}),
..
}) = &content.body
{
for attachment_pointer in attachments {
let mut message = crate::bridge::Presage::from_account(account);
message.timestamp = content.metadata.timestamp;
// TODO: `who` and `group` should be filled with the Receiver (group or contact) information
// so they end up in the correct conversation
// relevant for sync messages in particular
message.who = std::ffi::CString::new(content.metadata.sender.uuid.to_string()).unwrap().into_raw();

let Ok(attachment_data) = manager.get_attachment(attachment_pointer).await else {
Expand All @@ -164,19 +180,17 @@ async fn process_incoming_message<C: presage::store::Store>(
continue;
};

// TODO: have an explicit mapping of mime-type to extension
let extensions = mime_guess::get_mime_extensions_str(attachment_pointer.content_type.as_deref().unwrap_or("application/octet-stream"));
let extension = extensions
.and_then(|e| {
e.last() // using e.last here yields jpg instead of jfif, but also pnz instead of png and mpeg4 instead of mp4 😬
})
.unwrap_or(&"bin");
/*
let filename = attachment_pointer
.file_name
.clone()
.unwrap_or_else(|| Local::now().format("%Y-%m-%d-%H-%M-%s").to_string());
*/
let mimetype = attachment_pointer.content_type.as_deref().unwrap_or("application/octet-stream");
let extension = match mimetype {
"image/jpeg" => "jpg",
"image/png" => "png",
"video/mp4" => "mp4",
mimetype => {
let extensions = mime_guess::get_mime_extensions_str(mimetype);
extensions.and_then(|e| e.first()).unwrap_or(&"bin")
}
};

let filename = match attachment_pointer.attachment_identifier.clone().unwrap() {
presage::proto::attachment_pointer::AttachmentIdentifier::CdnId(id) => id.to_string(),
presage::proto::attachment_pointer::AttachmentIdentifier::CdnKey(key) => key,
Expand Down

0 comments on commit 856f62a

Please sign in to comment.