From de213f2b7afd8eb8bc5c0d4f4d00dec751f73ee8 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Wed, 2 May 2018 02:09:59 +0100 Subject: [PATCH] Initial integration with libdeltachat This implements configuration, connection, sending and receiving IMs in the simplest possible way. Lots and lots of cases are not handled. --- Makefile | 7 +- delta-connection.c | 229 +++++++++++++++++++++++++++++++++++++++++++-- delta-connection.h | 8 ++ libdelta.c | 50 +++++----- libdelta.h | 10 +- util.c | 11 +++ util.h | 9 ++ 7 files changed, 285 insertions(+), 39 deletions(-) create mode 100644 util.c create mode 100644 util.h diff --git a/Makefile b/Makefile index 49211f3..e101c80 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,18 @@ CC ?= gcc PREFIX ?= /usr/local -libdelta.so: *.c *.h +libdelta.so: *.c *.h Makefile $(CC) -C \ -Wall -Wextra -Werror \ -std=c11 \ -shared \ -fpic \ - $(shell pkg-config --cflags purple) \ + $(shell pkg-config --cflags purple libsoup-2.4) \ -o libdelta.so \ *.c \ -shared \ - $(shell pkg-config --libs purple) + $(shell pkg-config --libs purple libsoup-2.4) \ + -ldeltachat clean: rm libdelta.so diff --git a/delta-connection.c b/delta-connection.c index 605b1fe..15e5e5c 100644 --- a/delta-connection.c +++ b/delta-connection.c @@ -1,21 +1,137 @@ #include +#include + +#include +#include #include "delta-connection.h" +#include "libdelta.h" +#include "util.h" -void delta_connection_new(PurpleConnection *pc) +void delta_recv_im(DeltaConnectionData *conn, uint32_t chat_id, uint32_t msg_id); + +void +_transpose_config(mrmailbox_t *mailbox, PurpleAccount *acct) { - DeltaConnectionData *conn; + 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); + int imap_port = purple_account_get_int(acct, PLUGIN_ACCOUNT_OPT_IMAP_SERVER_PORT, DEFAULT_IMAP_PORT); + + 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); + int smtp_port = purple_account_get_int(acct, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT, DEFAULT_SMTP_PORT); + + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_ADDR, addr); + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_DISPLAY_NAME, display); + + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_SERVER_HOST, imap_host); + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_USER, imap_user); + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_PASS, imap_pass); + mrmailbox_set_config_int(mailbox, PLUGIN_ACCOUNT_OPT_IMAP_SERVER_PORT, imap_port); + + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_HOST, smtp_host); + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_USER, smtp_user); + mrmailbox_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_PASS, smtp_pass); + mrmailbox_set_config_int(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT, smtp_port); +} + +uintptr_t +_http_get(const char *url) +{ + // FIXME: we could keep a soup session around for more than a single request + uintptr_t out = 0; + guint status; + SoupSession *session = soup_session_new(); + SoupMessage *msg = soup_message_new("GET", url); + + status = soup_session_send_message(session, msg); + + if (status >= 200 && status < 300) { + out = (uintptr_t)msg->response_body->data; + } + +// g_free(msg); // FIXME: huge memory leak +// g_free(session); + + return out; +} + +uintptr_t +my_delta_handler(mrmailbox_t* mailbox, int event, uintptr_t data1, uintptr_t data2) +{ + DeltaConnectionData *conn = (DeltaConnectionData *)mrmailbox_get_userdata(mailbox); + g_assert(conn != NULL); + + uintptr_t out = 0; + + switch (event) { + case MR_EVENT_INFO: + printf("INFO: %s\n", (char *)data2); + break; + case MR_EVENT_WARNING: + printf("WARNING: %s\n", (char *)data2); + break; + case MR_EVENT_ERROR: + printf("ERROR: %d: %s\n", (int)data1, (char *)data2); + break; + + case MR_EVENT_MSGS_CHANGED: + debug("TODO: received MR_EVENT_MSGS_CHANGED"); + break; + + case MR_EVENT_INCOMING_MSG: + delta_recv_im(conn, (uint32_t)data1, (uint32_t)data2); + break; + + // These are all to do with sending & receiving messages. The real meat of + // the event loop + case MR_EVENT_MSG_DELIVERED: + case MR_EVENT_MSG_READ: + case MR_EVENT_CHAT_MODIFIED: + case MR_EVENT_CONTACTS_CHANGED: + debug("TODO!\n"); + break; + + case MR_EVENT_CONFIGURE_PROGRESS: + purple_connection_update_progress(conn->pc, "Connecting...", (int)data1, MAX_DELTA_CONFIGURE); + break; + case MR_EVENT_HTTP_GET: + printf("HTTP GET requested: %s\n", (char *)data1); + out = _http_get((char *)data1); + break; + case MR_EVENT_IS_OFFLINE: + debug("TODO: MR_EVENT_IS_OFFLINE handling. Returning online for now\n"); + break; + case MR_EVENT_GET_STRING: + case MR_EVENT_GET_QUANTITY_STRING: + case MR_EVENT_WAKE_LOCK: + break; + default: + printf("Unknown event: %d\n", event); + } + + return out; +} + +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) +void +delta_connection_free(PurpleConnection *pc) { DeltaConnectionData *conn = purple_connection_get_protocol_data(pc); @@ -23,9 +139,110 @@ void delta_connection_free(PurpleConnection *pc) purple_connection_set_protocol_data(pc, NULL); - // TODO: free resources as they are added to DeltaConnectionData + if (conn->mailbox != NULL) { + mrmailbox_stop_ongoing_process(conn->mailbox); + mrmailbox_disconnect(conn->mailbox); + mrmailbox_close(conn->mailbox); + mrmailbox_unref(conn->mailbox); + } + // 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); + mrmailbox_t *mailbox = mrmailbox_new(my_delta_handler, conn, NULL); + + g_snprintf( + dbname, 1024, "%s%sdelta_db-%s", + purple_user_dir(), G_DIR_SEPARATOR_S, acct->username + ); + + + if (!mrmailbox_open(mailbox, dbname, NULL)) { + debug("mrmailbox_open returned false...?"); + } + + conn->mailbox = mailbox; + + _transpose_config(mailbox, acct); + + purple_connection_set_state(pc, PURPLE_CONNECTING); + purple_connection_update_progress(pc, "Connecting...", 1, MAX_DELTA_CONFIGURE); + + if (mrmailbox_is_configured(mailbox)) { + mrmailbox_connect(mailbox); + } else if (!mrmailbox_configure_and_connect(mailbox)) { + char *info = mrmailbox_get_info(mailbox); + debug(info); + g_free(info); + + purple_connection_error(pc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR); + purple_connection_set_state(pc, PURPLE_DISCONNECTED); + return; + } + + purple_connection_set_state(pc, PURPLE_CONNECTED); + return; +} + +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); + + mrmailbox_t *mailbox = conn->mailbox; + g_assert(mailbox != NULL); + + uint32_t contact_id = mrmailbox_create_contact(mailbox, NULL, who); + uint32_t chat_id = mrmailbox_create_chat_by_contact_id(mailbox, contact_id); + + mrmailbox_send_text_msg(mailbox, chat_id, message); + return 1; // success; echo the message to the chat window +} + +void +delta_recv_im(DeltaConnectionData *conn, uint32_t chat_id, uint32_t msg_id) +{ + mrmailbox_t *mailbox = conn->mailbox; + g_assert(mailbox != NULL); + + PurpleConnection *pc = conn->pc; + g_assert(pc != NULL); + + mrmsg_t* msg = mrmailbox_get_msg(mailbox, msg_id); + + time_t timestamp = mrmsg_get_timestamp(msg); + char *text = mrmsg_get_text(msg); + uint32_t contact_id = mrmsg_get_from_id(msg); + + mrcontact_t *contact = mrmailbox_get_contact(mailbox, contact_id); + if (contact == NULL) { + debug("Unknown contact! FIXME!"); + goto out; + } + + char *who = mrcontact_get_addr(contact); + + // TODO: send this to the IM window instead + printf("who: %s, text: %s\n", who, text); + printf("message %d.%d: %s\n", chat_id, msg_id, text); + serv_got_im(pc, who, text, PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_RAW, timestamp); + + mrmailbox_markseen_msgs(mailbox, &msg_id, 1); + g_free(who); +out: + g_free(text); + mrmsg_unref(msg); +} diff --git a/delta-connection.h b/delta-connection.h index b5f2d5b..a2e79bb 100644 --- a/delta-connection.h +++ b/delta-connection.h @@ -4,13 +4,21 @@ #include struct _PurpleConnection; +struct _mrmailbox; typedef struct _DeltaConnectionData { struct _PurpleConnection *pc; + struct _mrmailbox *mailbox; } DeltaConnectionData; +#define MAX_DELTA_CONFIGURE 901 + 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 diff --git a/libdelta.c b/libdelta.c index fff4ef7..15d7548 100644 --- a/libdelta.c +++ b/libdelta.c @@ -7,21 +7,13 @@ // All from libpurple #include #include -#include #include #include #include #include #include "delta-connection.h" - -#define UNUSED(x) (void)(x) - -static void -debug(const char *str) -{ - purple_debug_info(PLUGIN_ID, str); -} +#include "util.h" static GList * delta_status_types(PurpleAccount *acct) @@ -41,10 +33,9 @@ delta_login(PurpleAccount *acct) { PurpleConnection *pc = purple_account_get_connection(acct); - delta_connection_new(pc); - purple_connection_set_state(pc, PURPLE_CONNECTING); - // TODO: attempt to connect! + delta_connection_new(pc); + delta_connection_start_login(pc); pc->flags |= PURPLE_CONNECTION_HTML; } @@ -57,8 +48,6 @@ delta_close(PurpleConnection *pc) delta_connection_free(pc); } -// Below the fold is libpurple plumbing. No, I don't understand it either - static const char * delta_list_icon(PurpleAccount *acct, PurpleBuddy *buddy) { @@ -97,22 +86,22 @@ delta_init_plugin(PurplePlugin *plugin) 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("Display Name", PLUGIN_ACCOUNT_OPT_DISPLAY_NAME, "")); - - opts = g_list_prepend(opts, str_opt("IMAP Server Host", PLUGIN_ACCOUNT_OPT_IMAP_SERVER_HOST, "")); + opts = g_list_prepend(opts, str_opt("IMAP Server Host", PLUGIN_ACCOUNT_OPT_IMAP_SERVER_HOST, NULL)); opts = g_list_prepend(opts, int_opt("IMAP Server Port", PLUGIN_ACCOUNT_OPT_IMAP_SERVER_PORT, DEFAULT_IMAP_PORT)); - opts = g_list_prepend(opts, str_opt("IMAP Username", PLUGIN_ACCOUNT_OPT_IMAP_USER, "")); + 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, "")); + opts = g_list_prepend(opts, str_opt("SMTP Server Host", PLUGIN_ACCOUNT_OPT_SMTP_SERVER_HOST, NULL)); opts = g_list_prepend(opts, int_opt("SMTP Server Port", PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT, DEFAULT_SMTP_PORT)); - opts = g_list_prepend(opts, str_opt("SMTP Username", PLUGIN_ACCOUNT_OPT_SMTP_USER, "")); - opts = g_list_prepend(opts, pwd_opt("SMTP Password", PLUGIN_ACCOUNT_OPT_SMTP_PASS, "")); + 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 @@ -120,12 +109,21 @@ delta_init_plugin(PurplePlugin *plugin) extra->protocol_options = g_list_reverse(opts); } -static void delta_destroy_plugin(PurplePlugin *plugin) { +static void +delta_destroy_plugin(PurplePlugin *plugin) { UNUSED(plugin); debug("Shutting down\n"); } +static gboolean +delta_offline_message(const PurpleBuddy *buddy) +{ + UNUSED(buddy); + + return TRUE; +} + static PurplePluginProtocolInfo extra_info = { DELTA_PROTOCOL_OPTS, /* options */ @@ -146,11 +144,11 @@ static PurplePluginProtocolInfo extra_info = NULL, /* tooltip_text */ delta_status_types, /* status_types */ NULL, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ + NULL, /* chat_info */ + NULL, /* chat_info_defaults */ delta_login, /* login */ delta_close, /* close */ - NULL, /* send_im */ + delta_send_im, /* send_im */ NULL, /* set_info */ NULL, /* send_typing */ NULL, /* get_info */ @@ -194,7 +192,7 @@ static PurplePluginProtocolInfo extra_info = NULL, /* can_receive_file */ NULL, /* send_file */ NULL, /* new_xfer */ - NULL, /* offline_message */ + delta_offline_message, /* offline_message */ NULL, /* whiteboard_prpl_ops */ NULL, /* send_raw */ NULL, /* roomlist_room_serialize */ diff --git a/libdelta.h b/libdelta.h index 09a63b1..44499da 100644 --- a/libdelta.h +++ b/libdelta.h @@ -3,19 +3,21 @@ #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 -#define DEFAULT_SMTP_PORT 25 -#define DEFAULT_IMAP_PORT 143 +#define DEFAULT_SMTP_PORT 0 +#define DEFAULT_IMAP_PORT 0 // 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" +#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" diff --git a/util.c b/util.c new file mode 100644 index 0000000..d4f6aee --- /dev/null +++ b/util.c @@ -0,0 +1,11 @@ +#include + +#include "libdelta.h" +#include "util.h" + +void +debug(const char *str) +{ + purple_debug_info(PLUGIN_ID, str); +} + diff --git a/util.h b/util.h new file mode 100644 index 0000000..82571e5 --- /dev/null +++ b/util.h @@ -0,0 +1,9 @@ +#ifndef UTIL_H +#define UTIL_H + +#define UNUSED(x) (void)(x) + +void debug(const char *str); + +#endif +