Fri, 10 Mar 2023 17:17:53 -0600
Convert PurpleRequestFieldList into a GObject
This also does an `hg cp`, though with all the renaming of the parameter names, maybe that wasn't as useful for tracking the diff.
Also could implement `GListModel`, but it takes arbitrary pointers too, so not right now.
Testing Done:
Compiled, and opened Request Fields from the Demo protocol.
Reviewed at https://reviews.imfreedom.org/r/2336/
/* purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include <glib.h> #include <glib/gi18n-lib.h> #include <gio/gio.h> #include <stdarg.h> #include <string.h> #include <purple.h> #include "util.h" GQuark fb_util_error_quark(void) { static GQuark q = 0; if (G_UNLIKELY(q == 0)) { q = g_quark_from_static_string("fb-util-error-quark"); } return q; } PurpleBuddy * fb_util_account_find_buddy(PurpleAccount *acct, PurpleChatConversation *chat, const gchar *search, GError **error) { const gchar *alias; const gchar *name; GSList *buddies; GSList *l; guint retc; PurpleBuddy *ret = NULL; g_return_val_if_fail(PURPLE_IS_ACCOUNT(acct), NULL); g_return_val_if_fail(search != NULL, NULL); buddies = purple_blist_find_buddies(acct, NULL); for (retc = 0, l = buddies; l != NULL; l = l->next) { name = purple_buddy_get_name(l->data); alias = purple_buddy_get_alias(l->data); if ((chat != NULL) && !purple_chat_conversation_has_user(chat, name)) { continue; } if (g_ascii_strcasecmp(name, search) == 0) { ret = l->data; retc++; } if (g_ascii_strcasecmp(alias, search) == 0) { ret = l->data; retc++; } } if (retc == 0) { g_set_error(error, FB_UTIL_ERROR, FB_UTIL_ERROR_GENERAL, _("Buddy %s not found"), search); } else if (retc > 1) { g_set_error(error, FB_UTIL_ERROR, FB_UTIL_ERROR_GENERAL, _("Buddy name %s is ambiguous"), search); ret = NULL; } g_slist_free(buddies); return ret; } void fb_util_debug(PurpleDebugLevel level, const gchar *format, ...) { va_list ap; va_start(ap, format); fb_util_vdebug(level, format, ap); va_end(ap); } void fb_util_vdebug(PurpleDebugLevel level, const gchar *format, va_list ap) { gboolean unsafe; gboolean verbose; gchar *str; g_return_if_fail(format != NULL); unsafe = (level & FB_UTIL_DEBUG_FLAG_UNSAFE) != 0; verbose = (level & FB_UTIL_DEBUG_FLAG_VERBOSE) != 0; if ((unsafe && !purple_debug_is_unsafe()) || (verbose && !purple_debug_is_verbose())) { return; } /* Ensure all local flags are removed */ level &= ~FB_UTIL_DEBUG_FLAG_ALL; str = g_strdup_vprintf(format, ap); purple_debug(level, "facebook", "%s", str); g_free(str); } void fb_util_debug_misc(const gchar *format, ...) { va_list ap; va_start(ap, format); fb_util_vdebug(PURPLE_DEBUG_MISC, format, ap); va_end(ap); } void fb_util_debug_info(const gchar *format, ...) { va_list ap; va_start(ap, format); fb_util_vdebug(PURPLE_DEBUG_INFO, format, ap); va_end(ap); } void fb_util_debug_warning(const gchar *format, ...) { va_list ap; va_start(ap, format); fb_util_vdebug(PURPLE_DEBUG_WARNING, format, ap); va_end(ap); } void fb_util_debug_error(const gchar *format, ...) { va_list ap; va_start(ap, format); fb_util_vdebug(PURPLE_DEBUG_ERROR, format, ap); va_end(ap); } void fb_util_debug_fatal(const gchar *format, ...) { va_list ap; va_start(ap, format); fb_util_vdebug(PURPLE_DEBUG_FATAL, format, ap); va_end(ap); } void fb_util_debug_hexdump(PurpleDebugLevel level, const GByteArray *bytes, const gchar *format, ...) { gchar c; guint i; guint j; GString *gstr; va_list ap; static const gchar *indent = " "; g_return_if_fail(bytes != NULL); if (format != NULL) { va_start(ap, format); fb_util_vdebug(level, format, ap); va_end(ap); } gstr = g_string_sized_new(80); for (i = 0; i < bytes->len; i += 16) { g_string_append_printf(gstr, "%s%08x ", indent, i); for (j = 0; j < 16; j++) { if ((i + j) < bytes->len) { g_string_append_printf(gstr, "%02x ", bytes->data[i + j]); } else { g_string_append(gstr, " "); } if (j == 7) { g_string_append_c(gstr, ' '); } } g_string_append(gstr, " |"); for (j = 0; (j < 16) && ((i + j) < bytes->len); j++) { c = bytes->data[i + j]; if (!g_ascii_isprint(c) || g_ascii_isspace(c)) { c = '.'; } g_string_append_c(gstr, c); } g_string_append_c(gstr, '|'); fb_util_debug(level, "%s", gstr->str); g_string_erase(gstr, 0, -1); } g_string_append_printf(gstr, "%s%08x", indent, i); fb_util_debug(level, "%s", gstr->str); g_string_free(gstr, TRUE); } gchar * fb_util_get_locale(void) { const gchar * const *langs; const gchar *lang; gchar *chr; guint i; static const gchar chrs[] = {'.', '@'}; langs = g_get_language_names(); lang = langs[0]; if (purple_strequal(lang, "C")) { return g_strdup("en_US"); } for (i = 0; i < G_N_ELEMENTS(chrs); i++) { chr = strchr(lang, chrs[i]); if (chr != NULL) { return g_strndup(lang, chr - lang); } } return g_strdup(lang); } gchar * fb_util_rand_alnum(guint len) { gchar *ret; GRand *rand; guint i; guint j; static const gchar chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789"; static const gsize charc = G_N_ELEMENTS(chars) - 1; g_return_val_if_fail(len > 0, NULL); rand = g_rand_new(); ret = g_new(gchar, len + 1); for (i = 0; i < len; i++) { j = g_rand_int_range(rand, 0, charc); ret[i] = chars[j]; } ret[len] = 0; g_rand_free(rand); return ret; } static void fb_util_request_buddy_ok(gpointer *request_data, PurpleRequestPage *page) { FbUtilRequestBuddyFunc func = request_data[0]; GList *l; GList *select; gpointer data = request_data[2]; GSList *ret = NULL; PurpleBuddy *bdy; PurpleRequestFieldList *field; if (func == NULL) { g_free(request_data); return; } field = PURPLE_REQUEST_FIELD_LIST(purple_request_page_get_field(page, "buddy")); select = purple_request_field_list_get_selected(field); for (l = select; l != NULL; l = l->next) { bdy = purple_request_field_list_get_data(field, l->data); ret = g_slist_prepend(ret, bdy); } ret = g_slist_reverse(ret); func(ret, data); g_slist_free(ret); g_free(request_data); } static void fb_util_request_buddy_cancel(gpointer *request_data, G_GNUC_UNUSED PurpleRequestPage *page) { FbUtilRequestBuddyFunc func = request_data[1]; gpointer data = request_data[2]; if (func != NULL) { func(NULL, data); } g_free(request_data); } static gint fb_buddy_cmp(gconstpointer a, gconstpointer b) { PurpleBuddy *pba, *pbb; gint alias_verdict, name_verdict; gchar *astr, *bstr; pba = PURPLE_BUDDY(a); pbb = PURPLE_BUDDY(b); astr = g_utf8_casefold(purple_buddy_get_alias(pba), -1); bstr = g_utf8_casefold(purple_buddy_get_alias(pbb), -1); alias_verdict = g_utf8_collate(astr, bstr); g_free(astr); g_free(bstr); if (alias_verdict != 0) { return alias_verdict; } astr = g_utf8_casefold(purple_buddy_get_name(pba), -1); bstr = g_utf8_casefold(purple_buddy_get_name(pbb), -1); name_verdict = g_utf8_collate(astr, bstr); g_free(astr); g_free(bstr); return name_verdict; } gpointer fb_util_request_buddy(PurpleConnection *gc, const gchar *title, const gchar *primary, const gchar *secondary, GSList *select, gboolean multi, GCallback ok_cb, GCallback cancel_cb, gpointer data) { const gchar *alias; const gchar *name; gchar *str; GList *items = NULL; gpointer *request_data; GSList *buddies; GSList *l; PurpleAccount *acct; PurpleRequestCommonParameters *cpar; PurpleRequestField *field; PurpleRequestFieldList *list; PurpleRequestGroup *group; PurpleRequestPage *page; request_data = g_new0(gpointer, 3); request_data[0] = ok_cb; request_data[1] = cancel_cb; request_data[2] = data; acct = purple_connection_get_account(gc); buddies = purple_blist_find_buddies(acct, NULL); buddies = g_slist_sort(buddies, fb_buddy_cmp); page = purple_request_page_new(); group = purple_request_group_new(NULL); purple_request_page_add_group(page, group); field = purple_request_field_list_new("buddy", NULL); list = PURPLE_REQUEST_FIELD_LIST(field); purple_request_field_list_set_multi_select(list, multi); purple_request_field_set_required(field, TRUE); purple_request_group_add_field(group, field); for (l = buddies; l != NULL; l = l->next) { name = purple_buddy_get_name(l->data); alias = purple_buddy_get_alias(l->data); str = g_strdup_printf("%s (%s)", alias, name); purple_request_field_list_add_icon(list, str, NULL, l->data); g_free(str); } for (l = select; l != NULL; l = l->next) { name = purple_buddy_get_name(l->data); alias = purple_buddy_get_alias(l->data); str = g_strdup_printf("%s (%s)", alias, name); items = g_list_append(items, str); } purple_request_field_list_set_selected(list, items); g_slist_free(buddies); g_list_free_full(items, g_free); cpar = purple_request_cpar_from_connection(gc); return purple_request_fields(gc, title, primary, secondary, page, _("Ok"), G_CALLBACK(fb_util_request_buddy_ok), _("Cancel"), G_CALLBACK(fb_util_request_buddy_cancel), cpar, request_data); } void fb_util_serv_got_im(PurpleConnection *gc, const gchar *who, const gchar *text, PurpleMessageFlags flags, guint64 timestamp) { GDateTime *dt = NULL; const gchar *name, *me; PurpleAccount *acct; PurpleContactInfo *info = NULL; PurpleConversation *conv; PurpleConversationManager *manager; PurpleMessage *msg; if (!(flags & PURPLE_MESSAGE_SEND)) { purple_serv_got_im(gc, who, text, flags, timestamp); return; } acct = purple_connection_get_account(gc); info = PURPLE_CONTACT_INFO(acct); manager = purple_conversation_manager_get_default(); conv = purple_conversation_manager_find_im(manager, acct, who); if (conv == NULL) { conv = purple_im_conversation_new(acct, who); } me = purple_contact_info_get_name_for_display(info); name = purple_contact_info_get_username(info); msg = purple_message_new_outgoing(acct, me, name, text, flags); dt = g_date_time_new_from_unix_local((gint64)timestamp); purple_message_set_timestamp(msg, dt); g_date_time_unref(dt); purple_conversation_write_message(conv, msg); g_object_unref(G_OBJECT(msg)); } void fb_util_serv_got_chat_in(PurpleConnection *gc, gint id, const gchar *who, const gchar *text, PurpleMessageFlags flags, guint64 timestamp) { GDateTime *dt = NULL; const gchar *name; PurpleAccount *acct; PurpleContactInfo *info = NULL; PurpleConversation *conv; PurpleConversationManager *manager; PurpleMessage *msg; const gchar *me; if (!(flags & PURPLE_MESSAGE_SEND)) { purple_serv_got_chat_in(gc, id, who, flags, text, timestamp); return; } acct = purple_connection_get_account(gc); info = PURPLE_CONTACT_INFO(acct); manager = purple_conversation_manager_get_default(); conv = purple_conversation_manager_find_chat_by_id(manager, acct, id); me = purple_contact_info_get_name_for_display(info); name = purple_contact_info_get_username(info); msg = purple_message_new_outgoing(acct, me, name, text, flags); dt = g_date_time_new_from_unix_local((gint64)timestamp); purple_message_set_timestamp(msg, dt); g_date_time_unref(dt); purple_conversation_write_message(conv, msg); g_object_unref(G_OBJECT(msg)); } gboolean fb_util_strtest(const gchar *str, GAsciiType type) { gsize i; gsize size; guchar c; g_return_val_if_fail(str != NULL, FALSE); size = strlen(str); for (i = 0; i < size; i++) { c = (guchar) str[i]; if ((g_ascii_table[c] & type) == 0) { return FALSE; } } return TRUE; } gboolean fb_util_zlib_test(const GByteArray *bytes) { guint8 b0; guint8 b1; g_return_val_if_fail(bytes != NULL, FALSE); if (bytes->len < 2) { return FALSE; } b0 = *(bytes->data + 0); b1 = *(bytes->data + 1); return ((((b0 << 8) | b1) % 31) == 0) && /* Check the header */ ((b0 & 0x0F) == 8 /* Z_DEFLATED */); /* Check the method */ } static GByteArray * fb_util_zlib_conv(GConverter *conv, const GByteArray *bytes, GError **error) { GByteArray *ret; GConverterResult res; gsize cize = 0; gsize rize; gsize wize; guint8 data[1024]; ret = g_byte_array_new(); while (TRUE) { rize = 0; wize = 0; res = g_converter_convert(conv, bytes->data + cize, bytes->len - cize, data, sizeof data, G_CONVERTER_INPUT_AT_END, &rize, &wize, error); switch (res) { case G_CONVERTER_CONVERTED: g_byte_array_append(ret, data, wize); cize += rize; break; case G_CONVERTER_ERROR: g_byte_array_free(ret, TRUE); return NULL; case G_CONVERTER_FINISHED: g_byte_array_append(ret, data, wize); return ret; default: break; } } } GByteArray * fb_util_zlib_deflate(const GByteArray *bytes, GError **error) { GByteArray *ret; GZlibCompressor *conv; conv = g_zlib_compressor_new(G_ZLIB_COMPRESSOR_FORMAT_ZLIB, -1); ret = fb_util_zlib_conv(G_CONVERTER(conv), bytes, error); g_object_unref(conv); return ret; } GByteArray * fb_util_zlib_inflate(const GByteArray *bytes, GError **error) { GByteArray *ret; GZlibDecompressor *conv; conv = g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_ZLIB); ret = fb_util_zlib_conv(G_CONVERTER(conv), bytes, error); g_object_unref(conv); return ret; }