Compare commits
14 Commits
cb3872bea8
...
main
Author | SHA1 | Date | |
---|---|---|---|
47cc4c1772 | |||
3e146313a9 | |||
18ae21daad | |||
5f9c60ce4b | |||
cff109abb5 | |||
6d4454f356 | |||
36b6b37e78 | |||
4484f51c5d | |||
83488c31fe | |||
6da5908686 | |||
25af2f6b49
|
|||
62a59b7b0a | |||
ed3c092f13
|
|||
5f01cdf21c
|
@@ -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
|
||||
|
33
README.md
33
README.md
@@ -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
|
||||
|
||||
|
@@ -1,48 +1,20 @@
|
||||
#include <connection.h>
|
||||
#include <debug.h>
|
||||
#include <eventloop.h>
|
||||
#include <imgstore.h>
|
||||
#include <util.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <inttypes.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "delta-connection.h"
|
||||
#include "libdelta.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)
|
||||
@@ -60,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);
|
||||
|
||||
@@ -72,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 {
|
||||
@@ -79,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
|
||||
@@ -96,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)
|
||||
{
|
||||
@@ -118,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);
|
||||
|
||||
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) {
|
||||
purple_debug_info("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;
|
||||
}
|
||||
@@ -215,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;
|
||||
|
||||
purple_debug_info(PLUGIN_ID, "Event %d received from Delta. Args: %" PRIuPTR ", %" PRIuPTR "\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:
|
||||
purple_debug_info(PLUGIN_ID, "INFO from Delta: %s\n", (char *)data2);
|
||||
break;
|
||||
case DC_EVENT_WARNING:
|
||||
purple_debug_info(PLUGIN_ID, "WARNING from Delta: %s\n", (char *)data2);
|
||||
break;
|
||||
case DC_EVENT_ERROR:
|
||||
case DC_EVENT_ERROR_NETWORK:
|
||||
purple_debug_info(PLUGIN_ID, "ERROR from Delta: %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:
|
||||
purple_debug_info(PLUGIN_ID, "Event %d is TODO\n", event);
|
||||
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:
|
||||
purple_debug_info(PLUGIN_ID, "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) ) {
|
||||
purple_debug_info(PLUGIN_ID, "Telling Delta we are offline\n");
|
||||
out = 1;
|
||||
} else {
|
||||
purple_debug_info(PLUGIN_ID, "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:
|
||||
purple_debug_info(PLUGIN_ID, "Unknown Delta 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
|
||||
@@ -334,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) {
|
||||
purple_debug_info(PLUGIN_ID, "joining imap thread failed\n");
|
||||
}
|
||||
if (pthread_join(conn->smtp_thread, NULL) != 0) {
|
||||
purple_debug_info(PLUGIN_ID, "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);
|
||||
}
|
||||
|
||||
@@ -364,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)) {
|
||||
purple_debug_info(PLUGIN_ID, "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)
|
||||
{
|
||||
@@ -401,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);
|
||||
@@ -419,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) {
|
||||
purple_debug_info(PLUGIN_ID, "Receiving IM: unknown contact: %d\n", contact_id);
|
||||
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);
|
||||
}
|
||||
|
@@ -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
|
||||
|
14
libdelta.c
14
libdelta.c
@@ -72,6 +72,12 @@ 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)
|
||||
{
|
||||
@@ -98,6 +104,8 @@ 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);
|
||||
}
|
||||
|
||||
@@ -197,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 */
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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,6 +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
|
||||
|
BIN
vendor/deltachat-core-0.41.0.tar.gz
(Stored with Git LFS)
vendored
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
BIN
vendor/libetpan-1.8.tar.gz
(Stored with Git LFS)
vendored
Binary file not shown.
Reference in New Issue
Block a user