From 5d2fc0dbcaf8e87adaca53ece89a7feab609cae0 Mon Sep 17 00:00:00 2001 From: Nick Thomas Date: Thu, 11 Apr 2019 23:57:11 +0100 Subject: [PATCH] Use native libpurple HTTP functions --- .gitlab-ci.yml | 2 +- Makefile | 4 +- README.md | 2 +- delta-connection.c | 189 +++++++++++++++++++++++---------------------- 4 files changed, 102 insertions(+), 95 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e2596fa..39da3c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,7 @@ Debian 9: - 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 libcurl4-openssl-dev libglib2.0-dev + - apt install --no-install-recommends -t stretch-backports -yy libpurple-dev libglib2.0-dev - make artifacts: paths: diff --git a/Makefile b/Makefile index 0c3b108..81bf13e 100644 --- a/Makefile +++ b/Makefile @@ -11,11 +11,11 @@ $(LIB_TARGET): *.c *.h Makefile -std=c11 \ -shared \ -fpic \ - $(shell $(PKG_CONFIG) --cflags purple libcurl deltachat) \ + $(shell $(PKG_CONFIG) --cflags purple deltachat) \ -o $(LIB_TARGET) \ *.c \ -shared \ - $(shell $(PKG_CONFIG) --libs purple libcurl deltachat) \ + $(shell $(PKG_CONFIG) --libs purple deltachat) \ install: install -D $(LIB_TARGET) $(LIB_DEST) diff --git a/README.md b/README.md index 176a2a0..9d4e55d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ you'll need to build and install it according to Now, you'll need some other build dependencies: ``` -sudo apt install build-essential libpurple-dev libcurl4-openssl-dev libglib2.0-dev +sudo apt install build-essential libpurple-dev libglib2.0-dev ``` Finally, run `make` to create a `libdelta.so` file. diff --git a/delta-connection.c b/delta-connection.c index 3433e3b..e7ceae6 100644 --- a/delta-connection.c +++ b/delta-connection.c @@ -5,9 +5,6 @@ #include #include -#include -#include - #include "delta-connection.h" #include "libdelta.h" #include "util.h" @@ -76,78 +73,6 @@ _transpose_config(dc_context_t *mailbox, PurpleAccount *acct) dc_set_config(mailbox, PLUGIN_ACCOUNT_OPT_SMTP_SERVER_PORT, smtp_port); } -// This and WriteMemoryCallback are "borrowed" from https://curl.haxx.se/libcurl/c/getinmemory.html -struct MemoryStruct { - char *memory; - size_t size; -}; - -static size_t -WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) -{ - size_t realsize = size * nmemb; - struct MemoryStruct *mem = (struct MemoryStruct *)userp; - - mem->memory = realloc(mem->memory, mem->size + realsize + 1); - if(mem->memory == NULL) { - printf("not enough memory (realloc returned NULL)\n"); - return 0; - } - - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - - return realsize; -} - -uintptr_t -_http_get(const char *url) -{ - long status = 0; - uintptr_t out = 0; - CURL *curl = curl_easy_init(); - CURLcode res; - - if (curl == NULL) { - return 0; - } - - struct MemoryStruct chunk; - chunk.memory = malloc(1); /* will be grown as needed by the realloc above */ - chunk.size = 0; /* no data at this point */ - - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk); - - res = curl_easy_perform(curl); - if (res != CURLE_OK) { - printf("Failed to GET %s: %s\n", url, curl_easy_strerror(res)); - goto err; - } - - res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); - if (res != CURLE_OK) { - printf("Failed to read response code for %s: %s\n", url, curl_easy_strerror(res)); - goto err; - } - - if (status < 200 || status > 299) { - printf("Non-success HTTP response code for %s: %lu\n", url, status); - goto err; - } - - out = (uintptr_t)chunk.memory; - -err: - curl_easy_cleanup(curl); - - // Don't free chunk.memory - that will be done by deltachat-core - - return out; -} - typedef struct { DeltaConnectionData *conn; @@ -156,6 +81,11 @@ typedef struct { // 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; } ProcessRequest; gboolean @@ -218,8 +148,71 @@ delta_process_fresh_messages(void *data) return FALSE; } -// Do not call any libpurple functions in here, as it is not thread-safe and -// events may be dispatched from any delta thread. Use +void +delta_process_http_get_cb( + PurpleUtilFetchUrlData *url_data, + gpointer user_data, + const gchar *url_text, + gsize len, + const gchar *error_message +) +{ + UNUSED(url_data); + + ProcessRequest *pr = (ProcessRequest *)user_data; + g_assert(pr != NULL); + g_assert(pr->http_wait != NULL); + + if (len == 0) { + printf("Failed to GET %s: %s\n", pr->http_url, error_message); + pr->http_response = NULL; + goto out; + } + + pr->http_response = g_malloc(len); + g_assert(pr->http_response != NULL); + + strncpy(pr->http_response, url_text, len); + +out: + pthread_cond_broadcast(pr->http_wait); + return; +} + +gboolean +delta_process_http_get(void *data) +{ + ProcessRequest *pr = (ProcessRequest *)data; + g_assert(pr != NULL); + g_assert(pr->http_url != NULL); + + purple_util_fetch_url( + pr->http_url, + TRUE, + NULL, + TRUE, + delta_process_http_get_cb, + data + ); + + 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 uintptr_t my_delta_handler(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t data2) @@ -227,8 +220,10 @@ my_delta_handler(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t da DeltaConnectionData *conn = (DeltaConnectionData *)dc_get_userdata(mailbox); g_assert(conn != NULL); - ProcessRequest *pr; + ProcessRequest *pr = NULL; uintptr_t out = 0; + pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_cond_t cond = PTHREAD_COND_INITIALIZER; printf("my_delta_handler(mailbox, %d, %lu, %lu)\n", event, data1, data2); @@ -248,20 +243,14 @@ my_delta_handler(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t da break; case DC_EVENT_MSGS_CHANGED: - pr = g_malloc(sizeof(ProcessRequest)); - g_assert(pr != NULL); - - pr->conn = conn; + 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 = g_malloc(sizeof(ProcessRequest)); - g_assert(pr != NULL); - - pr->conn = conn; + pr = delta_build_process_request(conn); pr->msg_id = (uint32_t)data2; purple_timeout_add(0, delta_process_incoming_message, pr); break; @@ -276,16 +265,34 @@ my_delta_handler(dc_context_t* mailbox, int event, uintptr_t data1, uintptr_t da break; case DC_EVENT_CONFIGURE_PROGRESS: - pr = g_malloc(sizeof(ProcessRequest)); - g_assert(pr != NULL); - - pr->conn = conn; + pr = delta_build_process_request(conn); pr->connection_state = (int)data1; purple_timeout_add(0, delta_process_connection_state, pr); break; + case DC_EVENT_HTTP_GET: printf("HTTP GET requested: %s\n", (char *)data1); - out = _http_get((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); + free(pr); + break; case DC_EVENT_IS_OFFLINE: if ( conn->pc == NULL || !PURPLE_CONNECTION_IS_CONNECTED(conn->pc) ) {