Switch to a non-functional Rust skeleton
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,6 +1 @@
|
|||||||
/libdelta.so
|
/target
|
||||||
/libetpan.so
|
|
||||||
/libdeltachat.so
|
|
||||||
/libnetpgp.so
|
|
||||||
/vendor/deltachat-core-0.35.0
|
|
||||||
/vendor/libetpan-1.8
|
|
||||||
|
3740
Cargo.lock
generated
Normal file
3740
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
24
Cargo.toml
Normal file
24
Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "purple-plugin-delta"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Nick Thomas <delta@ur.gs>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "delta"
|
||||||
|
path = "src/delta.rs"
|
||||||
|
crate-type = ["dylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "*"
|
||||||
|
glib-sys = "*"
|
||||||
|
purple-sys = { git = "https://github.com/lupine/libpurple-rust", branch="with-flared" }
|
||||||
|
deltachat = { git = "https://github.com/deltachat/deltachat-core-rust", tag="v1.51.0" }
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
debug = 0
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
|
||||||
|
|
27
Makefile
27
Makefile
@@ -1,27 +0,0 @@
|
|||||||
CC ?= gcc
|
|
||||||
PREFIX ?= /usr/local
|
|
||||||
|
|
||||||
PKG_CONFIG ?= pkg-config
|
|
||||||
LIB_TARGET = libdelta.so
|
|
||||||
LIB_DEST = $(DESTDIR)`$(PKG_CONFIG) --variable=plugindir purple`
|
|
||||||
|
|
||||||
$(LIB_TARGET): *.c *.h Makefile
|
|
||||||
$(CC) -C \
|
|
||||||
-Wall -Wextra -Werror \
|
|
||||||
-std=c11 \
|
|
||||||
-shared \
|
|
||||||
-fpic \
|
|
||||||
$(shell $(PKG_CONFIG) --cflags purple deltachat) \
|
|
||||||
-o $(LIB_TARGET) \
|
|
||||||
*.c \
|
|
||||||
-shared \
|
|
||||||
$(shell $(PKG_CONFIG) --libs purple deltachat) \
|
|
||||||
|
|
||||||
install:
|
|
||||||
install -D $(LIB_TARGET) $(LIB_DEST)
|
|
||||||
|
|
||||||
uninstall:
|
|
||||||
rm -f $(LIB_DEST)/$(LIB_TARGET)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm $(LIB_TARGET)
|
|
37
README.md
37
README.md
@@ -13,24 +13,27 @@ Delta has:
|
|||||||
* An electron [desktop application](https://github.com/deltachat/deltachat-desktop)
|
* An electron [desktop application](https://github.com/deltachat/deltachat-desktop)
|
||||||
|
|
||||||
This project is a [libpurple](https://developer.pidgin.im/wiki/WhatIsLibpurple)
|
This project is a [libpurple](https://developer.pidgin.im/wiki/WhatIsLibpurple)
|
||||||
plugin that wraps `deltachat-core`, allowing a number of existing desktop and
|
plugin that wraps `deltachat-core-rust`, allowing a number of existing desktop and
|
||||||
mobile clients to send and receive IMs over SMTP+IMAP. It may be useful for
|
mobile clients to send and receive IMs over SMTP+IMAP. It may be useful for
|
||||||
[Linux-based mobile devices](https://source.puri.sm/Librem5/chatty), for
|
[Linux-based mobile devices](https://source.puri.sm/Librem5/chatty), for
|
||||||
GUI desktop usage **without** an Electron dependency, or console desktop usage.
|
GUI desktop usage **without** an Electron dependency, or desktop usage.
|
||||||
|
|
||||||
Current status is probably best described as "skunkworks", although connecting
|
## Current status
|
||||||
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
|
Starting again from scratch in Rust. So currently, nothing works. TODO list:
|
||||||
further progress, I think.
|
|
||||||
|
- [~] Connect to email account
|
||||||
|
- [ ] Full settings support
|
||||||
|
- [ ] Show buddy list
|
||||||
|
- [ ] Send/receive text messages to single contact
|
||||||
|
- [ ] Send/receive text messages to group chat
|
||||||
|
- [ ] IMEX setup
|
||||||
|
- [ ] Send/receive image messages
|
||||||
|
- [ ] Send/receive audio messages
|
||||||
|
- [ ] Send/receive video messages
|
||||||
|
- [ ] Send/receive arbitrary attachments
|
||||||
|
|
||||||
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
|
## Build
|
||||||
|
|
||||||
@@ -47,17 +50,17 @@ 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
|
`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.
|
major version of OpenSSL. So, time should resolve this for us one way or another.
|
||||||
|
|
||||||
|
Significant code using the WTFPL includes the [libpurple-rust bindings](https://github.com/sbwtw/libpurple-rust)
|
||||||
|
and the [pidgin-wechat plugin](https://github.com/sbwtw/pidgin-wechat), which
|
||||||
|
I'm taking a lot of inspiration from. WTF I like happens to include building it
|
||||||
|
against this mess.
|
||||||
|
|
||||||
## Use
|
## Use
|
||||||
|
|
||||||
The easiest way to use this is to copy the `libdelta.so` file into
|
The easiest way to use this is to copy the `libdelta.so` file into
|
||||||
`~/.purple/plugins`. When running pidgin, you'll now have the option to add
|
`~/.purple/plugins`. When running pidgin, you'll now have the option to add
|
||||||
a "Delta Chat" account.
|
a "Delta Chat" account.
|
||||||
|
|
||||||
If it doesn't show up, chances are pidgin can't find the various shared
|
|
||||||
libraries the .so depends on. You can run `ldd ~/.purple/plugins/libdelta.so`
|
|
||||||
to confirm. I'll document fixing this after the build and install system is
|
|
||||||
settled.
|
|
||||||
|
|
||||||
At present, the "Username" and "Password" account fields correspond to email
|
At present, the "Username" and "Password" account fields correspond to email
|
||||||
address and password, respectively. Many important settings also show up on the
|
address and password, respectively. Many important settings also show up on the
|
||||||
"Advanced" tab - if left blank, the plugin will attempt to automatically detect
|
"Advanced" tab - if left blank, the plugin will attempt to automatically detect
|
||||||
|
@@ -1,560 +0,0 @@
|
|||||||
#include <connection.h>
|
|
||||||
#include <debug.h>
|
|
||||||
#include <eventloop.h>
|
|
||||||
#include <imgstore.h>
|
|
||||||
#include <util.h>
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "delta-connection.h"
|
|
||||||
#include "libdelta.h"
|
|
||||||
|
|
||||||
#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 delta_recv_im(DeltaConnectionData *conn, dc_msg_t *msg);
|
|
||||||
|
|
||||||
void
|
|
||||||
_transpose_config(dc_context_t *mailbox, PurpleAccount *acct)
|
|
||||||
{
|
|
||||||
const char *addr = acct->username;
|
|
||||||
const char *display = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_DISPLAY_NAME, NULL);
|
|
||||||
|
|
||||||
const char *imap_host = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_IMAP_SERVER_HOST, NULL);
|
|
||||||
const char *imap_user = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_IMAP_USER, NULL);
|
|
||||||
const char *imap_pass = purple_account_get_password(acct);
|
|
||||||
const char *imap_port = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_IMAP_SERVER_PORT, NULL);
|
|
||||||
|
|
||||||
const char *smtp_host = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_HOST, NULL);
|
|
||||||
const char *smtp_user = purple_account_get_string(acct, PLUGIN_ACCOUNT_OPT_SMTP_USER, NULL);
|
|
||||||
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);
|
|
||||||
|
|
||||||
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_SERVER_HOST, imap_host);
|
|
||||||
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_USER, imap_user);
|
|
||||||
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_PASS, imap_pass);
|
|
||||||
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_SERVER_PORT, imap_port);
|
|
||||||
|
|
||||||
dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_HOST, smtp_host);
|
|
||||||
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 {
|
|
||||||
DeltaConnectionData *conn;
|
|
||||||
|
|
||||||
// Used by delta_process_incoming_message
|
|
||||||
uint32_t msg_id;
|
|
||||||
gboolean msg_changed;
|
|
||||||
|
|
||||||
// Used by delta_process_connection_state
|
|
||||||
int connection_state;
|
|
||||||
} ProcessRequest;
|
|
||||||
|
|
||||||
gboolean
|
|
||||||
delta_process_incoming_message(void *data)
|
|
||||||
{
|
|
||||||
ProcessRequest *pr = (ProcessRequest *)data;
|
|
||||||
g_assert(pr != NULL);
|
|
||||||
g_assert(pr->conn != NULL);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
ProcessRequest *pr = (ProcessRequest *)data;
|
|
||||||
g_assert(pr != NULL);
|
|
||||||
g_assert(pr->conn != NULL);
|
|
||||||
|
|
||||||
purple_connection_update_progress(
|
|
||||||
pr->conn->pc,
|
|
||||||
"Connecting...",
|
|
||||||
pr->connection_state,
|
|
||||||
MAX_DELTA_CONFIGURE
|
|
||||||
);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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(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++) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
dc_array_unref(fresh_msgs);
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessRequest *
|
|
||||||
delta_build_process_request(DeltaConnectionData *conn)
|
|
||||||
{
|
|
||||||
g_assert(conn != NULL);
|
|
||||||
|
|
||||||
ProcessRequest *pr = g_malloc(sizeof(ProcessRequest));
|
|
||||||
g_assert(pr != NULL);
|
|
||||||
|
|
||||||
pr->conn = conn;
|
|
||||||
|
|
||||||
return pr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
void *
|
|
||||||
delta_event_handler(void *context)
|
|
||||||
{
|
|
||||||
DeltaConnectionData *conn = (DeltaConnectionData *)context;
|
|
||||||
|
|
||||||
g_assert(conn != NULL);
|
|
||||||
|
|
||||||
dc_context_t *mailbox = conn->mailbox;
|
|
||||||
dc_event_emitter_t* emitter = dc_get_event_emitter(mailbox);
|
|
||||||
dc_event_t* event;
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
purple_debug_info(PLUGIN_ID, "Event %d received from Delta.\n", event_id);
|
|
||||||
|
|
||||||
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:
|
|
||||||
case DC_EVENT_ERROR_NETWORK: {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
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
|
|
||||||
delta_connection_new(PurpleConnection *pc)
|
|
||||||
{
|
|
||||||
DeltaConnectionData *conn = NULL;
|
|
||||||
|
|
||||||
g_assert(purple_connection_get_protocol_data(pc) == NULL);
|
|
||||||
|
|
||||||
conn = g_new0(DeltaConnectionData, 1);
|
|
||||||
conn->pc = pc;
|
|
||||||
purple_connection_set_protocol_data(pc, conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
delta_connection_free(PurpleConnection *pc)
|
|
||||||
{
|
|
||||||
DeltaConnectionData *conn = purple_connection_get_protocol_data(pc);
|
|
||||||
|
|
||||||
g_assert(conn != NULL);
|
|
||||||
|
|
||||||
conn->runthreads = 0;
|
|
||||||
|
|
||||||
if (conn->mailbox != NULL) {
|
|
||||||
dc_stop_ongoing_process(conn->mailbox);
|
|
||||||
dc_stop_io(conn->mailbox);
|
|
||||||
|
|
||||||
// TODO: correctly handle join failing
|
|
||||||
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_context_unref(conn->mailbox);
|
|
||||||
}
|
|
||||||
|
|
||||||
purple_connection_set_protocol_data(pc, NULL);
|
|
||||||
|
|
||||||
// TODO: free resources as they are added to DeltaConnectionData
|
|
||||||
conn->pc = NULL;
|
|
||||||
conn->mailbox = NULL;
|
|
||||||
|
|
||||||
g_free(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
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 = NULL;
|
|
||||||
|
|
||||||
g_snprintf(
|
|
||||||
dbname, 1024, "%s%sdelta_db-%s",
|
|
||||||
purple_user_dir(), G_DIR_SEPARATOR_S, acct->username
|
|
||||||
);
|
|
||||||
|
|
||||||
mailbox = dc_context_new(PLUGIN_ID, dbname, NULL);
|
|
||||||
|
|
||||||
conn->mailbox = mailbox;
|
|
||||||
_transpose_config(mailbox, acct);
|
|
||||||
|
|
||||||
conn->runthreads = 1;
|
|
||||||
pthread_create(&conn->event_thread, NULL, delta_event_handler, conn);
|
|
||||||
|
|
||||||
dc_configure(mailbox);
|
|
||||||
dc_start_io(mailbox);
|
|
||||||
dc_maybe_network(mailbox);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
UNUSED(flags);
|
|
||||||
|
|
||||||
DeltaConnectionData *conn = (DeltaConnectionData *)purple_connection_get_protocol_data(pc);
|
|
||||||
g_assert(conn != NULL);
|
|
||||||
|
|
||||||
dc_context_t *mailbox = conn->mailbox;
|
|
||||||
g_assert(mailbox != NULL);
|
|
||||||
|
|
||||||
uint32_t contact_id = dc_create_contact(mailbox, NULL, who);
|
|
||||||
uint32_t chat_id = dc_create_chat_by_contact_id(mailbox, contact_id);
|
|
||||||
|
|
||||||
GData *attrs;
|
|
||||||
const char *msg_ptr, *start, *end;
|
|
||||||
msg_ptr = message;
|
|
||||||
|
|
||||||
// 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, dc_msg_t *msg)
|
|
||||||
{
|
|
||||||
dc_context_t *mailbox = conn->mailbox;
|
|
||||||
g_assert(mailbox != NULL);
|
|
||||||
|
|
||||||
PurpleConnection *pc = conn->pc;
|
|
||||||
g_assert(pc != NULL);
|
|
||||||
|
|
||||||
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 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);
|
|
||||||
|
|
||||||
if (chat == NULL) {
|
|
||||||
purple_debug_info(PLUGIN_ID, "Receiving IM: unknown chat: %d\n", chat_id);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_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(msgtext);
|
|
||||||
dc_str_unref(who);
|
|
||||||
dc_str_unref(name);
|
|
||||||
dc_contact_unref(contact);
|
|
||||||
out:
|
|
||||||
dc_str_unref(text);
|
|
||||||
dc_chat_unref(chat);
|
|
||||||
dc_array_unref(contacts);
|
|
||||||
}
|
|
@@ -1,30 +0,0 @@
|
|||||||
#ifndef DELTA_CONNECTION_H
|
|
||||||
#define DELTA_CONNECTION_H
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
#include <deltachat/deltachat.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
struct _PurpleConnection;
|
|
||||||
|
|
||||||
typedef struct _DeltaConnectionData {
|
|
||||||
struct _PurpleConnection *pc;
|
|
||||||
dc_context_t *mailbox;
|
|
||||||
|
|
||||||
// Set to 0 to convince threads to exit
|
|
||||||
int runthreads;
|
|
||||||
|
|
||||||
pthread_t event_thread;
|
|
||||||
} DeltaConnectionData;
|
|
||||||
|
|
||||||
#define MAX_DELTA_CONFIGURE 1000
|
|
||||||
|
|
||||||
void delta_connection_new(struct _PurpleConnection *pc);
|
|
||||||
void delta_connection_free(struct _PurpleConnection *pc);
|
|
||||||
|
|
||||||
void delta_connection_start_login(PurpleConnection *pc);
|
|
||||||
|
|
||||||
int delta_send_im(PurpleConnection *pc, const char *who, const char *message, PurpleMessageFlags flags);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
246
libdelta.c
246
libdelta.c
@@ -1,246 +0,0 @@
|
|||||||
#define PURPLE_PLUGINS
|
|
||||||
|
|
||||||
#include "libdelta.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
// All from libpurple
|
|
||||||
#include <accountopt.h>
|
|
||||||
#include <connection.h>
|
|
||||||
#include <notify.h>
|
|
||||||
#include <plugin.h>
|
|
||||||
#include <prpl.h>
|
|
||||||
#include <version.h>
|
|
||||||
|
|
||||||
#include "delta-connection.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
static GList *
|
|
||||||
delta_status_types(PurpleAccount *acct)
|
|
||||||
{
|
|
||||||
UNUSED(acct);
|
|
||||||
|
|
||||||
GList *types = NULL;
|
|
||||||
|
|
||||||
types = g_list_append(types, purple_status_type_new(PURPLE_STATUS_OFFLINE, "Offline", NULL, TRUE));
|
|
||||||
types = g_list_append(types, purple_status_type_new(PURPLE_STATUS_AVAILABLE, "Online", NULL, TRUE));
|
|
||||||
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
delta_login(PurpleAccount *acct)
|
|
||||||
{
|
|
||||||
PurpleConnection *pc = purple_account_get_connection(acct);
|
|
||||||
|
|
||||||
|
|
||||||
delta_connection_new(pc);
|
|
||||||
delta_connection_start_login(pc);
|
|
||||||
|
|
||||||
pc->flags |= PURPLE_CONNECTION_NO_BGCOLOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
delta_close(PurpleConnection *pc)
|
|
||||||
{
|
|
||||||
// TODO: actually disconnect!
|
|
||||||
purple_connection_set_state(pc, PURPLE_DISCONNECTED);
|
|
||||||
delta_connection_free(pc);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
delta_list_icon(PurpleAccount *acct, PurpleBuddy *buddy)
|
|
||||||
{
|
|
||||||
UNUSED(acct);
|
|
||||||
UNUSED(buddy);
|
|
||||||
|
|
||||||
return "delta";
|
|
||||||
}
|
|
||||||
|
|
||||||
static PurpleAccountOption *
|
|
||||||
str_opt(const char *text, const char *name, const char *def)
|
|
||||||
{
|
|
||||||
return purple_account_option_string_new(text, name, def);
|
|
||||||
}
|
|
||||||
|
|
||||||
static PurpleAccountOption *
|
|
||||||
pwd_opt(const char *text, const char *name, const char *def)
|
|
||||||
{
|
|
||||||
PurpleAccountOption* option = str_opt(text, name, def);
|
|
||||||
purple_account_option_set_masked(option, TRUE);
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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));
|
|
||||||
opts = g_list_prepend(opts, str_opt("IMAP Server Port", PLUGIN_ACCOUNT_OPT_IMAP_SERVER_PORT, NULL));
|
|
||||||
opts = g_list_prepend(opts, str_opt("IMAP Username", PLUGIN_ACCOUNT_OPT_IMAP_USER, NULL));
|
|
||||||
|
|
||||||
|
|
||||||
// These are pidgin's built-in username & password options
|
|
||||||
// FIXME: it's not super-obvious or pleasant :/
|
|
||||||
// opts = g_list_prepend(opts, str_opt("Email Address", PLUGIN_ACCOUNT_OPT_EMAIL_ADDRESS, ""));
|
|
||||||
// opts = g_list_prepend(opts, pwd_opt("IMAP Password", PLUGIN_ACCOUNT_OPT_IMAP_PASS, ""));
|
|
||||||
|
|
||||||
opts = g_list_prepend(opts, str_opt("SMTP Server Host", PLUGIN_ACCOUNT_OPT_SMTP_SERVER_HOST, NULL));
|
|
||||||
opts = g_list_prepend(opts, str_opt("SMTP Server Port", PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT, NULL));
|
|
||||||
opts = g_list_prepend(opts, str_opt("SMTP Username", PLUGIN_ACCOUNT_OPT_SMTP_USER, NULL));
|
|
||||||
opts = g_list_prepend(opts, pwd_opt("SMTP Password", PLUGIN_ACCOUNT_OPT_SMTP_PASS, NULL));
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
delta_offline_message(const PurpleBuddy *buddy)
|
|
||||||
{
|
|
||||||
UNUSED(buddy);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static PurplePluginProtocolInfo extra_info =
|
|
||||||
{
|
|
||||||
DELTA_PROTOCOL_OPTS, /* options */
|
|
||||||
NULL, /* user_splits */
|
|
||||||
NULL, /* protocol_options, initialized in delta_init_plugin() */
|
|
||||||
{ /* icon_spec, a PurpleBuddyIconSpec */
|
|
||||||
"svg,png,jpg,gif", /* format */
|
|
||||||
0, /* min_width */
|
|
||||||
0, /* min_height */
|
|
||||||
128, /* max_width */
|
|
||||||
128, /* max_height */
|
|
||||||
10000, /* max_filesize */
|
|
||||||
PURPLE_ICON_SCALE_DISPLAY, /* scale_rules */
|
|
||||||
},
|
|
||||||
delta_list_icon, /* list_icon */
|
|
||||||
NULL, /* list_emblem */
|
|
||||||
NULL, /* status_text */
|
|
||||||
NULL, /* tooltip_text */
|
|
||||||
delta_status_types, /* status_types */
|
|
||||||
NULL, /* blist_node_menu */
|
|
||||||
NULL, /* chat_info */
|
|
||||||
NULL, /* chat_info_defaults */
|
|
||||||
delta_login, /* login */
|
|
||||||
delta_close, /* close */
|
|
||||||
delta_send_im, /* send_im */
|
|
||||||
NULL, /* set_info */
|
|
||||||
NULL, /* send_typing */
|
|
||||||
NULL, /* get_info */
|
|
||||||
NULL, /* set_status */
|
|
||||||
NULL, /* set_idle */
|
|
||||||
NULL, /* change_passwd */
|
|
||||||
NULL, /* add_buddy */
|
|
||||||
NULL, /* add_buddies */
|
|
||||||
NULL, /* remove_buddy */
|
|
||||||
NULL, /* remove_buddies */
|
|
||||||
NULL, /* add_permit */
|
|
||||||
NULL, /* add_deny */
|
|
||||||
NULL, /* rem_permit */
|
|
||||||
NULL, /* rem_deny */
|
|
||||||
NULL, /* set_permit_deny */
|
|
||||||
NULL, /* join_chat */
|
|
||||||
NULL, /* reject_chat */
|
|
||||||
NULL, /* get_chat_name */
|
|
||||||
NULL, /* chat_invite */
|
|
||||||
NULL, /* chat_leave */
|
|
||||||
NULL, /* chat_whisper */
|
|
||||||
NULL, /* chat_send */
|
|
||||||
NULL, /* keepalive */
|
|
||||||
NULL, /* register_user */
|
|
||||||
NULL, /* get_cb_info */
|
|
||||||
NULL, /* get_cb_away */
|
|
||||||
NULL, /* alias_buddy */
|
|
||||||
NULL, /* group_buddy */
|
|
||||||
NULL, /* rename_group */
|
|
||||||
NULL, /* buddy_free */
|
|
||||||
NULL, /* convo_closed */
|
|
||||||
NULL, /* normalize */
|
|
||||||
NULL, /* set_buddy_icon */
|
|
||||||
NULL, /* remove_group */
|
|
||||||
NULL, /* get_cb_real_name */
|
|
||||||
NULL, /* set_chat_topic */
|
|
||||||
NULL, /* find_blist_chat */
|
|
||||||
NULL, /* roomlist_get_list */
|
|
||||||
NULL, /* roomlist_cancel */
|
|
||||||
NULL, /* roomlist_expand_category */
|
|
||||||
NULL, /* can_receive_file */
|
|
||||||
NULL, /* send_file */
|
|
||||||
NULL, /* new_xfer */
|
|
||||||
delta_offline_message, /* offline_message */
|
|
||||||
NULL, /* whiteboard_prpl_ops */
|
|
||||||
NULL, /* send_raw */
|
|
||||||
NULL, /* roomlist_room_serialize */
|
|
||||||
NULL, /* unregister_user */
|
|
||||||
NULL, /* send_attention */
|
|
||||||
NULL, /* get_attention_types */
|
|
||||||
sizeof(PurplePluginProtocolInfo), /* struct_size */
|
|
||||||
NULL, /* get_account_text_table */
|
|
||||||
NULL, /* initiate_media */
|
|
||||||
NULL, /* get_media_caps */
|
|
||||||
NULL, /* get_moods */
|
|
||||||
NULL, /* set_public_alias */
|
|
||||||
NULL, /* get_public_alias */
|
|
||||||
NULL, /* add_buddy_with_invite */
|
|
||||||
NULL /* add_buddies_with_invite */
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static PurplePluginInfo info = {
|
|
||||||
PURPLE_PLUGIN_MAGIC,
|
|
||||||
PURPLE_MAJOR_VERSION,
|
|
||||||
PURPLE_MINOR_VERSION,
|
|
||||||
PURPLE_PLUGIN_PROTOCOL,
|
|
||||||
NULL, // UI requirements
|
|
||||||
0, // flags
|
|
||||||
NULL, // dependencies
|
|
||||||
PURPLE_PRIORITY_DEFAULT,
|
|
||||||
|
|
||||||
PLUGIN_ID,
|
|
||||||
"Delta Chat",
|
|
||||||
"0.0.0",
|
|
||||||
|
|
||||||
"Delta Chat is an email-based instant messaging solution",
|
|
||||||
"See https://delta.chat for more information",
|
|
||||||
"Nick Thomas <delta@ur.gs>",
|
|
||||||
"https://delta.chat",
|
|
||||||
|
|
||||||
NULL, // plugin_load
|
|
||||||
NULL, // plugin_unload
|
|
||||||
delta_destroy_plugin, // plugin_destroy
|
|
||||||
|
|
||||||
NULL, // ui_info
|
|
||||||
&extra_info, // extra_info
|
|
||||||
NULL, // prefs_info
|
|
||||||
NULL, // actions
|
|
||||||
|
|
||||||
NULL, // reserved1
|
|
||||||
NULL, // reserved2
|
|
||||||
NULL, // reserved3
|
|
||||||
NULL // reserved4
|
|
||||||
};
|
|
||||||
|
|
||||||
PURPLE_INIT_PLUGIN(delta, delta_init_plugin, info)
|
|
35
libdelta.h
35
libdelta.h
@@ -1,35 +0,0 @@
|
|||||||
#ifndef LIBDELTA_H
|
|
||||||
#define LIBDELTA_H
|
|
||||||
|
|
||||||
#define PLUGIN_ID "prpl-delta"
|
|
||||||
|
|
||||||
#define PLUGIN_CHAT_INFO_CHAT_ID "chat_id"
|
|
||||||
|
|
||||||
#define DELTA_PROTOCOL_OPTS \
|
|
||||||
OPT_PROTO_UNIQUE_CHATNAME | \
|
|
||||||
OPT_PROTO_CHAT_TOPIC | \
|
|
||||||
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.
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_ADDR "addr"
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_IMAP_PASS "mail_pw"
|
|
||||||
|
|
||||||
// Share the remaining keys between purple and delta
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_DISPLAY_NAME "displayname"
|
|
||||||
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_IMAP_SERVER_HOST "mail_server"
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_IMAP_SERVER_PORT "mail_port"
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_IMAP_USER "mail_user"
|
|
||||||
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_SMTP_SERVER_HOST "send_server"
|
|
||||||
#define PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT "send_port"
|
|
||||||
#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
|
|
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
@@ -0,0 +1 @@
|
|||||||
|
1.50.0
|
188
src/delta.rs
Normal file
188
src/delta.rs
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
extern crate purple_sys;
|
||||||
|
extern crate glib_sys;
|
||||||
|
extern crate libc;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
/*
|
||||||
|
mod pointer;
|
||||||
|
mod server;
|
||||||
|
mod user;
|
||||||
|
mod chatroom;
|
||||||
|
mod message;
|
||||||
|
*/
|
||||||
|
use std::os::raw::{c_void, c_char};
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
use std::boxed::Box;
|
||||||
|
use std::ffi::CString; // CStr
|
||||||
|
//use std::sync::RwLock;
|
||||||
|
|
||||||
|
use purple_sys::*;
|
||||||
|
use purple_sys::PurpleType;
|
||||||
|
//use purple_sys::PurpleConnectionState;
|
||||||
|
use purple_sys::PurpleStatusPrimitive;
|
||||||
|
//use purple_sys::PurpleRoomlistFieldType;
|
||||||
|
//use message::*;
|
||||||
|
//use pointer::Pointer;
|
||||||
|
//use server::ACCOUNT;
|
||||||
|
//use server::{send_im, send_chat, find_blist_chat, find_chat_token};
|
||||||
|
|
||||||
|
use glib_sys::{GHashTable, GList};
|
||||||
|
|
||||||
|
const TRUE: i32 = 1;
|
||||||
|
const FALSE: i32 = 0;
|
||||||
|
|
||||||
|
lazy_static!{
|
||||||
|
// static ref PLUGIN: RwLock<Pointer> = RwLock::new(Pointer::new());
|
||||||
|
static ref ICON_FILE: CString = CString::new("delta").unwrap();
|
||||||
|
// static ref DELTA_CATEGORY: CString = CString::new("Delta Chat").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_item(list: *mut GList, item: *mut c_void) -> *mut GList {
|
||||||
|
unsafe {
|
||||||
|
glib_sys::g_list_append(list as *mut glib_sys::GList, item as *mut libc::c_void) as
|
||||||
|
*mut GList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn list_icon(_: *mut PurpleAccount, _: *mut PurpleBuddy) -> *const c_char {
|
||||||
|
ICON_FILE.as_ptr()
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn status_types(_: *mut PurpleAccount) -> *mut GList {
|
||||||
|
|
||||||
|
let mut list: *mut GList = null_mut();
|
||||||
|
|
||||||
|
let available = CString::new("available").unwrap();
|
||||||
|
let available_name = CString::new("Available").unwrap();
|
||||||
|
let offline = CString::new("offline").unwrap();
|
||||||
|
let offline_name = CString::new("Offline").unwrap();
|
||||||
|
let nick = CString::new("nick").unwrap();
|
||||||
|
|
||||||
|
let status = unsafe {
|
||||||
|
purple_status_type_new_with_attrs(
|
||||||
|
PurpleStatusPrimitive::PURPLE_STATUS_AVAILABLE,
|
||||||
|
available.as_ptr(),
|
||||||
|
available_name.as_ptr(),
|
||||||
|
TRUE,
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
nick.as_ptr(),
|
||||||
|
nick.as_ptr(),
|
||||||
|
purple_value_new(PurpleType::PURPLE_TYPE_STRING),
|
||||||
|
null_mut() as *mut c_void,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
list = append_item(list, status as *mut c_void);
|
||||||
|
|
||||||
|
let status = unsafe {
|
||||||
|
purple_status_type_new_with_attrs(
|
||||||
|
PurpleStatusPrimitive::PURPLE_STATUS_OFFLINE,
|
||||||
|
offline.as_ptr(),
|
||||||
|
offline_name.as_ptr(),
|
||||||
|
TRUE,
|
||||||
|
TRUE,
|
||||||
|
FALSE,
|
||||||
|
nick.as_ptr(),
|
||||||
|
nick.as_ptr(),
|
||||||
|
purple_value_new(PurpleType::PURPLE_TYPE_STRING),
|
||||||
|
null_mut() as *mut c_void,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
list = append_item(list, status as *mut c_void);
|
||||||
|
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn login(account: *mut PurpleAccount) {
|
||||||
|
println!("account: {:?}", account);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn chat_info(_: *mut PurpleConnection) -> *mut GList {
|
||||||
|
|
||||||
|
let list: *mut GList = null_mut();
|
||||||
|
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn join_chat(gc: *mut PurpleConnection, components: *mut GHashTable) {
|
||||||
|
println!("join_chat: {:?}, {:?}", gc, components);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn chat_info_defaults(_: *mut PurpleConnection, _: *const c_char) -> *mut GHashTable {
|
||||||
|
|
||||||
|
let table: *mut GHashTable = null_mut();
|
||||||
|
|
||||||
|
table
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn close(_: *mut PurpleConnection) {}
|
||||||
|
|
||||||
|
extern "C" fn buddy_list(gc: *mut PurpleConnection) -> *mut PurpleRoomlist {
|
||||||
|
let buddies = unsafe { purple_roomlist_new(purple_connection_get_account(gc)) };
|
||||||
|
|
||||||
|
buddies
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn callback(_plugin: *mut PurplePlugin) -> i32 {
|
||||||
|
TRUE
|
||||||
|
}
|
||||||
|
|
||||||
|
// extern "C" fn action_cb(_: *mut PurplePluginAction) {
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
extern "C" fn actions(_: *mut PurplePlugin, _: *mut c_void) -> *mut GList {
|
||||||
|
|
||||||
|
let list: *mut GList = null_mut();
|
||||||
|
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn purple_init_plugin(plugin: *mut PurplePlugin) -> i32 {
|
||||||
|
// save plugin pointer
|
||||||
|
// PLUGIN.write().unwrap().set(plugin as *mut c_void);
|
||||||
|
|
||||||
|
let id = CString::new("prpl-delta").unwrap();
|
||||||
|
let name = CString::new("Delta Chat").unwrap();
|
||||||
|
let version = CString::new("0.1.0").unwrap();
|
||||||
|
let summary = CString::new("Delta Chat is an email-based instant messaging solution").unwrap();
|
||||||
|
let description = CString::new("See https://delta.chat for more information").unwrap();
|
||||||
|
let author = CString::new("Nick Thomas <delta@ur.gs>").unwrap();
|
||||||
|
let home_page = CString::new("https://delta.chat").unwrap();
|
||||||
|
|
||||||
|
let mut info = Box::new(PurplePluginInfo::new());
|
||||||
|
let mut extra_info = Box::new(PurplePluginProtocolInfo::new());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
|
||||||
|
extra_info.list_icon = Some(list_icon);
|
||||||
|
extra_info.status_types = Some(status_types);
|
||||||
|
extra_info.login = Some(login);
|
||||||
|
extra_info.close = Some(close);
|
||||||
|
extra_info.roomlist_get_list = Some(buddy_list);
|
||||||
|
extra_info.chat_info = Some(chat_info);
|
||||||
|
extra_info.chat_info_defaults = Some(chat_info_defaults);
|
||||||
|
//extra_info.chat_send = Some(send_chat);
|
||||||
|
extra_info.join_chat = Some(join_chat);
|
||||||
|
//extra_info.find_blist_chat = Some(find_blist_chat);
|
||||||
|
//extra_info.send_im = Some(send_im);
|
||||||
|
|
||||||
|
info.id = id.into_raw();
|
||||||
|
info.name = name.into_raw();
|
||||||
|
info.version = version.into_raw();
|
||||||
|
info.summary = summary.into_raw();
|
||||||
|
info.description = description.into_raw();
|
||||||
|
info.author = author.into_raw();
|
||||||
|
info.homepage = home_page.into_raw();
|
||||||
|
info.load = Some(callback);
|
||||||
|
info.actions = Some(actions);
|
||||||
|
info.extra_info = Box::into_raw(extra_info) as *mut c_void;
|
||||||
|
|
||||||
|
(*plugin).info = Box::into_raw(info);
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { purple_plugin_register(plugin) }
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user