Compare commits

...

16 Commits

Author SHA1 Message Date
47cc4c1772 Fix displaying contact name 2022-05-23 21:28:27 +01:00
3e146313a9 Get compiling against libdeltachat 1.83 2022-05-23 20:47:35 +01:00
18ae21daad tracking configure as connected is buggy 2022-05-23 18:35:26 +01:00
5f9c60ce4b Compile against purple 2.14 2022-05-23 18:34:56 +01:00
cff109abb5 Display text associated with an incoming image message 2021-04-04 13:35:55 +01:00
6d4454f356 Fix showing duplicated messages
This commit also has some other changes sprinkled in but the main
effect is that messages are only shown precisely once. Next stage must
be to move away from serv_got_im and friends.
2021-01-12 00:04:46 +00:00
36b6b37e78 Start using chats to correctly attribute messages to windows
Without this, "self_bcc" would create messages that were attributed to
the self-chat.

We still have duplicate messages with self-bcc enabled.
2021-01-11 00:39:47 +00:00
4484f51c5d Support the "BCC self" option 2021-01-10 18:08:22 +00:00
83488c31fe Support IMEX messages 2021-01-10 17:12:29 +00:00
6da5908686 Upgrade to deltachat v1.50.0 2021-01-10 15:56:07 +00:00
25af2f6b49 Fix licensing issues 2019-08-20 22:01:47 +01:00
62a59b7b0a Merge branch '8-handle-incoming-images' into 'master'
Handle incoming images

See merge request lupine/purple-plugin-delta!14
2019-04-22 00:15:58 +00:00
ed3c092f13 Send images in messages 2019-04-22 01:00:09 +01:00
5f01cdf21c Handle incoming images 2019-04-21 23:22:42 +01:00
cb3872bea8 Merge branch 'cleanup-debug-output' into 'master'
Clean up debug output

Closes #16

See merge request lupine/purple-plugin-delta!13
2019-04-21 20:39:06 +00:00
81ccd8c82c Clean up debug output
Remove the unnecessary util.c/util.h files and use purple_debug_info()
throughout instead. This is more direct. Also replace all usages of
printf() with the same helper.

This makes the Delta plugin usable in command-line applications like
Finch. Previously, the printf() output was messing up the screen!
2019-04-21 21:27:45 +01:00
10 changed files with 381 additions and 290 deletions

View File

@@ -1,26 +0,0 @@
---
Debian 9:
stage: build
image: debian:stretch
script:
# deltachat-core needs a later version of meson, fortunately it's in stretch-backports
- echo 'deb http://httpredir.debian.org/debian/ stretch-backports main' > /etc/apt/sources.list.d/stretch-backports.list
- apt update
# libetpan
# FIXME: libetpan 1.16 is available in Debian, but we need 1.17+: https://github.com/deltachat/deltachat-core/issues/157
- apt install --no-install-recommends -yy -t stretch-backports build-essential autoconf automake libtool libdb-dev libexpat1-dev libsasl2-dev libssl1.0-dev
- cd vendor && tar -xvzf libetpan-1.8.tar.gz && cd libetpan-1.8 && ./autogen.sh && ./configure && make && make install && cd ../..
- cp /usr/local/lib/libetpan.so libetpan.so
# deltachat-core
- apt install --no-install-recommends -t stretch-backports -yy meson ninja-build pkg-config zlib1g-dev liblockfile-dev libsqlite3-dev libsasl2-dev libssl1.0-dev libbz2-dev
- cd vendor && tar -xvzf deltachat-core-0.41.0.tar.gz && cd deltachat-core-0.41.0 && mkdir builddir && cd builddir && meson && ninja && ninja install && cd ../../..
- cp /usr/local/lib/x86_64-linux-gnu/libdeltachat.so libdeltachat.so
# purple-plugin-delta
- apt install --no-install-recommends -t stretch-backports -yy libpurple-dev libglib2.0-dev
- make
artifacts:
paths:
- libetpan.so
- libdeltachat.so
- libdelta.so

View File

@@ -7,7 +7,7 @@ email - which is to say, SMTP+IMAP.
Delta has:
* A mature [core library](https://github.com/deltachat/deltachat-core)
* A mature [core library](https://github.com/deltachat/deltachat-core-rust)
* A mature [Android application](https://github.com/deltachat/deltachat-android)
* An experimental [iOS application](https://github.com/deltachat/deltachat-ios)
* An electron [desktop application](https://github.com/deltachat/deltachat-desktop)
@@ -19,22 +19,33 @@ mobile clients to send and receive IMs over SMTP+IMAP. It may be useful for
GUI desktop usage **without** an Electron dependency, or console desktop usage.
Current status is probably best described as "skunkworks", although connecting
to an account and sending / receiving text messages should work reliably. You
can view specific progress on [the issue board](https://gitlab.com/lupine/purple-plugin-delta/boards).
to an account and sending / receiving text and image messages should work
reliably in pidgin. Chatty supports text messages, and can be coaxed into using
this plugin, but there's a long way to go with that yet.
A big refactoring to use "proper" purple IM structures is necessary to make
further progress, I think.
I also need to implement support for the buddy list.
We currrently build against deltachat v1.50.0. You'll need to build and install
deltachat-ffi separately and ensure that it's available via `pkg-config` for
deltachat to install.
## Build
Very basic instructions at present. First, `deltachat-core` isn't packaged, so
you'll need to build and install it according to
[these instructions](https://github.com/deltachat/deltachat-core/blob/master/README.md#build).
There are some licensing issues at present, so you shouldn't build this plugin.
Now, you'll need some other build dependencies:
`deltachat-core-rust` uses a vendored openssl 1, unconditionally links it, and
is MPL-licensed.
```
sudo apt install build-essential libpurple-dev libglib2.0-dev
```
`purple-plugin-delta` is GPLv3 without the [OpenSSL exemption](https://people.gnome.org/~markmc/openssl-and-the-gpl.html)
Finally, run `make` to create a `libdelta.so` file.
`libpurple` itself is GPLv2 without the OpenSSL exemption.
There's no point to `purple-plugin-delta` adding the OpenSSL exemption because
`libpurple` lacks it, and in any event, it will be unnecessary with the next
major version of OpenSSL. So, time should resolve this for us one way or another.
## Use

View File

@@ -1,47 +1,20 @@
#include <connection.h>
#include <debug.h>
#include <eventloop.h>
#include <imgstore.h>
#include <util.h>
#include <string.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "delta-connection.h"
#include "libdelta.h"
#include "util.h"
void delta_recv_im(DeltaConnectionData *conn, uint32_t msg_id);
#define IMEX_RECEIVED_MESSAGE "Setup message received. To apply it, reply with:\nIMEX: %d nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn-nnnn\nNo whitespace in the setup-code!"
void *imap_thread_func(void *delta_connection_data)
{
DeltaConnectionData *conn = (DeltaConnectionData *)delta_connection_data;
g_assert(conn != NULL);
dc_context_t *mailbox = conn->mailbox;
g_assert(mailbox != NULL);
while (conn->runthreads) {
dc_perform_imap_jobs(mailbox);
dc_perform_imap_fetch(mailbox);
dc_perform_imap_idle(mailbox);
}
return NULL;
}
void *smtp_thread_func(void *delta_connection_data)
{
DeltaConnectionData *conn = (DeltaConnectionData *)delta_connection_data;
g_assert(conn != NULL);
dc_context_t *mailbox = conn->mailbox;
g_assert(mailbox != NULL);
while (conn->runthreads) {
dc_perform_smtp_jobs(mailbox);
dc_perform_smtp_idle(mailbox);
}
return NULL;
}
void delta_recv_im(DeltaConnectionData *conn, dc_msg_t *msg);
void
_transpose_config(dc_context_t *mailbox, PurpleAccount *acct)
@@ -59,6 +32,8 @@ _transpose_config(dc_context_t *mailbox, PurpleAccount *acct)
const char *smtp_pass = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_SMTP_PASS, NULL);
const char *smtp_port = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT, NULL);
gboolean bcc_self = purple_account_get_bool(acct, PLUGIN_ACCOUNT_OPT_BCC_SELF, FALSE);
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_ADDR, addr);
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_DISPLAY_NAME, display);
@@ -71,6 +46,12 @@ _transpose_config(dc_context_t *mailbox, PurpleAccount *acct)
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_USER, smtp_user);
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_PASS, smtp_pass);
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT, smtp_port);
if (bcc_self) {
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_BCC_SELF, "1");
} else {
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_BCC_SELF, "0");
};
}
typedef struct {
@@ -78,14 +59,10 @@ typedef struct {
// Used by delta_process_incoming_message
uint32_t msg_id;
gboolean msg_changed;
// Used by delta_process_connection_state
int connection_state;
// Used by delta_process_http_get
char *http_url;
char *http_response;
pthread_cond_t *http_wait;
// int connection_state;
} ProcessRequest;
gboolean
@@ -95,13 +72,14 @@ delta_process_incoming_message(void *data)
g_assert(pr != NULL);
g_assert(pr->conn != NULL);
delta_recv_im(pr->conn, pr->msg_id);
dc_msg_t *msg = dc_get_msg(pr->conn->mailbox, pr->msg_id);
delta_recv_im(pr->conn, msg);
dc_msg_unref(msg);
g_free(data);
return FALSE;
}
/*
gboolean
delta_process_connection_state(void *data)
{
@@ -117,83 +95,38 @@ delta_process_connection_state(void *data)
);
if (pr->connection_state == MAX_DELTA_CONFIGURE) {
purple_connection_set_state(pr->conn->pc, PURPLE_CONNECTED);
}
g_free(data);
return FALSE;
}
*/
gboolean
delta_process_fresh_messages(void *data)
{
ProcessRequest *pr = (ProcessRequest *)data;
g_assert(pr != NULL);
g_assert(pr->conn != NULL);
g_assert(pr->conn->mailbox != NULL);
dc_context_t *mailbox = pr->conn->mailbox;
g_assert(mailbox != NULL);
// Spot any messages received while offline
dc_array_t *fresh_msgs = dc_get_fresh_msgs(pr->conn->mailbox);
dc_array_t *fresh_msgs = dc_get_fresh_msgs(mailbox);
size_t fresh_count = dc_array_get_cnt(fresh_msgs);
printf("*** fresh_count: %zu\n", fresh_count);
purple_debug_info(PLUGIN_ID, "fresh_count: %zu\n", fresh_count);
for(size_t i = 0; i < fresh_count; i++) {
delta_recv_im(pr->conn, dc_array_get_id(fresh_msgs, i));
uint32_t msg_id = dc_array_get_id(fresh_msgs, i);
dc_msg_t *msg = dc_get_msg(mailbox, msg_id);
delta_recv_im(pr->conn, msg);
}
g_free(fresh_msgs);
return FALSE;
}
void
delta_process_http_get_cb(
PurpleUtilFetchUrlData *url_data,
gpointer user_data,
const gchar *url_text,
gsize len,
const gchar *error_message
)
{
UNUSED(url_data);
ProcessRequest *pr = (ProcessRequest *)user_data;
g_assert(pr != NULL);
g_assert(pr->http_wait != NULL);
if (len == 0) {
printf("Failed to GET %s: %s\n", pr->http_url, error_message);
pr->http_response = NULL;
goto out;
}
pr->http_response = g_malloc(len);
g_assert(pr->http_response != NULL);
strncpy(pr->http_response, url_text, len);
out:
pthread_cond_broadcast(pr->http_wait);
return;
}
gboolean
delta_process_http_get(void *data)
{
ProcessRequest *pr = (ProcessRequest *)data;
g_assert(pr != NULL);
g_assert(pr->http_url != NULL);
purple_util_fetch_url(
pr->http_url,
TRUE,
NULL,
TRUE,
delta_process_http_get_cb,
data
);
dc_array_unref(fresh_msgs);
return FALSE;
}
@@ -214,101 +147,113 @@ delta_build_process_request(DeltaConnectionData *conn)
// Do not call any libpurple or delta functions in here, as it is not
// thread-safe and events may be dispatched from any delta thread. Use
// purple_timeout_add(0, callback, data) to run on the main thread instead
uintptr_t
my_delta_handler(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t data2)
void *
delta_event_handler(void *context)
{
DeltaConnectionData *conn = (DeltaConnectionData *)dc_get_userdata(mailbox);
DeltaConnectionData *conn = (DeltaConnectionData *)context;
g_assert(conn != NULL);
ProcessRequest *pr = NULL;
uintptr_t out = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
dc_context_t *mailbox = conn->mailbox;
dc_event_emitter_t* emitter = dc_get_event_emitter(mailbox);
dc_event_t* event;
printf("my_delta_handler(mailbox, %d, %lu, %lu)\n", event, data1, data2);
// FIXME: do we still need runthreads?
while (conn->runthreads && (event = dc_get_next_event(emitter)) != NULL) {
ProcessRequest *pr = NULL;
int event_id = dc_event_get_id(event);
switch (event) {
case DC_EVENT_SMTP_MESSAGE_SENT:
case DC_EVENT_IMAP_CONNECTED:
case DC_EVENT_SMTP_CONNECTED:
case DC_EVENT_INFO:
printf("INFO: %s\n", (char *)data2);
break;
case DC_EVENT_WARNING:
printf("WARNING: %s\n", (char *)data2);
break;
case DC_EVENT_ERROR:
case DC_EVENT_ERROR_NETWORK:
printf("ERROR: %d: %s\n", (int)data1, (char *)data2);
break;
purple_debug_info(PLUGIN_ID, "Event %d received from Delta.\n", event_id);
case DC_EVENT_MSGS_CHANGED:
pr = delta_build_process_request(conn);
purple_timeout_add(0, delta_process_fresh_messages, pr);
break;
case DC_EVENT_INCOMING_MSG:
// data1 is chat_id, which we don't seem to need yet.
// TODO: It may be needed for group chats
pr = delta_build_process_request(conn);
pr->msg_id = (uint32_t)data2;
purple_timeout_add(0, delta_process_incoming_message, pr);
break;
// These are all to do with sending & receiving messages. The real meat of
// the event loop
case DC_EVENT_MSG_DELIVERED:
case DC_EVENT_MSG_READ:
case DC_EVENT_CHAT_MODIFIED:
case DC_EVENT_CONTACTS_CHANGED:
debug("TODO!\n");
break;
case DC_EVENT_CONFIGURE_PROGRESS:
pr = delta_build_process_request(conn);
pr->connection_state = (int)data1;
purple_timeout_add(0, delta_process_connection_state, pr);
break;
case DC_EVENT_HTTP_GET:
printf("HTTP GET requested: %s\n", (char *)data1);
pthread_mutex_lock(&mutex);
pr = delta_build_process_request(conn);
g_assert(pr != NULL);
pr->http_url = (char *)data1;
pr->http_wait = &cond;
purple_timeout_add(0, delta_process_http_get, pr);
// Wait patiently for the HTTP GET to complete
pthread_cond_wait(pr->http_wait, &mutex);
out = (uintptr_t)pr->http_response;
pthread_mutex_unlock(&mutex);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
g_free(pr);
break;
case DC_EVENT_IS_OFFLINE:
if ( conn->pc == NULL || !PURPLE_CONNECTION_IS_CONNECTED(conn->pc) ) {
debug("Telling Delta we are offline\n");
out = 1;
} else {
debug("Telling Delta we are online\n");
switch (event_id) {
case DC_EVENT_SMTP_MESSAGE_SENT:
case DC_EVENT_IMAP_CONNECTED:
case DC_EVENT_SMTP_CONNECTED:
case DC_EVENT_IMAP_MESSAGE_DELETED:
case DC_EVENT_IMAP_MESSAGE_MOVED:
case DC_EVENT_INFO: {
char *info = dc_event_get_data2_str(event);
purple_debug_info(PLUGIN_ID, "INFO from Delta: %s\n", info);
dc_str_unref(info);
break;
}
case DC_EVENT_WARNING: {
char *warn = dc_event_get_data2_str(event);
purple_debug_info(PLUGIN_ID, "WARNING from Delta: %s\n", warn);
dc_str_unref(warn);
break;
}
case DC_EVENT_ERROR: {
int errcode = dc_event_get_data1_int(event);
char *err = dc_event_get_data2_str(event);
purple_debug_info(PLUGIN_ID, "ERROR from Delta: %d: %s\n", errcode, err);
dc_str_unref(err);
break;
}
break;
case DC_EVENT_GET_STRING:
break;
default:
printf("Unknown event: %d\n", event);
}
return out;
case DC_EVENT_MSGS_CHANGED: {
// This event may be issued for a single message, in which case the
// message ID is in data2 and we should treat it as an incoming msg
// FIXME: this leads to duplicate messages when it's an outgoing
// message we just sent
uint32_t msg_id = dc_event_get_data2_int(event);
pr = delta_build_process_request(conn);
if (msg_id) {
// FIXME: for now, only display IMEX setup messages to avoid duplicates
pr->msg_id = msg_id;
pr->msg_changed = TRUE;
purple_timeout_add(0, delta_process_incoming_message, pr);
} else {
purple_timeout_add(0, delta_process_fresh_messages, pr);
}
break;
}
case DC_EVENT_INCOMING_MSG:
// data1 is chat_id, which we don't seem to need yet.
// TODO: It may be needed for group chats
pr = delta_build_process_request(conn);
pr->msg_id = (uint32_t)dc_event_get_data2_int(event);
purple_timeout_add(0, delta_process_incoming_message, pr);
break;
// Things left to do
case DC_EVENT_CHAT_EPHEMERAL_TIMER_MODIFIED:
case DC_EVENT_NEW_BLOB_FILE:
case DC_EVENT_DELETED_BLOB_FILE:
case DC_EVENT_MSG_DELIVERED:
case DC_EVENT_MSG_READ:
case DC_EVENT_MSG_FAILED:
case DC_EVENT_CHAT_MODIFIED:
case DC_EVENT_CONTACTS_CHANGED:
case DC_EVENT_ERROR_SELF_NOT_IN_GROUP:
case DC_EVENT_IMEX_FILE_WRITTEN:
case DC_EVENT_IMEX_PROGRESS:
case DC_EVENT_LOCATION_CHANGED:
case DC_EVENT_MSGS_NOTICED:
case DC_EVENT_SECUREJOIN_INVITER_PROGRESS:
case DC_EVENT_SECUREJOIN_JOINER_PROGRESS:
purple_debug_info(PLUGIN_ID, "Event %d is TODO\n", event_id);
break;
case DC_EVENT_CONFIGURE_PROGRESS:
//pr = delta_build_process_request(conn);
//pr->connection_state = dc_event_get_data1_int(event);
//purple_timeout_add(0, delta_process_connection_state, pr);
purple_debug_info(PLUGIN_ID, "Configure progress: %d\n", dc_event_get_data1_int(event));
break;
default:
purple_debug_info(PLUGIN_ID, "Unknown Delta event: %d\n", event_id);
}
dc_event_unref(event);
}
dc_event_emitter_unref(emitter);
return NULL;
}
void
@@ -333,18 +278,15 @@ delta_connection_free(PurpleConnection *pc)
conn->runthreads = 0;
if (conn->mailbox != NULL) {
dc_maybe_network(conn->mailbox);
dc_stop_ongoing_process(conn->mailbox);
dc_stop_io(conn->mailbox);
// TODO: correctly handle join failing
if (pthread_join(conn->imap_thread, NULL) != 0) {
debug("joining imap thread failed!\n");
}
if (pthread_join(conn->smtp_thread, NULL) != 0) {
debug("joining smtp thread failed!\n");
purple_debug_info(PLUGIN_ID, "Joining event thread\n");
if (pthread_join(conn->event_thread, NULL) != 0) {
purple_debug_info(PLUGIN_ID, "joining event thread failed\n");
}
dc_stop_ongoing_process(conn->mailbox);
dc_close(conn->mailbox);
dc_context_unref(conn->mailbox);
}
@@ -363,30 +305,49 @@ delta_connection_start_login(PurpleConnection *pc)
char dbname[1024];
PurpleAccount *acct = pc->account;
DeltaConnectionData *conn = purple_connection_get_protocol_data(pc);
dc_context_t *mailbox = dc_context_new(my_delta_handler, conn, NULL);
dc_context_t *mailbox = NULL;
g_snprintf(
dbname, 1024, "%s%sdelta_db-%s",
purple_user_dir(), G_DIR_SEPARATOR_S, acct->username
);
if (!dc_open(mailbox, dbname, NULL)) {
debug("dc_open returned false...?\n");
}
mailbox = dc_context_new(PLUGIN_ID, dbname, NULL);
conn->mailbox = mailbox;
_transpose_config(mailbox, acct);
conn->runthreads = 1;
pthread_create(&conn->imap_thread, NULL, imap_thread_func, conn);
pthread_create(&conn->smtp_thread, NULL, smtp_thread_func, conn);
pthread_create(&conn->event_thread, NULL, delta_event_handler, conn);
dc_configure(mailbox);
dc_start_io(mailbox);
dc_maybe_network(mailbox);
purple_connection_set_state(pc, PURPLE_CONNECTED);
return;
}
gboolean delta_try_process_imex(dc_context_t *mailbox, char *text) {
if (!g_str_has_prefix(text, "IMEX: ")) {
return FALSE;
}
gchar **parts = g_strsplit(text, " ", 3);
if (g_strv_length(parts) != 3) {
g_strfreev(parts);
return FALSE;
}
int msg_id = atoi(parts[1]);
gboolean success = dc_continue_key_transfer(mailbox, msg_id, parts[2]);
g_strfreev(parts);
return success;
}
int
delta_send_im(PurpleConnection *pc, const char *who, const char *message, PurpleMessageFlags flags)
{
@@ -400,17 +361,80 @@ delta_send_im(PurpleConnection *pc, const char *who, const char *message, Purple
uint32_t contact_id = dc_create_contact(mailbox, NULL, who);
uint32_t chat_id = dc_create_chat_by_contact_id(mailbox, contact_id);
char *unescaped_message = purple_unescape_html(message);
g_assert(unescaped_message != NULL);
dc_send_text_msg(mailbox, chat_id, unescaped_message);
g_free(unescaped_message);
GData *attrs;
const char *msg_ptr, *start, *end;
msg_ptr = message;
return 1; // success; echo the message to the chat window
// Send each image included in the message.
while (purple_markup_find_tag("img", msg_ptr, &start, &end, &attrs) == TRUE) {
char *id_str = g_datalist_id_get_data(&attrs, g_quark_from_string("id"));
purple_debug_info(PLUGIN_ID, "In a loop, got %s\n", id_str);
msg_ptr = end + 1;
if (id_str == NULL || strlen(id_str) == 0) {
g_datalist_clear(&attrs);
continue;
}
int id = atoi(id_str);
g_datalist_clear(&attrs);
if (id <= 0) {
continue;
}
GError *err = NULL;
char *tempdir = g_dir_make_tmp(NULL, &err);
if (err != NULL) {
purple_debug_info(PLUGIN_ID, "Couldn't get a temporary dir for image %d: %s", id, err->message);
g_free(err);
continue;
}
PurpleStoredImage *img = purple_imgstore_find_by_id(id);
const char *filename = purple_imgstore_get_filename(img);
const char *extension = purple_imgstore_get_extension(img);
gconstpointer data = purple_imgstore_get_data(img);
char *path = g_strdup_printf("%s/%s", tempdir, filename);
g_file_set_contents(path, data, purple_imgstore_get_size(img), &err);
if (err != NULL) {
purple_debug_info(PLUGIN_ID, "failed to write %s to temporary file: %s\n", filename, err->message);
g_free(err);
goto next;
}
purple_debug_info(PLUGIN_ID, "Sending image %s from imgstore: %d\n", filename, id);
dc_msg_t *img_msg = dc_msg_new(mailbox, DC_MSG_IMAGE);
dc_msg_set_file(img_msg, path, extension);
dc_send_msg(mailbox, chat_id, img_msg);
dc_msg_unref(img_msg);
next:
remove(path);
remove(tempdir);
g_free(path);
}
// Send any text left
char *stripped_message = purple_markup_strip_html(message);
g_assert(stripped_message != NULL);
if (strlen(stripped_message) > 0) {
if (!delta_try_process_imex(mailbox, stripped_message)) {
dc_send_text_msg(mailbox, chat_id, stripped_message);
}
}
g_free(stripped_message);
return 0; // success; don't echo the message to the chat window since we display it anyway
}
void
delta_recv_im(DeltaConnectionData *conn, uint32_t msg_id)
delta_recv_im(DeltaConnectionData *conn, dc_msg_t *msg)
{
dc_context_t *mailbox = conn->mailbox;
g_assert(mailbox != NULL);
@@ -418,25 +442,121 @@ delta_recv_im(DeltaConnectionData *conn, uint32_t msg_id)
PurpleConnection *pc = conn->pc;
g_assert(pc != NULL);
dc_msg_t* msg = dc_get_msg(mailbox, msg_id);
uint32_t msg_id = dc_msg_get_id(msg);
int viewtype = dc_msg_get_viewtype(msg);
time_t timestamp = dc_msg_get_timestamp(msg);
char *text = dc_msg_get_text(msg);
uint32_t contact_id = dc_msg_get_from_id(msg);
uint32_t chat_id = dc_msg_get_chat_id(msg);
dc_contact_t *from = dc_get_contact(mailbox, dc_msg_get_from_id(msg));
dc_chat_t *chat = dc_get_chat(mailbox, chat_id);
dc_array_t *contacts = dc_get_chat_contacts(mailbox, chat_id);
int num_contacts = dc_array_get_cnt(contacts);
dc_contact_t *contact = dc_get_contact(mailbox, contact_id);
if (contact == NULL) {
debug("Unknown contact! FIXME!\n");
if (chat == NULL) {
purple_debug_info(PLUGIN_ID, "Receiving IM: unknown chat: %d\n", chat_id);
goto out;
}
char *who = dc_contact_get_addr(contact);
if (dc_chat_get_type(chat) == DC_CHAT_TYPE_GROUP) {
purple_debug_info(PLUGIN_ID, "Receiving IM: group chat with ID %d! Not yet supported\n", chat_id);
goto out;
}
serv_got_im(pc, who, text, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_RAW, timestamp);
if (num_contacts != 1) {
purple_debug_info(PLUGIN_ID, "Receiving IM: 1-1 chat %d with %d contacts instead of 1!\n", chat_id, num_contacts);
goto out;
}
// FIXME: using dc_array_get_contact_id fails here, complaining that it's not an array of locations
dc_contact_t *contact = dc_get_contact(mailbox, dc_array_get_id(contacts, 0));
char *who = NULL;
// In the current architecture, delta_send_im and delta_recv_im must agree
// on the value for 'who'. Using the email address is an easy cheat for this
// but gets shaky in the long term.
if (contact != NULL) {
who = dc_contact_get_addr(contact);
} else {
who = dc_chat_get_name(chat);
}
int flags = 0;
int state = dc_msg_get_state(msg);
if (state == DC_STATE_IN_FRESH || state == DC_STATE_IN_NOTICED || state == DC_STATE_IN_SEEN) {
flags |= PURPLE_MESSAGE_RECV;
} else {
flags |= PURPLE_MESSAGE_SEND;
}
// FIXME: as a massive hack, convert IMEX setup messages into a text message
// prompting the user how to trigger the IMEX filter in outgoing messages.
if (dc_msg_is_setupmessage(msg)) {
purple_debug_info(PLUGIN_ID, "Receiving IMEX: ID=%d\n", msg_id);
viewtype = DC_MSG_TEXT;
dc_str_unref(text);
text = g_strndup("", 1024);
g_assert(text != NULL);
g_snprintf(text, 1024, IMEX_RECEIVED_MESSAGE, msg_id);
}
switch(viewtype) {
case DC_MSG_GIF:
case DC_MSG_IMAGE:
case DC_MSG_STICKER:
flags = flags | PURPLE_MESSAGE_IMAGES;
break;
case DC_MSG_TEXT:
flags = flags | PURPLE_MESSAGE_RAW;
break;
case DC_MSG_VIDEO: // Pidgin only supports these as files for download
case DC_MSG_FILE:
case DC_MSG_AUDIO: // Sound to play
case DC_MSG_VOICE:
break;
default:
purple_debug_info(PLUGIN_ID, "Message %d: unknown message type: %d\n", msg_id, viewtype);
}
int image_id = 0;
if ((flags & PURPLE_MESSAGE_IMAGES) > 0) {
char *filename = dc_msg_get_file(msg);
gchar *data;
gsize length;
GError *err = NULL;
g_file_get_contents(filename, &data, &length, &err);
if (err != NULL) {
purple_debug_info(PLUGIN_ID, "Failed to read image %s: %s\n", filename, err->message);
g_error_free(err);
goto out;
}
image_id = purple_imgstore_add_with_id(data, length, filename);
text = g_strdup_printf("<img id='%d'><br/>%s", image_id, text);
}
char *name = dc_contact_get_display_name(from);
int msglen = strlen(name) + 3 + strlen(text);
char *msgtext = malloc(msglen);
g_snprintf(msgtext, msglen, "%s: %s", name, text);
serv_got_im(pc, who, msgtext, flags, timestamp);
if (image_id > 0) {
purple_imgstore_unref_by_id(image_id);
}
dc_markseen_msgs(mailbox, &msg_id, 1);
g_free(who);
g_free(msgtext);
dc_str_unref(who);
dc_str_unref(name);
dc_contact_unref(contact);
out:
g_free(text);
dc_msg_unref(msg);
dc_str_unref(text);
dc_chat_unref(chat);
dc_array_unref(contacts);
}

View File

@@ -2,7 +2,7 @@
#define DELTA_CONNECTION_H
#include <glib.h>
#include <deltachat/deltachat.h>
#include <deltachat.h>
#include <pthread.h>
struct _PurpleConnection;
@@ -14,8 +14,7 @@ typedef struct _DeltaConnectionData {
// Set to 0 to convince threads to exit
int runthreads;
pthread_t imap_thread;
pthread_t smtp_thread;
pthread_t event_thread;
} DeltaConnectionData;
#define MAX_DELTA_CONFIGURE 1000

View File

@@ -72,14 +72,18 @@ pwd_opt(const char *text, const char *name, const char *def)
return option;
}
static PurpleAccountOption *
bool_opt(const char *text, const char *name, const gboolean def)
{
return purple_account_option_bool_new(text, name, def);
}
static void
delta_init_plugin(PurplePlugin *plugin)
{
PurplePluginProtocolInfo *extra = (PurplePluginProtocolInfo *)plugin->info->extra_info;
GList *opts = NULL;
debug("Starting up\n");
opts = g_list_prepend(opts, str_opt("Display Name", PLUGIN_ACCOUNT_OPT_DISPLAY_NAME, NULL));
opts = g_list_prepend(opts, str_opt("IMAP Server Host", PLUGIN_ACCOUNT_OPT_IMAP_SERVER_HOST, NULL));
@@ -100,14 +104,14 @@ delta_init_plugin(PurplePlugin *plugin)
// Not exposed: server_flags, selfstatus, e2ee_enabled
// https://deltachat.github.io/api/classmrmailbox__t.html
opts = g_list_prepend(opts, bool_opt("Send copy to self", PLUGIN_ACCOUNT_OPT_BCC_SELF, FALSE));
extra->protocol_options = g_list_reverse(opts);
}
static void
delta_destroy_plugin(PurplePlugin *plugin) {
UNUSED(plugin);
debug("Shutting down\n");
}
static gboolean
@@ -201,7 +205,11 @@ static PurplePluginProtocolInfo extra_info =
NULL, /* set_public_alias */
NULL, /* get_public_alias */
NULL, /* add_buddy_with_invite */
NULL /* add_buddies_with_invite */
NULL, /* add_buddies_with_invite */
// 2.14
NULL, /* get_cb_alias */
NULL, /* chat_can_send_file */
NULL /* some send file */
};

View File

@@ -8,7 +8,8 @@
#define DELTA_PROTOCOL_OPTS \
OPT_PROTO_UNIQUE_CHATNAME | \
OPT_PROTO_CHAT_TOPIC | \
OPT_PROTO_IM_IMAGE
OPT_PROTO_IM_IMAGE | \
OPT_PROTO_MAIL_CHECK
// These two will instead be the pidgin "username" and "password" options that
// I can't seem to get rid of.
@@ -27,4 +28,8 @@
#define PLUGIN_ACCOUNT_OPT_SMTP_USER "send_user"
#define PLUGIN_ACCOUNT_OPT_SMTP_PASS "send_pw"
#define PLUGIN_ACCOUNT_OPT_BCC_SELF "bcc_self"
#define UNUSED(x) (void)(x)
#endif

11
util.c
View File

@@ -1,11 +0,0 @@
#include <debug.h>
#include "libdelta.h"
#include "util.h"
void
debug(const char *str)
{
purple_debug_info(PLUGIN_ID, str);
}

9
util.h
View File

@@ -1,9 +0,0 @@
#ifndef UTIL_H
#define UTIL_H
#define UNUSED(x) (void)(x)
void debug(const char *str);
#endif

BIN
vendor/deltachat-core-0.41.0.tar.gz (Stored with Git LFS) vendored

Binary file not shown.

BIN
vendor/libetpan-1.8.tar.gz (Stored with Git LFS) vendored

Binary file not shown.