From 63f48e9d479ad7fd0471174ab84f4b694b8f63bd Mon Sep 17 00:00:00 2001 From: Ayush Date: Sun, 13 Jun 2021 16:55:21 +0530 Subject: [PATCH] Updating to the latest hg --- .gitignore | 1 + pidgin/libpurple/glibcompat.h | 27 ++ pidgin/libpurple/http.c | 305 +++++---------- .../libpurple/protocols/facebook/Makefile.am | 30 +- .../protocols/facebook/Makefile.mingw | 20 +- pidgin/libpurple/protocols/facebook/api.c | 80 ++-- pidgin/libpurple/protocols/facebook/api.h | 4 +- pidgin/libpurple/protocols/facebook/data.c | 4 +- .../libpurple/protocols/facebook/facebook.c | 250 ++++++------- .../libpurple/protocols/facebook/facebook.h | 50 --- pidgin/libpurple/protocols/facebook/http.c | 2 +- pidgin/libpurple/protocols/facebook/json.c | 3 +- pidgin/libpurple/protocols/facebook/mqtt.c | 346 ++++++++---------- pidgin/libpurple/protocols/facebook/mqtt.h | 17 + pidgin/libpurple/protocols/facebook/thrift.c | 3 +- 15 files changed, 515 insertions(+), 627 deletions(-) diff --git a/.gitignore b/.gitignore index a632a291..db0c607c 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ Makefile Makefile.in pidgin win32-install-dir +.idea \ No newline at end of file diff --git a/pidgin/libpurple/glibcompat.h b/pidgin/libpurple/glibcompat.h index ccc303d4..03826511 100644 --- a/pidgin/libpurple/glibcompat.h +++ b/pidgin/libpurple/glibcompat.h @@ -71,6 +71,30 @@ purple_g_stat(const gchar *filename, GStatBufW32 *buf) /****************************************************************************** * g_assert_* macros *****************************************************************************/ + +#if !GLIB_CHECK_VERSION(2, 32, 0) +static inline GByteArray * g_byte_array_new_take(guint8 *data, gsize len) +{ + GByteArray *array; + + array = g_byte_array_new(); + g_byte_array_append(array, data, len); + g_free(data); + + return array; +} + +static inline void g_queue_free_full(GQueue *queue, GDestroyNotify free_func) +{ + g_queue_foreach(queue, (GFunc)free_func, NULL); + g_queue_free(queue); +} +#endif + +#if !GLIB_CHECK_VERSION(2, 30, 0) +#define G_VALUE_INIT {0, {{0}}} +#endif + #if !GLIB_CHECK_VERSION(2, 38, 0) #define g_assert_true(expr) G_STMT_START { \ if G_LIKELY (expr) ; else \ @@ -86,6 +110,9 @@ purple_g_stat(const gchar *filename, GStatBufW32 *buf) g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ "'" #expr "' should be NULL"); \ } G_STMT_END +#define G_ADD_PRIVATE(TypeName) G_STMT_START { } G_STMT_END +#else +#define g_type_class_add_private(k,s) G_STMT_START { } G_STMT_END #endif #if !GLIB_CHECK_VERSION(2, 40, 0) diff --git a/pidgin/libpurple/http.c b/pidgin/libpurple/http.c index fd882947..75e390ab 100644 --- a/pidgin/libpurple/http.c +++ b/pidgin/libpurple/http.c @@ -27,7 +27,12 @@ #include "debug.h" #include "proxy.h" -#include "purple-gio.h" +#include "purple-socket.h" + +#include +#ifndef z_const +#define z_const +#endif #define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-" #define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 10240 @@ -51,15 +56,9 @@ typedef struct _PurpleHttpKeepaliveRequest PurpleHttpKeepaliveRequest; typedef struct _PurpleHttpGzStream PurpleHttpGzStream; -typedef void (*PurpleHttpSocketConnectCb)(PurpleHttpSocket *hs, - const gchar *error, gpointer _hc); - struct _PurpleHttpSocket { - GSocketConnection *conn; - GCancellable *cancellable; - guint input_source; - guint output_source; + PurpleSocket *ps; gboolean is_busy; guint use_count; @@ -175,7 +174,7 @@ struct _PurpleHttpCookieJar struct _PurpleHttpKeepaliveRequest { PurpleConnection *gc; - PurpleHttpSocketConnectCb cb; + PurpleSocketConnectCb cb; gpointer user_data; PurpleHttpKeepaliveHost *host; @@ -218,7 +217,7 @@ struct _PurpleHttpConnectionSet struct _PurpleHttpGzStream { gboolean failed; - GZlibDecompressor *decompressor; + z_stream zs; gsize max_output; gsize decompressed; GString *pending; @@ -266,7 +265,7 @@ gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar); static PurpleHttpKeepaliveRequest * purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool, PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl, - PurpleHttpSocketConnectCb cb, gpointer user_data); + PurpleSocketConnectCb cb, gpointer user_data); static void purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req); static void @@ -366,14 +365,19 @@ static PurpleHttpGzStream * purple_http_gz_new(gsize max_output, gboolean is_deflate) { PurpleHttpGzStream *gzs = g_new0(PurpleHttpGzStream, 1); - GZlibCompressorFormat format; + int windowBits; if (is_deflate) - format = G_ZLIB_COMPRESSOR_FORMAT_RAW; + windowBits = -MAX_WBITS; else /* is gzip */ - format = G_ZLIB_COMPRESSOR_FORMAT_GZIP; + windowBits = MAX_WBITS + 32; + + if (inflateInit2(&gzs->zs, windowBits) != Z_OK) { + purple_debug_error("http", "Cannot initialize zlib stream\n"); + g_free(gzs); + return NULL; + } - gzs->decompressor = g_zlib_decompressor_new(format); gzs->max_output = max_output; return gzs; @@ -385,6 +389,7 @@ purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len) const gchar *compressed_buff; gsize compressed_len; GString *ret; + z_stream *zs; g_return_val_if_fail(gzs != NULL, NULL); g_return_val_if_fail(buf != NULL, NULL); @@ -392,6 +397,8 @@ purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len) if (gzs->failed) return NULL; + zs = &gzs->zs; + if (gzs->pending) { g_string_append_len(gzs->pending, buf, len); compressed_buff = gzs->pending->str; @@ -401,26 +408,22 @@ purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len) compressed_len = len; } + zs->next_in = (z_const Bytef*)compressed_buff; + zs->avail_in = compressed_len; + ret = g_string_new(NULL); - while (compressed_len > 0) { - GConverterResult gzres; + while (zs->avail_in > 0) { + int gzres; gchar decompressed_buff[PURPLE_HTTP_GZ_BUFF_LEN]; - gsize decompressed_len = 0; - gsize bytes_read = 0; - GError *error = NULL; - - gzres = g_converter_convert(G_CONVERTER(gzs->decompressor), - compressed_buff, compressed_len, - decompressed_buff, sizeof(decompressed_buff), - G_CONVERTER_NO_FLAGS, - &bytes_read, - &decompressed_len, - &error); - - compressed_buff += bytes_read; - compressed_len -= bytes_read; - - if (gzres == G_CONVERTER_CONVERTED || G_CONVERTER_FINISHED) { + gsize decompressed_len; + + zs->next_out = (Bytef*)decompressed_buff; + zs->avail_out = sizeof(decompressed_buff); + decompressed_len = zs->avail_out = sizeof(decompressed_buff); + gzres = inflate(zs, Z_FULL_FLUSH); + decompressed_len -= zs->avail_out; + + if (gzres == Z_OK || gzres == Z_STREAM_END) { if (decompressed_len == 0) break; if (gzs->decompressed + decompressed_len >= @@ -430,18 +433,17 @@ purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len) " decompressed data is reached\n"); decompressed_len = gzs->max_output - gzs->decompressed; - gzres = G_CONVERTER_FINISHED; + gzres = Z_STREAM_END; } gzs->decompressed += decompressed_len; g_string_append_len(ret, decompressed_buff, decompressed_len); - if (gzres == G_CONVERTER_FINISHED) + if (gzres == Z_STREAM_END) break; } else { purple_debug_error("http", "Decompression failed (%d): %s\n", gzres, - error->message); - g_clear_error(&error); + zs->msg); gzs->failed = TRUE; return NULL; } @@ -452,9 +454,9 @@ purple_http_gz_put(PurpleHttpGzStream *gzs, const gchar *buf, gsize len) gzs->pending = NULL; } - if (compressed_len > 0) { - gzs->pending = g_string_new_len(compressed_buff, - compressed_len); + if (zs->avail_in > 0) { + gzs->pending = g_string_new_len((gchar*)zs->next_in, + zs->avail_in); } return ret; @@ -465,7 +467,7 @@ purple_http_gz_free(PurpleHttpGzStream *gzs) { if (gzs == NULL) return; - g_object_unref(gzs->decompressor); + inflateEnd(&gzs->zs); if (gzs->pending) g_string_free(gzs->pending, TRUE); g_free(gzs); @@ -529,69 +531,23 @@ purple_http_socket_hash(const gchar *host, int port, gboolean is_ssl) return g_strdup_printf("%c:%s:%d", (is_ssl ? 'S' : 'R'), host, port); } -static void -purple_http_socket_connect_new_cb(GObject *source, GAsyncResult *res, - gpointer user_data) -{ - PurpleHttpSocket *hs = user_data; - GSocketConnection *conn; - PurpleHttpSocketConnectCb cb; - gpointer cb_data; - GError *error = NULL; - - conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), - res, &error); - - cb = g_object_steal_data(source, "cb"); - cb_data = g_object_steal_data(source, "cb_data"); - - if (conn == NULL) { - if (!g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - cb(hs, error->message, cb_data); - } - - g_clear_error(&error); - return; - } - - hs->conn = conn; - - cb(hs, NULL, cb_data); -} - static PurpleHttpSocket * purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host, - int port, gboolean is_ssl, - PurpleHttpSocketConnectCb cb, gpointer user_data) -{ - PurpleHttpSocket *hs; - GSocketClient *client; - GError *error = NULL; - - client = purple_gio_socket_client_new( - purple_connection_get_account(gc), &error); - - if (client == NULL) { - purple_debug_error("http", "Error connecting to '%s:%d': %s", - host, port, error->message); - g_clear_error(&error); + int port, gboolean is_ssl, PurpleSocketConnectCb cb, gpointer user_data) +{ + PurpleHttpSocket *hs = g_new0(PurpleHttpSocket, 1); + + hs->ps = purple_socket_new(gc); + purple_socket_set_data(hs->ps, "hs", hs); + purple_socket_set_tls(hs->ps, is_ssl); + purple_socket_set_host(hs->ps, host); + purple_socket_set_port(hs->ps, port); + if (!purple_socket_connect(hs->ps, cb, user_data)) { + purple_socket_destroy(hs->ps); + g_free(hs); return NULL; } - hs = g_new0(PurpleHttpSocket, 1); - hs->cancellable = g_cancellable_new(); - - g_socket_client_set_tls(client, is_ssl); - g_object_set_data(G_OBJECT(client), "cb", cb); - g_object_set_data(G_OBJECT(client), "cb_data", user_data); - - g_socket_client_connect_to_host_async(client, - host, port, hs->cancellable, - purple_http_socket_connect_new_cb, hs); - - g_object_unref(client); - if (purple_debug_is_verbose()) purple_debug_misc("http", "new socket created: %p\n", hs); @@ -607,26 +563,7 @@ purple_http_socket_close_free(PurpleHttpSocket *hs) if (purple_debug_is_verbose()) purple_debug_misc("http", "destroying socket: %p\n", hs); - if (hs->input_source > 0) { - g_source_remove(hs->input_source); - hs->input_source = 0; - } - - if (hs->output_source > 0) { - g_source_remove(hs->output_source); - hs->output_source = 0; - } - - if (hs->cancellable != NULL) { - g_cancellable_cancel(hs->cancellable); - g_clear_object(&hs->cancellable); - } - - if (hs->conn != NULL) { - purple_gio_graceful_close(G_IO_STREAM(hs->conn), NULL, NULL); - g_clear_object(&hs->conn); - } - + purple_socket_destroy(hs->ps); g_free(hs); } @@ -813,9 +750,10 @@ static void _purple_http_disconnect(PurpleHttpConnection *hc, gboolean is_graceful); static void _purple_http_gen_headers(PurpleHttpConnection *hc); -static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc); -static gboolean _purple_http_recv(GObject *source, gpointer _hc); -static gboolean _purple_http_send(GObject *source, gpointer _hc); +static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd); +static void _purple_http_recv(gpointer _hc, gint fd, + PurpleInputCondition cond); +static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond); /* closes current connection (if exists), estabilishes one and proceeds with * request */ @@ -1208,33 +1146,21 @@ static gboolean _purple_http_recv_body(PurpleHttpConnection *hc, return _purple_http_recv_body_data(hc, buf, len); } -static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc) +static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd) { int len; gchar buf[4096]; gboolean got_anything; - GError *error = NULL; - - len = g_pollable_input_stream_read_nonblocking( - G_POLLABLE_INPUT_STREAM( - g_io_stream_get_input_stream( - G_IO_STREAM(hc->socket->conn))), - buf, sizeof(buf), hc->socket->cancellable, - &error); + + len = purple_socket_read(hc->socket->ps, (guchar*)buf, sizeof(buf)); got_anything = (len > 0); - if (len < 0 && (g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || - g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_CANCELLED))) { - g_clear_error(&error); + if (len < 0 && errno == EAGAIN) return FALSE; - } if (len < 0) { _purple_http_error(hc, _("Error reading from %s: %s"), - hc->url->host, error->message); - g_clear_error(&error); + hc->url->host, g_strerror(errno)); return FALSE; } @@ -1413,13 +1339,11 @@ static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc) return got_anything; } -static gboolean _purple_http_recv(GObject *source, gpointer _hc) +static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond) { PurpleHttpConnection *hc = _hc; - while (_purple_http_recv_loopbody(hc)); - - return G_SOURCE_CONTINUE; + while (_purple_http_recv_loopbody(hc, fd)); } static void _purple_http_send_got_data(PurpleHttpConnection *hc, @@ -1450,19 +1374,17 @@ static void _purple_http_send_got_data(PurpleHttpConnection *hc, hc->request->contents_length = estimated_length; } -static gboolean _purple_http_send(GObject *source, gpointer _hc) +static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond) { PurpleHttpConnection *hc = _hc; int written, write_len; const gchar *write_from; gboolean writing_headers; - GError *error = NULL; - GSource *gsource; /* Waiting for data. This could be written more efficiently, by removing * (and later, adding) hs->inpa. */ if (hc->contents_reader_requested) - return G_SOURCE_CONTINUE; + return; _purple_http_gen_headers(hc); @@ -1475,7 +1397,7 @@ static gboolean _purple_http_send(GObject *source, gpointer _hc) hc->request_header_written; } else if (hc->request->contents_reader) { if (hc->contents_reader_requested) - return G_SOURCE_CONTINUE; /* waiting for data */ + return; /* waiting for data */ if (!hc->contents_reader_buffer) hc->contents_reader_buffer = g_string_new(""); if (hc->contents_reader_buffer->len == 0) { @@ -1488,7 +1410,7 @@ static gboolean _purple_http_send(GObject *source, gpointer _hc) PURPLE_HTTP_MAX_READ_BUFFER_LEN, hc->request->contents_reader_data, _purple_http_send_got_data); - return G_SOURCE_CONTINUE; + return; } write_from = hc->contents_reader_buffer->str; write_len = hc->contents_reader_buffer->len; @@ -1503,21 +1425,12 @@ static gboolean _purple_http_send(GObject *source, gpointer _hc) purple_debug_warning("http", "Nothing to write\n"); written = 0; } else { - written = g_pollable_output_stream_write_nonblocking( - G_POLLABLE_OUTPUT_STREAM( - g_io_stream_get_output_stream( - G_IO_STREAM(hc->socket->conn))), - write_from, write_len, hc->socket->cancellable, - &error); + written = purple_socket_write(hc->socket->ps, + (const guchar*)write_from, write_len); } - if (written < 0 && (g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) || - g_error_matches(error, - G_IO_ERROR, G_IO_ERROR_CANCELLED))) { - g_clear_error(&error); - return G_SOURCE_CONTINUE; - } + if (written < 0 && errno == EAGAIN) + return; if (written < 0) { if (hc->request_header_written == 0 && @@ -1526,22 +1439,21 @@ static gboolean _purple_http_send(GObject *source, gpointer _hc) purple_debug_info("http", "Keep-alive connection " "expired (when writing), retrying...\n"); purple_http_conn_retry(hc); - } else { - _purple_http_error(hc, _("Error writing to %s: %s"), - hc->url->host, error->message); + return; } - g_clear_error(&error); - return G_SOURCE_CONTINUE; + _purple_http_error(hc, _("Error writing to %s: %s"), + hc->url->host, g_strerror(errno)); + return; } if (writing_headers) { hc->request_header_written += written; purple_http_conn_notify_progress_watcher(hc); if (hc->request_header_written < hc->request_header->len) - return G_SOURCE_CONTINUE; + return; if (hc->request->contents_length > 0) - return G_SOURCE_CONTINUE; + return; } else { hc->request_contents_written += written; purple_http_conn_notify_progress_watcher(hc); @@ -1551,24 +1463,14 @@ static gboolean _purple_http_send(GObject *source, gpointer _hc) hc->request_contents_written < (guint)hc->request->contents_length) { - return G_SOURCE_CONTINUE; + return; } } /* request is completely written, let's read the response */ hc->is_reading = TRUE; - gsource = g_pollable_input_stream_create_source( - G_POLLABLE_INPUT_STREAM( - g_io_stream_get_input_stream( - G_IO_STREAM(hc->socket->conn))), - NULL); - g_source_set_callback(gsource, - (GSourceFunc)_purple_http_recv, hc, NULL); - hc->socket->input_source = g_source_attach(gsource, NULL); - g_source_unref(gsource); - - hc->socket->output_source = 0; - return G_SOURCE_REMOVE; + purple_socket_watch(hc->socket->ps, PURPLE_INPUT_READ, + _purple_http_recv, hc); } static void _purple_http_disconnect(PurpleHttpConnection *hc, @@ -1593,10 +1495,13 @@ static void _purple_http_disconnect(PurpleHttpConnection *hc, } static void -_purple_http_connected(PurpleHttpSocket *hs, const gchar *error, gpointer _hc) +_purple_http_connected(PurpleSocket *ps, const gchar *error, gpointer _hc) { + PurpleHttpSocket *hs = NULL; PurpleHttpConnection *hc = _hc; - GSource *source; + + if (ps != NULL) + hs = purple_socket_get_data(ps, "hs"); hc->socket_request = NULL; hc->socket = hs; @@ -1607,14 +1512,7 @@ _purple_http_connected(PurpleHttpSocket *hs, const gchar *error, gpointer _hc) return; } - source = g_pollable_output_stream_create_source( - G_POLLABLE_OUTPUT_STREAM( - g_io_stream_get_output_stream(G_IO_STREAM(hs->conn))), - NULL); - g_source_set_callback(source, - (GSourceFunc)_purple_http_send, hc, NULL); - hc->socket->output_source = g_source_attach(source, NULL); - g_source_unref(source); + purple_socket_watch(ps, PURPLE_INPUT_WRITE, _purple_http_send, hc); } static gboolean _purple_http_reconnect(PurpleHttpConnection *hc) @@ -2326,7 +2224,7 @@ purple_http_keepalive_pool_unref(PurpleHttpKeepalivePool *pool) static PurpleHttpKeepaliveRequest * purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool, PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl, - PurpleHttpSocketConnectCb cb, gpointer user_data) + PurpleSocketConnectCb cb, gpointer user_data) { PurpleHttpKeepaliveRequest *req; PurpleHttpKeepaliveHost *kahost; @@ -2369,15 +2267,19 @@ purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool, } static void -_purple_http_keepalive_socket_connected(PurpleHttpSocket *hs, +_purple_http_keepalive_socket_connected(PurpleSocket *ps, const gchar *error, gpointer _req) { + PurpleHttpSocket *hs = NULL; PurpleHttpKeepaliveRequest *req = _req; + if (ps != NULL) + hs = purple_socket_get_data(ps, "hs"); + if (hs != NULL) hs->use_count++; - req->cb(hs, error, req->user_data); + req->cb(ps, error, req->user_data); g_free(req); } @@ -2433,7 +2335,7 @@ _purple_http_keepalive_host_process_queue_cb(gpointer _host) purple_http_keepalive_host_process_queue(host); - req->cb(hs, NULL, req->user_data); + req->cb(hs->ps, NULL, req->user_data); g_free(req); return FALSE; @@ -2504,16 +2406,7 @@ purple_http_keepalive_pool_release(PurpleHttpSocket *hs, gboolean invalidate) if (purple_debug_is_verbose()) purple_debug_misc("http", "releasing a socket: %p\n", hs); - if (hs->input_source > 0) { - g_source_remove(hs->input_source); - hs->input_source = 0; - } - - if (hs->output_source > 0) { - g_source_remove(hs->output_source); - hs->output_source = 0; - } - + purple_socket_watch(hs->ps, 0, NULL, NULL); hs->is_busy = FALSE; host = hs->host; diff --git a/pidgin/libpurple/protocols/facebook/Makefile.am b/pidgin/libpurple/protocols/facebook/Makefile.am index 268e8efc..87dd0825 100644 --- a/pidgin/libpurple/protocols/facebook/Makefile.am +++ b/pidgin/libpurple/protocols/facebook/Makefile.am @@ -1,9 +1,12 @@ EXTRA_DIST = \ - Makefile.mingw + Makefile.mingw \ + marshaller.list pkgdir = @PURPLE_PLUGINDIR@ FACEBOOKSOURCES = \ + marshal.c \ + marshal.h \ api.c \ api.h \ data.c \ @@ -20,7 +23,23 @@ FACEBOOKSOURCES = \ thrift.c \ thrift.h \ util.c \ - util.h + util.h \ + ../../glibcompat.h \ + ../../http.c \ + ../../http.h \ + ../../purple-socket.h \ + ../../purple-socket.c + +CLEANFILES = \ + marshal.c \ + marshal.h + +marshal.c: $(srcdir)/marshaller.list marshal.h + $(AM_V_GEN)echo "#include \"marshal.h\"" > $@ + $(AM_V_at)$(GLIB_GENMARSHAL) --prefix=fb_marshal --body $(srcdir)/marshaller.list >> $@ + +marshal.h: $(srcdir)/marshaller.list + $(AM_V_GEN)$(GLIB_GENMARSHAL) --prefix=fb_marshal --header $(srcdir)/marshaller.list > $@ AM_CFLAGS = $(st) @@ -43,10 +62,9 @@ libfacebook_la_LIBADD = @PURPLE_LIBS@ $(JSON_LIBS) endif AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - -I$(top_srcdir) \ $(GLIB_CFLAGS) \ $(JSON_CFLAGS) \ - $(GPLUGIN_CFLAGS) \ + $(PURPLE_CFLAGS) \ + $(ZLIB_CFLAGS) \ + $(PLUGIN_CFLAGS) \ $(DEBUG_CFLAGS) diff --git a/pidgin/libpurple/protocols/facebook/Makefile.mingw b/pidgin/libpurple/protocols/facebook/Makefile.mingw index c2aaad84..181937ff 100644 --- a/pidgin/libpurple/protocols/facebook/Makefile.mingw +++ b/pidgin/libpurple/protocols/facebook/Makefile.mingw @@ -42,6 +42,7 @@ LIB_PATHS += -L$(GTK_TOP)/lib \ ## SOURCES, OBJECTS ## C_SRC = \ + marshal.c \ api.c \ data.c \ facebook.c \ @@ -49,7 +50,9 @@ C_SRC = \ json.c \ mqtt.c \ thrift.c \ - util.c + util.c \ + ../../http.c \ + ../../purple-socket.c OBJECTS = $(C_SRC:%.c=%.o) @@ -62,7 +65,6 @@ LIBS = \ -lgobject-2.0 \ -lws2_32 \ -lintl \ - -lgplugin \ -ljson-glib-1.0 \ -lz \ -lpurple @@ -76,19 +78,27 @@ include $(PIDGIN_COMMON_RULES) all: $(TARGET).dll -install: all $(DLL_INSTALL_DIR) +install: all cp $(TARGET).dll $(DLL_INSTALL_DIR) $(OBJECTS): $(PURPLE_CONFIG_H) -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) +$(TARGET).dll: $(OBJECTS) $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll +marshal.c: marshaller.list marshal.h + @echo "#include \"marshal.h\"" > $@ + @$(GLIB_GENMARSHAL) --prefix=fb_marshal --body marshaller.list >> $@ + +marshal.h: marshaller.list + @$(GLIB_GENMARSHAL) --prefix=fb_marshal --header marshaller.list > $@ + + ## ## CLEAN RULES ## clean: - rm -f $(OBJECTS) + rm -f $(OBJECTS) marshal.c marshal.h rm -f $(TARGET).dll include $(PIDGIN_COMMON_TARGETS) diff --git a/pidgin/libpurple/protocols/facebook/api.c b/pidgin/libpurple/protocols/facebook/api.c index 163a4884..445714bb 100644 --- a/pidgin/libpurple/protocols/facebook/api.c +++ b/pidgin/libpurple/protocols/facebook/api.c @@ -28,6 +28,7 @@ #include "api.h" #include "http.h" #include "json.h" +#include "marshal.h" #include "thrift.h" #include "util.h" @@ -91,7 +92,7 @@ fb_api_sticker(FbApi *api, FbId sid, FbApiMessage *msg); void fb_api_contacts_delta(FbApi *api, const gchar *delta_cursor); -G_DEFINE_TYPE(FbApi, fb_api, G_TYPE_OBJECT); +G_DEFINE_TYPE_WITH_CODE(FbApi, fb_api, G_TYPE_OBJECT, G_ADD_PRIVATE(FbApi)); static void fb_api_set_property(GObject *obj, guint prop, const GValue *val, @@ -291,7 +292,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -306,7 +308,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -322,7 +325,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -342,7 +346,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_BOOLEAN); @@ -358,7 +363,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); @@ -374,7 +380,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -389,7 +396,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -404,7 +412,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -419,7 +428,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -435,7 +445,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -452,7 +463,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__INT64, G_TYPE_NONE, 1, FB_TYPE_ID); @@ -468,7 +480,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -484,7 +497,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); @@ -499,7 +513,8 @@ fb_api_class_init(FbApiClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER); } @@ -1486,6 +1501,23 @@ fb_api_cb_publish_ms_new_message(FbApi *api, JsonNode *root, GSList *msgs, GErro static GSList * fb_api_cb_publish_ms_event(FbApi *api, JsonNode *root, GSList *events, FbApiEventType type, GError **error); +static void +fb_api_cb_publish_mst(FbThrift *thft, GError **error) +{ + if (fb_thrift_read_isstop(thft)) { + FB_API_TCHK(fb_thrift_read_stop(thft)); + } else { + FbThriftType type; + gint16 id; + + FB_API_TCHK(fb_thrift_read_field(thft, &type, &id, 0)); + FB_API_TCHK(type == FB_THRIFT_TYPE_STRING); + // FB_API_TCHK(id == 2); + FB_API_TCHK(fb_thrift_read_str(thft, NULL)); + FB_API_TCHK(fb_thrift_read_stop(thft)); + } +} + static void fb_api_cb_publish_ms(FbApi *api, GByteArray *pload) { @@ -1516,10 +1548,14 @@ fb_api_cb_publish_ms(FbApi *api, GByteArray *pload) /* Read identifier string (for Facebook employees) */ thft = fb_thrift_new(pload, 0); - fb_thrift_read_str(thft, NULL); + fb_api_cb_publish_mst(thft, &err); size = fb_thrift_get_pos(thft); g_object_unref(thft); + FB_API_ERROR_EMIT(api, err, + return; + ); + g_return_if_fail(size < pload->len); data = (gchar *) pload->data + size; size = pload->len - size; @@ -1829,10 +1865,10 @@ fb_api_cb_publish_pt(FbThrift *thft, GSList **press, GError **error) pres->active = i32 != 0; *press = g_slist_prepend(*press, pres); - fb_util_debug_info("Presence: %" FB_ID_FORMAT " (%d)", - i64, i32 != 0); + fb_util_debug_info("Presence: %" FB_ID_FORMAT " (%d) id: %d", + i64, i32 != 0, id); - while (id <= 5) { + while (id <= 6) { if (fb_thrift_read_isstop(thft)) { break; } @@ -1879,7 +1915,9 @@ fb_api_cb_publish_pt(FbThrift *thft, GSList **press, GError **error) } /* Read the field stop */ - FB_API_TCHK(fb_thrift_read_stop(thft)); + if (fb_thrift_read_isstop(thft)) { + FB_API_TCHK(fb_thrift_read_stop(thft)); + } } static void @@ -2409,7 +2447,7 @@ fb_api_cb_contacts(PurpleHttpConnection *con, PurpleHttpResponse *res, priv->contacts_delta = g_strdup(is_delta ? cursor : delta_cursor); } - if (users) { + if (users || (complete && !is_delta)) { g_signal_emit_by_name(api, "contacts", users, complete); } diff --git a/pidgin/libpurple/protocols/facebook/api.h b/pidgin/libpurple/protocols/facebook/api.h index 111788ea..63c9a1b9 100644 --- a/pidgin/libpurple/protocols/facebook/api.h +++ b/pidgin/libpurple/protocols/facebook/api.h @@ -104,14 +104,14 @@ * server started checking this. */ -#define FB_ORCA_AGENT "[FBAN/Orca-Android;FBAV/109.0.0.17.70;FBPN/com.facebook.orca;FBLC/en_US;FBBV/52182662]" +#define FB_ORCA_AGENT "[FBAN/Orca-Android;FBAV/537.0.0.31.101;FBPN/com.facebook.orca;FBLC/en_US;FBBV/52182662]" /** * FB_API_AGENT: * * The HTTP User-Agent header. */ -#define FB_API_AGENT "Facebook plugin / Purple / 0.9.5 " FB_ORCA_AGENT +#define FB_API_AGENT "Facebook plugin / Purple / " PACKAGE_VERSION " " FB_ORCA_AGENT /** * FB_API_MQTT_AGENT diff --git a/pidgin/libpurple/protocols/facebook/data.c b/pidgin/libpurple/protocols/facebook/data.c index fe32404f..81e32581 100644 --- a/pidgin/libpurple/protocols/facebook/data.c +++ b/pidgin/libpurple/protocols/facebook/data.c @@ -59,8 +59,8 @@ static const gchar *fb_props_strs[] = { "token" }; -G_DEFINE_TYPE(FbData, fb_data, G_TYPE_OBJECT); -G_DEFINE_TYPE(FbDataImage, fb_data_image, G_TYPE_OBJECT); +G_DEFINE_TYPE_WITH_CODE(FbData, fb_data, G_TYPE_OBJECT, G_ADD_PRIVATE(FbData)); +G_DEFINE_TYPE_WITH_CODE(FbDataImage, fb_data_image, G_TYPE_OBJECT, G_ADD_PRIVATE(FbDataImage)); static void fb_data_dispose(GObject *obj) diff --git a/pidgin/libpurple/protocols/facebook/facebook.c b/pidgin/libpurple/protocols/facebook/facebook.c index 9554adc7..7a6ed818 100644 --- a/pidgin/libpurple/protocols/facebook/facebook.c +++ b/pidgin/libpurple/protocols/facebook/facebook.c @@ -373,8 +373,8 @@ fb_cb_api_error(FbApi *api, GError *error, gpointer data) gc = fb_data_get_connection(fata); - if (error->domain == G_IO_ERROR) { - purple_connection_g_error(gc, error); + if (error->domain == FB_MQTT_SSL_ERROR) { + purple_connection_ssl_error(gc, error->code); return; } @@ -500,7 +500,7 @@ fb_cb_image(FbDataImage *img, GError *error) id = purple_image_store_add_weak(pimg); g_free(msg->text); - msg->text = g_strdup_printf("text = g_strdup_printf("", id); msg->flags |= FB_API_MESSAGE_FLAG_DONE; @@ -966,7 +966,7 @@ fb_blist_chat_init(PurpleBlistNode *node, gpointer data) GSList *select = NULL; PurpleConnection *gc; - if (!PURPLE_IS_BUDDY(node)) { + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { return; } @@ -1160,7 +1160,7 @@ fb_client_blist_node_menu(PurpleBlistNode *node) PurpleConnection *gc; PurpleMenuAction *act; - if (!PURPLE_IS_BUDDY(node)) { + if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) { return NULL; } @@ -1208,7 +1208,8 @@ fb_server_set_status(PurpleAccount *acct, PurpleStatus *status) } static gint -fb_im_send(PurpleConnection *gc, PurpleMessage *msg) +fb_im_send(PurpleConnection *gc, const gchar *who, const gchar *tmsg, + PurpleMessageFlags flags) { const gchar *name; const gchar *text; @@ -1217,6 +1218,8 @@ fb_im_send(PurpleConnection *gc, PurpleMessage *msg) FbId uid; gchar *sext; + PurpleMessage *msg = purple_message_new_outgoing(who, tmsg, flags); + fata = purple_connection_get_protocol_data(gc); api = fb_data_get_api(fata); @@ -1355,7 +1358,8 @@ fb_chat_invite(PurpleConnection *gc, gint id, const gchar *msg, } static gint -fb_chat_send(PurpleConnection *gc, gint id, PurpleMessage *msg) +fb_chat_send(PurpleConnection *gc, gint id, const gchar *tmsg, + PurpleMessageFlags flags) { const gchar *name; const gchar *text; @@ -1366,6 +1370,8 @@ fb_chat_send(PurpleConnection *gc, gint id, PurpleMessage *msg) PurpleAccount *acct; PurpleChatConversation *chat; + PurpleMessage *msg = purple_message_new_outgoing(NULL, tmsg, flags); + acct = purple_connection_get_account(gc); fata = purple_connection_get_protocol_data(gc); api = fb_data_get_api(fata); @@ -1533,107 +1539,6 @@ fb_cmd_leave(PurpleConversation *conv, const gchar *cmd, gchar **args, return PURPLE_CMD_RET_OK; } -static void -facebook_protocol_init(PurpleProtocol *protocol) -{ - GList *opts = NULL; - PurpleAccountOption *opt; - - protocol->id = FB_PROTOCOL_ID; - protocol->name = "Facebook"; - protocol->options = OPT_PROTO_CHAT_TOPIC; - - opt = purple_account_option_int_new(_("Buddy list sync interval"), - "sync-interval", 5); - opts = g_list_prepend(opts, opt); - - opt = purple_account_option_bool_new(_("Mark messages as read on focus"), - "mark-read", TRUE); - opts = g_list_prepend(opts, opt); - - opt = purple_account_option_bool_new(_("Mark messages as read only when available"), - "mark-read-available", FALSE); - opts = g_list_prepend(opts, opt); - - opt = purple_account_option_bool_new(_("Show self messages"), - "show-self", TRUE); - opts = g_list_prepend(opts, opt); - - opt = purple_account_option_bool_new(_("Show unread messages"), - "show-unread", TRUE); - opts = g_list_prepend(opts, opt); - - opt = purple_account_option_bool_new(_("Open new group chats with " - "incoming messages"), - "group-chat-open", TRUE); - opts = g_list_prepend(opts, opt); - protocol->account_options = g_list_reverse(opts); -} - -static void -facebook_protocol_class_init(PurpleProtocolClass *klass) -{ - klass->login = fb_login; - klass->close = fb_close; - klass->status_types = fb_status_types; - klass->list_icon = fb_list_icon; -} - -static void -facebook_protocol_client_iface_init(PurpleProtocolClientIface *iface) -{ - iface->tooltip_text = fb_client_tooltip_text; - iface->blist_node_menu = fb_client_blist_node_menu; - iface->offline_message = fb_client_offline_message; -} - -static void -facebook_protocol_server_iface_init(PurpleProtocolServerIface *iface) -{ - iface->set_status = fb_server_set_status; -} - -static void -facebook_protocol_im_iface_init(PurpleProtocolIMIface *iface) -{ - iface->send = fb_im_send; - iface->send_typing = fb_im_send_typing; -} - -static void -facebook_protocol_chat_iface_init(PurpleProtocolChatIface *iface) -{ - iface->info = fb_chat_info; - iface->info_defaults = fb_chat_info_defaults; - iface->join = fb_chat_join; - iface->get_name = fb_chat_get_name; - iface->invite = fb_chat_invite; - iface->send = fb_chat_send; - iface->set_topic = fb_chat_set_topic; -} - -static void -facebook_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *iface) -{ - iface->get_list = fb_roomlist_get_list; - iface->cancel = fb_roomlist_cancel; -} - -PURPLE_DEFINE_TYPE_EXTENDED( - FacebookProtocol, facebook_protocol, PURPLE_TYPE_PROTOCOL, 0, - - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, - facebook_protocol_client_iface_init) - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE, - facebook_protocol_server_iface_init) - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE, - facebook_protocol_im_iface_init) - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE, - facebook_protocol_chat_iface_init) - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, - facebook_protocol_roomlist_iface_init) -); - static void fb_cmds_register(void) { @@ -1646,13 +1551,13 @@ fb_cmds_register(void) g_return_if_fail(fb_cmds == NULL); id = purple_cmd_register("kick", "s", PURPLE_CMD_P_PROTOCOL, cflags, - fb_protocol->id, fb_cmd_kick, + "prpl-facebook", fb_cmd_kick, _("kick: Kick someone from the chat"), NULL); fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id)); id = purple_cmd_register("leave", "", PURPLE_CMD_P_PROTOCOL, cflags, - fb_protocol->id, fb_cmd_leave, + "prpl-facebook", fb_cmd_leave, _("leave: Leave the chat"), NULL); fb_cmds = g_slist_prepend(fb_cmds, GUINT_TO_POINTER(id)); @@ -1671,43 +1576,110 @@ fb_cmds_unregister(void) g_slist_free_full(fb_cmds, fb_cmds_unregister_free); } -static PurplePluginInfo * -plugin_query(GError **error) -{ - return purple_plugin_info_new( - "id", FB_PROTOCOL_ID, - "name", "Facebook Protocol", - "version", DISPLAY_VERSION, - "category", N_("Protocol"), - "summary", N_("Facebook Protocol Plugin"), - "description", N_("Facebook Protocol Plugin"), - "website", PURPLE_WEBSITE, - "abi-version", PURPLE_ABI_VERSION, - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL | - PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD, - NULL - ); -} - static gboolean -plugin_load(PurplePlugin *plugin, GError **error) +plugin_load(PurplePlugin *plugin) { - facebook_protocol_register_type(plugin); - fb_protocol = purple_protocols_add(FACEBOOK_TYPE_PROTOCOL, error); - - if (fb_protocol == NULL) { - return FALSE; - } - fb_cmds_register(); + _purple_socket_init(); + purple_http_init(); return TRUE; } static gboolean -plugin_unload(PurplePlugin *plugin, GError **error) +plugin_unload(PurplePlugin *plugin) { fb_cmds_unregister(); - return purple_protocols_remove(fb_protocol, error); + purple_http_uninit(); + _purple_socket_uninit(); + return TRUE; } -PURPLE_PLUGIN_INIT(facebook, plugin_query, plugin_load, plugin_unload); +G_MODULE_EXPORT gboolean +purple_init_plugin(PurplePlugin *plugin); + +G_MODULE_EXPORT gboolean +purple_init_plugin(PurplePlugin *plugin) +{ + GList *opts = NULL; + PurpleAccountOption *opt; + + static gboolean inited = FALSE; + static PurplePluginInfo info; + static PurplePluginProtocolInfo pinfo; + + (void) fb_protocol; + plugin->info = &info; + + if (G_LIKELY(inited)) { + return purple_plugin_register(plugin); + } + + memset(&info, 0, sizeof info); + memset(&pinfo, 0, sizeof pinfo); + + info.magic = PURPLE_PLUGIN_MAGIC; + info.major_version = PURPLE_MAJOR_VERSION; + info.minor_version = PURPLE_MINOR_VERSION; + info.type = PURPLE_PLUGIN_PROTOCOL; + info.priority = PURPLE_PRIORITY_DEFAULT; + info.id = FB_PROTOCOL_ID; + info.name = "Facebook"; + info.version = PACKAGE_VERSION; + info.summary = N_("Facebook Protocol Plugin"); + info.description = N_("Facebook Protocol Plugin"); + info.homepage = PACKAGE_URL; + info.load = plugin_load; + info.unload = plugin_unload; + info.extra_info = &pinfo; + + pinfo.options = OPT_PROTO_CHAT_TOPIC; + pinfo.list_icon = fb_list_icon; + pinfo.tooltip_text = fb_client_tooltip_text; + pinfo.status_types = fb_status_types; + pinfo.blist_node_menu = fb_client_blist_node_menu; + pinfo.chat_info = fb_chat_info; + pinfo.chat_info_defaults = fb_chat_info_defaults; + pinfo.login = fb_login; + pinfo.close = fb_close; + pinfo.send_im = fb_im_send; + pinfo.send_typing = fb_im_send_typing; + pinfo.set_status = fb_server_set_status; + pinfo.join_chat = fb_chat_join; + pinfo.get_chat_name = fb_chat_get_name; + pinfo.chat_invite = fb_chat_invite; + pinfo.chat_send = fb_chat_send; + pinfo.set_chat_topic = fb_chat_set_topic; + pinfo.roomlist_get_list = fb_roomlist_get_list; + pinfo.roomlist_cancel = fb_roomlist_cancel; + pinfo.offline_message = fb_client_offline_message; + pinfo.struct_size = sizeof pinfo; + + opt = purple_account_option_int_new(_("Buddy list sync interval"), + "sync-interval", 5); + opts = g_list_prepend(opts, opt); + + opt = purple_account_option_bool_new(_("Mark messages as read on focus"), + "mark-read", TRUE); + opts = g_list_prepend(opts, opt); + + opt = purple_account_option_bool_new(_("Mark messages as read only when available"), + "mark-read-available", FALSE); + opts = g_list_prepend(opts, opt); + + opt = purple_account_option_bool_new(_("Show self messages"), + "show-self", TRUE); + opts = g_list_prepend(opts, opt); + + opt = purple_account_option_bool_new(_("Show unread messages"), + "show-unread", TRUE); + opts = g_list_prepend(opts, opt); + + opt = purple_account_option_bool_new(_("Open new group chats with " + "incoming messages"), + "group-chat-open", TRUE); + opts = g_list_prepend(opts, opt); + pinfo.protocol_options = g_list_reverse(opts); + + inited = TRUE; + return purple_plugin_register(plugin); +} diff --git a/pidgin/libpurple/protocols/facebook/facebook.h b/pidgin/libpurple/protocols/facebook/facebook.h index edc8b262..aa8b3c34 100644 --- a/pidgin/libpurple/protocols/facebook/facebook.h +++ b/pidgin/libpurple/protocols/facebook/facebook.h @@ -22,24 +22,7 @@ #ifndef _FACEBOOK_H_ #define _FACEBOOK_H_ -/** - * SECTION:facebook - * @section_id: facebook-plugin - * @short_description: facebook.h - * @title: Facebook Plugin - * - * The Facebook Messenger #PurpleProtocol. - */ - #include -#include - -#define FACEBOOK_TYPE_PROTOCOL (facebook_protocol_get_type()) -#define FACEBOOK_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), FACEBOOK_TYPE_PROTOCOL, FacebookProtocol)) -#define FACEBOOK_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), FACEBOOK_TYPE_PROTOCOL, FacebookProtocolClass)) -#define FACEBOOK_IS_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), FACEBOOK_TYPE_PROTOCOL)) -#define FACEBOOK_IS_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), FACEBOOK_TYPE_PROTOCOL)) -#define FACEBOOK_PROTOCOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FACEBOOK_TYPE_PROTOCOL, FacebookProtocolClass)) /** * FB_PROTOCOL_ID: @@ -48,37 +31,4 @@ */ #define FB_PROTOCOL_ID "prpl-facebook" -typedef struct _FacebookProtocol FacebookProtocol; -typedef struct _FacebookProtocolClass FacebookProtocolClass; - -/** - * FacebookProtocol: - * - * Represents the Facebook #PurpleProtocol. - */ -struct _FacebookProtocol -{ - /*< private >*/ - PurpleProtocol parent; -}; - -/** - * FacebookProtocolClass: - * - * The base class for all #FacebookProtocol's. - */ -struct _FacebookProtocolClass -{ - /*< private >*/ - PurpleProtocolClass parent_class; -}; - -/** - * facebook_protocol_get_type: - * - * Returns: The #GType for a #FacebookProtocol. - */ -G_MODULE_EXPORT GType -facebook_protocol_get_type(void); - #endif /* _FACEBOOK_H_ */ diff --git a/pidgin/libpurple/protocols/facebook/http.c b/pidgin/libpurple/protocols/facebook/http.c index 148c9c26..cd9876cf 100644 --- a/pidgin/libpurple/protocols/facebook/http.c +++ b/pidgin/libpurple/protocols/facebook/http.c @@ -381,7 +381,7 @@ fb_http_urlcmp(const gchar *url1, const gchar *url2, gboolean protocol) PurpleHttpURL *purl1; PurpleHttpURL *purl2; - static const const gchar * (*funcs[]) (const PurpleHttpURL *url) = { + static const gchar * (*funcs[]) (const PurpleHttpURL *url) = { /* Always first so it can be skipped */ purple_http_url_get_protocol, diff --git a/pidgin/libpurple/protocols/facebook/json.c b/pidgin/libpurple/protocols/facebook/json.c index f36839c5..448c5b2e 100644 --- a/pidgin/libpurple/protocols/facebook/json.c +++ b/pidgin/libpurple/protocols/facebook/json.c @@ -25,6 +25,7 @@ #include #include "json.h" +#include "glibcompat.h" #include "util.h" typedef struct _FbJsonValue FbJsonValue; @@ -50,7 +51,7 @@ struct _FbJsonValuesPrivate GError *error; }; -G_DEFINE_TYPE(FbJsonValues, fb_json_values, G_TYPE_OBJECT); +G_DEFINE_TYPE_WITH_CODE(FbJsonValues, fb_json_values, G_TYPE_OBJECT, G_ADD_PRIVATE(FbJsonValues)); static void fb_json_values_dispose(GObject *obj) diff --git a/pidgin/libpurple/protocols/facebook/mqtt.c b/pidgin/libpurple/protocols/facebook/mqtt.c index da279cbe..ec87b0f7 100644 --- a/pidgin/libpurple/protocols/facebook/mqtt.c +++ b/pidgin/libpurple/protocols/facebook/mqtt.c @@ -28,26 +28,26 @@ #include "account.h" #include "eventloop.h" #include "glibcompat.h" -#include "purple-gio.h" -#include "queuedoutputstream.h" +#include "sslconn.h" +#include "marshal.h" #include "mqtt.h" #include "util.h" struct _FbMqttPrivate { PurpleConnection *gc; - GIOStream *conn; - GBufferedInputStream *input; - PurpleQueuedOutputStream *output; - GCancellable *cancellable; + PurpleSslConnection *gsc; gboolean connected; guint16 mid; GByteArray *rbuf; + GByteArray *wbuf; gsize remz; gint tev; + gint rev; + gint wev; }; struct _FbMqttMessagePrivate @@ -62,10 +62,8 @@ struct _FbMqttMessagePrivate gboolean local; }; -G_DEFINE_TYPE(FbMqtt, fb_mqtt, G_TYPE_OBJECT); -G_DEFINE_TYPE(FbMqttMessage, fb_mqtt_message, G_TYPE_OBJECT); - -static void fb_mqtt_read_packet(FbMqtt *mqtt); +G_DEFINE_TYPE_WITH_CODE(FbMqtt, fb_mqtt, G_TYPE_OBJECT, G_ADD_PRIVATE(FbMqtt)); +G_DEFINE_TYPE_WITH_CODE(FbMqttMessage, fb_mqtt_message, G_TYPE_OBJECT, G_ADD_PRIVATE(FbMqttMessage)); static void fb_mqtt_dispose(GObject *obj) @@ -75,6 +73,7 @@ fb_mqtt_dispose(GObject *obj) fb_mqtt_close(mqtt); g_byte_array_free(priv->rbuf, TRUE); + g_byte_array_free(priv->wbuf, TRUE); } static void @@ -96,7 +95,8 @@ fb_mqtt_class_init(FbMqttClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -112,9 +112,10 @@ fb_mqtt_class_init(FbMqttClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__POINTER, G_TYPE_NONE, - 1, G_TYPE_ERROR); + 1, G_TYPE_POINTER); /** * FbMqtt::open: @@ -128,7 +129,8 @@ fb_mqtt_class_init(FbMqttClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -144,7 +146,8 @@ fb_mqtt_class_init(FbMqttClass *klass) G_TYPE_FROM_CLASS(klass), G_SIGNAL_ACTION, 0, - NULL, NULL, NULL, + NULL, NULL, + fb_marshal_VOID__STRING_BOXED, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BYTE_ARRAY); } @@ -158,6 +161,7 @@ fb_mqtt_init(FbMqtt *mqtt) mqtt->priv = priv; priv->rbuf = g_byte_array_new(); + priv->wbuf = g_byte_array_new(); } static void @@ -201,6 +205,18 @@ fb_mqtt_error_quark(void) return q; } +GQuark +fb_mqtt_ssl_error_quark(void) +{ + static GQuark q = 0; + + if (G_UNLIKELY(q == 0)) { + q = g_quark_from_static_string("fb-mqtt-ssl-error-quark"); + } + + return q; +} + FbMqtt * fb_mqtt_new(PurpleConnection *gc) { @@ -224,47 +240,33 @@ fb_mqtt_close(FbMqtt *mqtt) g_return_if_fail(FB_IS_MQTT(mqtt)); priv = mqtt->priv; - if (priv->tev > 0) { - g_source_remove(priv->tev); - priv->tev = 0; + if (priv->wev > 0) { + purple_input_remove(priv->wev); + priv->wev = 0; } - if (priv->cancellable != NULL) { - g_cancellable_cancel(priv->cancellable); - g_clear_object(&priv->cancellable); + if (priv->rev > 0) { + purple_input_remove(priv->rev); + priv->rev = 0; } - if (priv->conn != NULL) { - purple_gio_graceful_close(priv->conn, - G_INPUT_STREAM(priv->input), - G_OUTPUT_STREAM(priv->output)); - g_clear_object(&priv->input); - g_clear_object(&priv->output); - g_clear_object(&priv->conn); + if (priv->tev > 0) { + purple_timeout_remove(priv->tev); + priv->tev = 0; } - priv->connected = FALSE; - g_byte_array_set_size(priv->rbuf, 0); -} - -static void -fb_mqtt_take_error(FbMqtt *mqtt, GError *err, const gchar *prefix) -{ - if (g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - /* Return as cancelled means the connection is closing */ - g_error_free(err); - return; + if (priv->gsc != NULL) { + purple_ssl_close(priv->gsc); + priv->gsc = NULL; } - /* Now we can check for programming errors */ - g_return_if_fail(FB_IS_MQTT(mqtt)); - - if (prefix != NULL) { - g_prefix_error(&err, "%s: ", prefix); + if (priv->wbuf->len > 0) { + fb_util_debug_warning("Closing with unwritten data"); } - g_signal_emit_by_name(mqtt, "error", err); - g_error_free(err); + priv->connected = FALSE; + g_byte_array_set_size(priv->rbuf, 0); + g_byte_array_set_size(priv->wbuf, 0); } void @@ -342,127 +344,84 @@ fb_mqtt_ping(FbMqtt *mqtt) } static void -fb_mqtt_cb_fill(GObject *source, GAsyncResult *res, gpointer data) +fb_mqtt_cb_read(gpointer data, gint fd, PurpleInputCondition cond) { - GBufferedInputStream *input = G_BUFFERED_INPUT_STREAM(source); FbMqtt *mqtt = data; - gssize ret; - GError *err = NULL; + FbMqttMessage *msg; + FbMqttPrivate *priv = mqtt->priv; + gint res; + guint mult; + guint8 buf[1024]; + guint8 byte; + gsize size; + gssize rize; + + if (priv->remz < 1) { + /* Reset the read buffer */ + g_byte_array_set_size(priv->rbuf, 0); - ret = g_buffered_input_stream_fill_finish(input, res, &err); + res = purple_ssl_read(priv->gsc, &byte, sizeof byte); - if (ret < 1) { - if (ret == 0) { - err = g_error_new_literal(G_IO_ERROR, - G_IO_ERROR_CONNECTION_CLOSED, - _("Connection closed")); + if (res < 0 && errno == EAGAIN) { + return; + } else if (res != 1) { + fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, + _("Failed to read fixed header")); + return; } - fb_mqtt_take_error(mqtt, err, _("Failed to read fixed header")); - return; - } + g_byte_array_append(priv->rbuf, &byte, sizeof byte); - fb_mqtt_read_packet(mqtt); -} + mult = 1; -static void -fb_mqtt_cb_read_packet(GObject *source, GAsyncResult *res, gpointer data) -{ - FbMqtt *mqtt = data; - FbMqttPrivate *priv; - gssize ret; - FbMqttMessage *msg; - GError *err = NULL; + do { + res = purple_ssl_read(priv->gsc, &byte, sizeof byte); - ret = g_input_stream_read_finish(G_INPUT_STREAM(source), res, &err); + /* TODO: this case isn't handled yet */ + if (0 && res < 0 && errno == EAGAIN) { + return; + } else if (res != 1) { + fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, + _("Failed to read packet size")); + return; + } - if (ret < 1) { - if (ret == 0) { - err = g_error_new_literal(G_IO_ERROR, - G_IO_ERROR_CONNECTION_CLOSED, - _("Connection closed")); - } + g_byte_array_append(priv->rbuf, &byte, sizeof byte); - fb_mqtt_take_error(mqtt, err, _("Failed to read packet data")); - return; + priv->remz += (byte & 127) * mult; + mult *= 128; + } while ((byte & 128) != 0); } - priv = mqtt->priv; - priv->remz -= ret; - if (priv->remz > 0) { - g_input_stream_read_async(G_INPUT_STREAM(source), - priv->rbuf->data + - priv->rbuf->len - priv->remz, priv->remz, - G_PRIORITY_DEFAULT, priv->cancellable, - fb_mqtt_cb_read_packet, mqtt); - return; - } - - msg = fb_mqtt_message_new_bytes(priv->rbuf); + size = MIN(priv->remz, sizeof buf); + rize = purple_ssl_read(priv->gsc, buf, size); - if (G_UNLIKELY(msg == NULL)) { - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, - _("Failed to parse message")); - return; - } - - fb_mqtt_read(mqtt, msg); - g_object_unref(msg); + if (rize < 0 && errno == EAGAIN) { + return; + } else if (rize < 1) { + fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, + _("Failed to read packet data")); + return; + } - /* Read another packet if connection wasn't reset in fb_mqtt_read() */ - if (fb_mqtt_connected(mqtt, FALSE)) { - fb_mqtt_read_packet(mqtt); + g_byte_array_append(priv->rbuf, buf, rize); + priv->remz -= rize; } -} -static void -fb_mqtt_read_packet(FbMqtt *mqtt) -{ - FbMqttPrivate *priv = mqtt->priv; - const guint8 const *buf; - gsize count = 0; - gsize pos; - guint mult = 1; - guint8 byte; - gsize size = 0; + if (priv->remz < 1) { + msg = fb_mqtt_message_new_bytes(priv->rbuf); + priv->remz = 0; - buf = g_buffered_input_stream_peek_buffer(priv->input, &count); - - /* Start at 1 to skip the first byte */ - pos = 1; - - do { - if (pos >= count) { - /* Not enough data yet, try again later */ - g_buffered_input_stream_fill_async(priv->input, -1, - G_PRIORITY_DEFAULT, priv->cancellable, - fb_mqtt_cb_fill, mqtt); + if (G_UNLIKELY(msg == NULL)) { + fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, + _("Failed to parse message")); return; } - byte = *(buf + pos++); - - size += (byte & 127) * mult; - mult *= 128; - } while ((byte & 128) != 0); - - /* Add header to size */ - size += pos; - - g_byte_array_set_size(priv->rbuf, size); - priv->remz = size; - - /* TODO: Use g_input_stream_read_all_async() when available. */ - /* TODO: Alternately, it would be nice to let the - * FbMqttMessage directly use the GBufferedInputStream - * buffer instead of copying it, provided it's consumed - * before the next read. - */ - g_input_stream_read_async(G_INPUT_STREAM(priv->input), - priv->rbuf->data, priv->rbuf->len, - G_PRIORITY_DEFAULT, priv->cancellable, - fb_mqtt_cb_read_packet, mqtt); + fb_mqtt_read(mqtt, msg); + g_object_unref(msg); + } } void @@ -569,16 +528,27 @@ fb_mqtt_read(FbMqtt *mqtt, FbMqttMessage *msg) } static void -fb_mqtt_cb_flush(GObject *source, GAsyncResult *res, gpointer data) +fb_mqtt_cb_write(gpointer data, gint fd, PurpleInputCondition cond) { FbMqtt *mqtt = data; - GError *err = NULL; + FbMqttPrivate *priv = mqtt->priv; + gssize wize; + + wize = purple_ssl_write(priv->gsc, priv->wbuf->data, priv->wbuf->len); - if (!g_output_stream_flush_finish(G_OUTPUT_STREAM(source), - res, &err)) { - fb_mqtt_take_error(mqtt, err, _("Failed to write data")); + if (wize < 0) { + fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, + _("Failed to write data")); return; } + + if (wize > 0) { + g_byte_array_remove_range(priv->wbuf, 0, wize); + } + + if (priv->wbuf->len < 1) { + priv->wev = 0; + } } void @@ -587,7 +557,6 @@ fb_mqtt_write(FbMqtt *mqtt, FbMqttMessage *msg) const GByteArray *bytes; FbMqttMessagePrivate *mriv; FbMqttPrivate *priv; - GBytes *gbytes; g_return_if_fail(FB_IS_MQTT(mqtt)); g_return_if_fail(FB_IS_MQTT_MESSAGE(msg)); @@ -606,46 +575,46 @@ fb_mqtt_write(FbMqtt *mqtt, FbMqttMessage *msg) "Writing %d (flags: 0x%0X)", mriv->type, mriv->flags); - /* TODO: Would be nice to refactor this to not require copying bytes */ - gbytes = g_bytes_new(bytes->data, bytes->len); - purple_queued_output_stream_push_bytes(priv->output, gbytes); - g_bytes_unref(gbytes); + g_byte_array_append(priv->wbuf, bytes->data, bytes->len); + fb_mqtt_cb_write(mqtt, priv->gsc->fd, PURPLE_INPUT_WRITE); - if (!g_output_stream_has_pending(G_OUTPUT_STREAM(priv->output))) { - g_output_stream_flush_async(G_OUTPUT_STREAM(priv->output), - G_PRIORITY_DEFAULT, priv->cancellable, - fb_mqtt_cb_flush, mqtt); + if (priv->wev > 0) { + priv->wev = purple_input_add(priv->gsc->fd, + PURPLE_INPUT_WRITE, + fb_mqtt_cb_write, mqtt); } } static void -fb_mqtt_cb_open(GObject *source, GAsyncResult *res, gpointer data) +fb_mqtt_cb_open(gpointer data, PurpleSslConnection *ssl, + PurpleInputCondition cond) { FbMqtt *mqtt = data; - FbMqttPrivate *priv; - GSocketConnection *conn; - GError *err = NULL; - - conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source), - res, &err); - - if (conn == NULL) { - fb_mqtt_take_error(mqtt, err, NULL); - return; - } + FbMqttPrivate *priv = mqtt->priv; fb_mqtt_timeout_clear(mqtt); + priv->rev = purple_input_add(priv->gsc->fd, PURPLE_INPUT_READ, + fb_mqtt_cb_read, mqtt); + g_signal_emit_by_name(mqtt, "open"); +} - priv = mqtt->priv; - priv->conn = G_IO_STREAM(conn); - priv->input = G_BUFFERED_INPUT_STREAM(g_buffered_input_stream_new( - g_io_stream_get_input_stream(priv->conn))); - priv->output = purple_queued_output_stream_new( - g_io_stream_get_output_stream(priv->conn)); +static void +fb_mqtt_cb_open_error(PurpleSslConnection *ssl, PurpleSslErrorType error, + gpointer data) +{ + const gchar *str; + FbMqtt *mqtt = data; + FbMqttPrivate *priv = mqtt->priv; + GError *err; - fb_mqtt_read_packet(mqtt); + str = purple_ssl_strerror(error); + err = g_error_new_literal(FB_MQTT_SSL_ERROR, error, str); - g_signal_emit_by_name(mqtt, "open"); + /* Do not call purple_ssl_close() from the error_func */ + priv->gsc = NULL; + + g_signal_emit_by_name(mqtt, "error", err); + g_error_free(err); } void @@ -653,29 +622,20 @@ fb_mqtt_open(FbMqtt *mqtt, const gchar *host, gint port) { FbMqttPrivate *priv; PurpleAccount *acc; - GSocketClient *client; - GError *err = NULL; g_return_if_fail(FB_IS_MQTT(mqtt)); priv = mqtt->priv; acc = purple_connection_get_account(priv->gc); fb_mqtt_close(mqtt); + priv->gsc = purple_ssl_connect(acc, host, port, fb_mqtt_cb_open, + fb_mqtt_cb_open_error, mqtt); - client = purple_gio_socket_client_new(acc, &err); - - if (client == NULL) { - fb_mqtt_take_error(mqtt, err, NULL); + if (priv->gsc == NULL) { + fb_mqtt_cb_open_error(NULL, 0, mqtt); return; } - priv->cancellable = g_cancellable_new(); - - g_socket_client_set_tls(client, TRUE); - g_socket_client_connect_to_host_async(client, host, port, - priv->cancellable, fb_mqtt_cb_open, mqtt); - g_object_unref(client); - fb_mqtt_timeout(mqtt); } @@ -711,7 +671,7 @@ fb_mqtt_connected(FbMqtt *mqtt, gboolean error) g_return_val_if_fail(FB_IS_MQTT(mqtt), FALSE); priv = mqtt->priv; - connected = (priv->conn != NULL) && priv->connected; + connected = (priv->gsc != NULL) && priv->connected; if (!connected && error) { fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, diff --git a/pidgin/libpurple/protocols/facebook/mqtt.h b/pidgin/libpurple/protocols/facebook/mqtt.h index 30172eb4..a33b4f52 100644 --- a/pidgin/libpurple/protocols/facebook/mqtt.h +++ b/pidgin/libpurple/protocols/facebook/mqtt.h @@ -107,6 +107,13 @@ */ #define FB_MQTT_ERROR fb_mqtt_error_quark() +/** + * FB_MQTT_SSL_ERROR: + * + * The #GQuark of the domain of MQTT SSL errors. + */ +#define FB_MQTT_SSL_ERROR fb_mqtt_ssl_error_quark() + typedef struct _FbMqtt FbMqtt; typedef struct _FbMqttClass FbMqttClass; typedef struct _FbMqttPrivate FbMqttPrivate; @@ -290,6 +297,16 @@ fb_mqtt_message_get_type(void); GQuark fb_mqtt_error_quark(void); +/** + * fb_mqtt_ssl_error_quark: + * + * Gets the #GQuark of the domain of MQTT SSL errors. + * + * Returns: The #GQuark of the domain. + */ +GQuark +fb_mqtt_ssl_error_quark(void); + /** * fb_mqtt_new: * @gc: The #PurpleConnection. diff --git a/pidgin/libpurple/protocols/facebook/thrift.c b/pidgin/libpurple/protocols/facebook/thrift.c index d33a94e7..c4356f6f 100644 --- a/pidgin/libpurple/protocols/facebook/thrift.c +++ b/pidgin/libpurple/protocols/facebook/thrift.c @@ -21,6 +21,7 @@ #include +#include "glibcompat.h" #include "thrift.h" struct _FbThriftPrivate @@ -32,7 +33,7 @@ struct _FbThriftPrivate guint lastbool; }; -G_DEFINE_TYPE(FbThrift, fb_thrift, G_TYPE_OBJECT); +G_DEFINE_TYPE_WITH_CODE(FbThrift, fb_thrift, G_TYPE_OBJECT, G_ADD_PRIVATE(FbThrift)); static void fb_thrift_dispose(GObject *obj)