pidgin/pidgin

00a9ef389aab
Merge upstream, resolve conflicts
  • +7 -0
    ChangeLog.API
  • +0 -2
    finch/gntblist.c
  • +2 -2
    finch/gntrequest.c
  • +0 -1
    finch/plugins/gntclipboard.c
  • +1 -3
    libpurple/buddylist.c
  • +1 -1
    libpurple/core.c
  • +2 -1
    libpurple/image-store.c
  • +1 -2
    libpurple/keyring.c
  • +5 -7
    libpurple/log.c
  • +59 -0
    libpurple/network.c
  • +18 -0
    libpurple/network.h
  • +2 -1
    libpurple/plugins/autoaccept.c
  • +1 -1
    libpurple/plugins/keyrings/internalkeyring.c
  • +76 -71
    libpurple/plugins/log_reader.c
  • +1 -1
    libpurple/presence.c
  • +1 -1
    libpurple/protocols.c
  • +2 -5
    libpurple/protocols/bonjour/bonjour_ft.c
  • +1 -1
    libpurple/protocols/bonjour/mdns_dns_sd.c
  • +1 -1
    libpurple/protocols/facebook/api.c
  • +1 -1
    libpurple/protocols/gg/blist.c
  • +1 -1
    libpurple/protocols/gg/blist.h
  • +8 -9
    libpurple/protocols/gg/edisc.c
  • +0 -2
    libpurple/protocols/irc/cmds.c
  • +3 -8
    libpurple/protocols/irc/msgs.c
  • +12 -1
    libpurple/protocols/jabber/auth_cyrus.c
  • +0 -6
    libpurple/protocols/jabber/bosh.c
  • +0 -1
    libpurple/protocols/jabber/google/gmail.c
  • +1 -1
    libpurple/protocols/jabber/google/google_presence.c
  • +246 -291
    libpurple/protocols/jabber/jabber.c
  • +5 -5
    libpurple/protocols/jabber/jabber.h
  • +3 -0
    libpurple/protocols/jabber/namespaces.h
  • +9 -4
    libpurple/protocols/jabber/si.c
  • +130 -160
    libpurple/protocols/novell/nmconn.c
  • +16 -70
    libpurple/protocols/novell/nmconn.h
  • +217 -75
    libpurple/protocols/novell/nmevent.c
  • +45 -34
    libpurple/protocols/novell/nmuser.c
  • +1 -0
    libpurple/protocols/novell/nmuser.h
  • +44 -35
    libpurple/protocols/novell/novell.c
  • +5 -4
    libpurple/protocols/sametime/sametime.c
  • +1 -1
    libpurple/protocols/silc/ops.c
  • +68 -52
    libpurple/protocols/silc/silc.c
  • +3 -3
    libpurple/protocols/silc/silcpurple.h
  • +1 -3
    libpurple/protocols/simple/simple.c
  • +0 -1
    libpurple/protocols/zephyr/Zinternal.c
  • +0 -22
    libpurple/protocols/zephyr/zephyr.c
  • +3 -3
    libpurple/upnp.c
  • +1 -490
    libpurple/util.c
  • +0 -130
    libpurple/util.h
  • +1 -1
    meson.build
  • +2 -1
    pidgin/gtkblist.c
  • +3 -3
    pidgin/gtkconv.c
  • +2 -1
    pidgin/gtkdialogs.c
  • +2 -4
    pidgin/gtknotify.c
  • +12 -8
    pidgin/gtkprefs.c
  • +1 -2
    pidgin/gtkrequest.c
  • +7 -12
    pidgin/gtkutils.c
  • +3 -2
    pidgin/gtkxfer.c
  • +1 -1
    pidgin/libpidgin.c
  • +1 -1
    pidgin/pidgincontactcompletion.c
  • +2 -0
    pidgin/pidgintooltip.c
  • +0 -1
    pidgin/plugins/spellchk.c
  • --- a/ChangeLog.API Mon Nov 04 10:00:14 2019 +0300
    +++ b/ChangeLog.API Mon Nov 04 14:43:49 2019 +0300
    @@ -393,6 +393,7 @@
    * purple_buddy_icons_has_custom_icon
    * purple_buddy_icons_find_custom_icon
    * purple_buddy_icons_set_custom_icon
    + * purple_build_dir. Use g_mkdir_with_parents instead
    * purple_certificate_check_signature_chain_with_failing. Use
    purple_certificate_check_signature_chain, instead
    * purple_certificate_display_x509. Use purple_request_certificate,
    @@ -416,10 +417,12 @@
    event loop yourself. See GLib Main Event Loop docs.
    * PurpleEventLoopUiOps. Manually drive the GLib event loop
    yourself. See GLib Main Event Loop docs.
    + * purple_get_host_name. Use g_get_host_name, instead.
    * purple_get_tzoff_str(). Use g_date_time_format, instead.
    * purple_ip_address_is_valid, purple_ipv4_address_is_valid, and
    purple_ipv6_address_is_valid. Use g_hostname_is_ip_address()
    or #GInetAddress instead.
    + * purple_markup_extract_info_field
    * PurpleMimeDocument, PurpleMimePart,
    purple_mime_document_new, purple_mime_document_free,
    purple_mime_document_parse, purple_mime_document_parsen,
    @@ -496,6 +499,9 @@
    * purple_status_type_set_primary_attr
    * purple_strlcat
    * purple_strlcpy
    + * purple_str_binary_to_ascii
    + * purple_str_has_prefix. Use g_str_has_prefix instead
    + * purple_str_has_suffix. Use g_str_has_suffix instead
    * purple_str_size_to_units. Use g_format_size() instead.
    * purple_timeout_*. Use g_timeout_* or g_idle_* instead.
    * purple_txt_cancel
    @@ -508,6 +514,7 @@
    purple_util_fetch_url_request, instead.
    * purple_util_get_image_checksum. Use
    g_compute_checksum_for_data(G_CHECKSUM_SHA1, ...), instead.
    + * purple_uts35_to_str
    * PurpleCertificateVerificationStatus.PURPLE_CERTIFICATE_INVALID
    * PurpleConnectionUiOps.report_disconnect_reason
    * PurplePluginProtocolInfo.add_buddy_with_invite
    --- a/finch/gntblist.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/finch/gntblist.c Mon Nov 04 14:43:49 2019 +0300
    @@ -2066,8 +2066,6 @@
    PURPLE_SIGNAL_PRIORITY_HIGHEST);
    finch_blist_install_manager(&default_manager);
    -
    - return;
    }
    static gboolean
    --- a/finch/gntrequest.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/finch/gntrequest.c Mon Nov 04 14:43:49 2019 +0300
    @@ -433,9 +433,9 @@
    purple_request_field_string_get_default_value(field));
    gnt_entry_set_masked(GNT_ENTRY(entry),
    purple_request_field_string_is_masked(field));
    - if (hint && purple_str_has_prefix(hint, "screenname")) {
    + if (hint && g_str_has_prefix(hint, "screenname")) {
    PurpleBlistNode *node = purple_blist_get_default_root();
    - gboolean offline = purple_str_has_suffix(hint, "all");
    + gboolean offline = g_str_has_suffix(hint, "all");
    for (; node; node = purple_blist_node_next(node, offline)) {
    if (!PURPLE_IS_BUDDY(node))
    continue;
    --- a/finch/plugins/gntclipboard.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/finch/plugins/gntclipboard.c Mon Nov 04 14:43:49 2019 +0300
    @@ -90,7 +90,6 @@
    return;
    }
    }
    - return;
    }
    static void
    --- a/libpurple/buddylist.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/buddylist.c Mon Nov 04 14:43:49 2019 +0300
    @@ -727,9 +727,7 @@
    buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
    NULL, (GDestroyNotify)g_hash_table_destroy);
    - groups_cache = g_hash_table_new_full((GHashFunc)g_str_hash,
    - (GEqualFunc)g_str_equal,
    - (GDestroyNotify)g_free, NULL);
    + groups_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    for (account = purple_accounts_get_all(); account != NULL; account = account->next)
    {
    --- a/libpurple/core.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/core.c Mon Nov 04 14:43:49 2019 +0300
    @@ -249,7 +249,7 @@
    purple_log_uninit();
    _purple_message_uninit();
    /* Everything after util_uninit cannot try to write things to the
    - * confdir nor use purple_escape_js
    + * confdir.
    */
    purple_util_uninit();
    --- a/libpurple/image-store.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/image-store.c Mon Nov 04 14:43:49 2019 +0300
    @@ -169,8 +169,9 @@
    g_return_val_if_fail(uri != NULL, NULL);
    - if (!purple_str_has_prefix(uri, PURPLE_IMAGE_STORE_PROTOCOL))
    + if (!g_str_has_prefix(uri, PURPLE_IMAGE_STORE_PROTOCOL)) {
    return NULL;
    + }
    uri += sizeof(PURPLE_IMAGE_STORE_PROTOCOL) - 1;
    if (uri[0] == '-')
    --- a/libpurple/keyring.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/keyring.c Mon Nov 04 14:43:49 2019 +0300
    @@ -1287,8 +1287,7 @@
    GPluginPluginInfo *info =
    GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin));
    - if (!purple_str_has_prefix(gplugin_plugin_info_get_id(info),
    - "keyring-")) {
    + if (!g_str_has_prefix(gplugin_plugin_info_get_id(info), "keyring-")) {
    continue;
    }
    --- a/libpurple/log.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/log.c Mon Nov 04 14:43:49 2019 +0300
    @@ -871,7 +871,7 @@
    if (dir == NULL)
    return;
    - purple_build_dir (dir, S_IRUSR | S_IWUSR | S_IXUSR);
    + g_mkdir_with_parents(dir, S_IRUSR | S_IWUSR | S_IXUSR);
    dt = g_date_time_to_local(log->time);
    tz = purple_escape_filename(g_date_time_get_timezone_abbreviation(dt));
    @@ -927,9 +927,8 @@
    while ((filename = g_dir_read_name(dir)))
    {
    - if (purple_str_has_suffix(filename, ext) &&
    - strlen(filename) >= (17 + strlen(ext)))
    - {
    + if (g_str_has_suffix(filename, ext) &&
    + strlen(filename) >= (17 + strlen(ext))) {
    PurpleLog *log;
    PurpleLogCommonLoggerData *data;
    GDateTime *stamp = purple_str_to_date_time(purple_unescape_filename(filename), FALSE);
    @@ -971,9 +970,8 @@
    while ((filename = g_dir_read_name(dir)))
    {
    - if (purple_str_has_suffix(filename, ext) &&
    - strlen(filename) >= (17 + strlen(ext)))
    - {
    + if (g_str_has_suffix(filename, ext) &&
    + strlen(filename) >= (17 + strlen(ext))) {
    char *tmp = g_build_filename(path, filename, NULL);
    GStatBuf st;
    if (g_stat(tmp, &st))
    --- a/libpurple/network.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/network.c Mon Nov 04 14:43:49 2019 +0300
    @@ -150,6 +150,29 @@
    return "0.0.0.0";
    }
    +static gchar *
    +purple_network_get_local_system_ip_from_gio(GSocketConnection *sockconn)
    +{
    + GSocketAddress *addr;
    + GInetSocketAddress *inetsockaddr;
    + gchar *ip;
    +
    + addr = g_socket_connection_get_local_address(sockconn, NULL);
    + if ((inetsockaddr = G_INET_SOCKET_ADDRESS(addr)) != NULL) {
    + GInetAddress *inetaddr =
    + g_inet_socket_address_get_address(inetsockaddr);
    + if (g_inet_address_get_family(inetaddr) == G_SOCKET_FAMILY_IPV4 &&
    + !g_inet_address_get_is_loopback(inetaddr)) {
    + ip = g_inet_address_to_string(inetaddr);
    + g_object_unref(addr);
    + return ip;
    + }
    + }
    + g_object_unref(addr);
    +
    + return g_strdup("0.0.0.0");
    +}
    +
    GList *
    purple_network_get_all_local_system_ips(void)
    {
    @@ -286,6 +309,42 @@
    return purple_network_get_local_system_ip(fd);
    }
    +gchar *
    +purple_network_get_my_ip_from_gio(GSocketConnection *sockconn)
    +{
    + const gchar *ip = NULL;
    + PurpleStunNatDiscovery *stun;
    +
    + /* Check if the user specified an IP manually */
    + if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
    + ip = purple_network_get_public_ip();
    + /* Make sure the IP address entered by the user is valid */
    + if ((ip != NULL) && (purple_network_is_ipv4(ip))) {
    + return g_strdup(ip);
    + }
    + } else {
    + /* Check if STUN discovery was already done */
    + stun = purple_stun_discover(NULL);
    + if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED)) {
    + return g_strdup(stun->publicip);
    + }
    +
    + /* Attempt to get the IP from a NAT device using UPnP */
    + ip = purple_upnp_get_public_ip();
    + if (ip != NULL) {
    + return g_strdup(ip);
    + }
    +
    + /* Attempt to get the IP from a NAT device using NAT-PMP */
    + ip = purple_pmp_get_public_ip();
    + if (ip != NULL) {
    + return g_strdup(ip);
    + }
    + }
    +
    + /* Just fetch the IP of the local system */
    + return purple_network_get_local_system_ip_from_gio(sockconn);
    +}
    static void
    purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
    --- a/libpurple/network.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/network.h Mon Nov 04 14:43:49 2019 +0300
    @@ -29,6 +29,7 @@
    */
    #include <glib.h>
    +#include <gio/gio.h>
    G_BEGIN_DECLS
    @@ -113,6 +114,23 @@
    const char *purple_network_get_my_ip(int fd);
    /**
    + * purple_network_get_my_ip_from_gio:
    + * @sockconn: The socket connection to use to help figure out the IP, or %NULL.
    + *
    + * Returns the IP address that should be used anywhere a
    + * public IP addresses is needed (listening for an incoming
    + * file transfer, etc).
    + *
    + * If the user has manually specified an IP address via
    + * preferences, then this IP is returned. Otherwise the
    + * IP address returned by purple_network_get_local_system_ip_from_gio()
    + * is returned.
    + *
    + * Returns: The local IP address to be used.
    + */
    +gchar *purple_network_get_my_ip_from_gio(GSocketConnection *sockconn);
    +
    +/**
    * purple_network_listen:
    * @port: The port number to bind to. Must be greater than 0.
    * @socket_family: The protocol family of the socket. This should be
    --- a/libpurple/plugins/autoaccept.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/plugins/autoaccept.c Mon Nov 04 14:43:49 2019 +0300
    @@ -54,8 +54,9 @@
    {
    if (!g_file_test(dir, G_FILE_TEST_IS_DIR))
    {
    - if (purple_build_dir(dir, S_IRUSR | S_IWUSR | S_IXUSR))
    + if (g_mkdir_with_parents(dir, S_IRUSR | S_IWUSR | S_IXUSR)) {
    return FALSE;
    + }
    }
    return TRUE;
    --- a/libpurple/plugins/keyrings/internalkeyring.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/plugins/keyrings/internalkeyring.c Mon Nov 04 14:43:49 2019 +0300
    @@ -849,7 +849,7 @@
    *mode = "ciphertext";
    *data = ciphertext;
    - *destroy = (GDestroyNotify)g_free;
    + *destroy = g_free;
    return TRUE;
    }
    --- a/libpurple/plugins/log_reader.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/plugins/log_reader.c Mon Nov 04 14:43:49 2019 +0300
    @@ -100,9 +100,11 @@
    const gchar *file;
    while ((file = g_dir_read_name(dir))) {
    - if (!purple_str_has_prefix(file, sn))
    + if (!g_str_has_prefix(file, sn)) {
    continue;
    - if (purple_str_has_suffix(file, ".html") || purple_str_has_suffix(file, ".AdiumHTMLLog")) {
    + }
    + if (g_str_has_suffix(file, ".html") ||
    + g_str_has_suffix(file, ".AdiumHTMLLog")) {
    GDateTime *dt;
    gint year, month, day, hour, minute, second;
    const char *date = file;
    @@ -165,7 +167,7 @@
    list = g_list_prepend(list, log);
    }
    - } else if (purple_str_has_suffix(file, ".adiumLog")) {
    + } else if (g_str_has_suffix(file, ".adiumLog")) {
    GDateTime *dt;
    gint year, month, day, hour, minute, second;
    const char *date = file;
    @@ -269,8 +271,7 @@
    /* This problem only seems to show up on Windows.
    * The BOM is displaying as a space at the beginning of the log.
    */
    - if (purple_str_has_prefix(read, "\xef\xbb\xbf"))
    - {
    + if (g_str_has_prefix(read, "\xef\xbb\xbf")) {
    /* FIXME: This feels so wrong... */
    char *temp = g_strdup(&(read[3]));
    g_free(read);
    @@ -578,8 +579,9 @@
    while ((name = g_dir_read_name(dir))) {
    const char *c = name;
    - if (!purple_str_has_prefix(c, username))
    + if (!g_str_has_prefix(c, username)) {
    continue;
    + }
    c += strlen(username);
    while (*c) {
    @@ -631,8 +633,9 @@
    const char *c = name;
    gchar *full_path;
    - if (!purple_str_has_prefix(c, username))
    + if (!g_str_has_prefix(c, username)) {
    continue;
    + }
    c += strlen(username);
    while (*c) {
    @@ -862,16 +865,18 @@
    * friendly name or alias. For this test, "match" is defined as:
    * ^(friendly_name|alias)([^a-zA-Z0-9].*)?$
    */
    - from_name_matches = (purple_str_has_prefix(from_name, friendly_name) &&
    - !isalnum(*(from_name + friendly_name_length))) ||
    - (purple_str_has_prefix(from_name, alias) &&
    - !isalnum(*(from_name + alias_length)));
    -
    - to_name_matches = to_name != NULL && (
    - (purple_str_has_prefix(to_name, friendly_name) &&
    - !isalnum(*(to_name + friendly_name_length))) ||
    - (purple_str_has_prefix(to_name, alias) &&
    - !isalnum(*(to_name + alias_length))));
    + from_name_matches =
    + (g_str_has_prefix(from_name, friendly_name) &&
    + !isalnum(*(from_name + friendly_name_length))) ||
    + (g_str_has_prefix(from_name, alias) &&
    + !isalnum(*(from_name + alias_length)));
    +
    + to_name_matches =
    + to_name != NULL &&
    + ((g_str_has_prefix(to_name, friendly_name) &&
    + !isalnum(*(to_name + friendly_name_length))) ||
    + (g_str_has_prefix(to_name, alias) &&
    + !isalnum(*(to_name + alias_length))));
    if (from_name_matches) {
    if (!to_name_matches) {
    @@ -901,15 +906,13 @@
    * matches their alias. For this test, "match" is
    * defined as: ^alias([^a-zA-Z0-9].*)?$
    */
    - from_name_matches = (purple_str_has_prefix(
    - from_name, alias) &&
    - !isalnum(*(from_name +
    - alias_length)));
    -
    - to_name_matches = to_name && (purple_str_has_prefix(
    - to_name, alias) &&
    - !isalnum(*(to_name +
    - alias_length)));
    + from_name_matches =
    + (g_str_has_prefix(from_name, alias) &&
    + !isalnum(*(from_name + alias_length)));
    +
    + to_name_matches = to_name &&
    + (g_str_has_prefix(to_name, alias) &&
    + !isalnum(*(to_name + alias_length)));
    g_free(alias);
    @@ -929,17 +932,17 @@
    * this test, "match" is defined as:
    * ^friendly_name([^a-zA-Z0-9].*)?$
    */
    - from_name_matches = (purple_str_has_prefix(
    - from_name,
    - server_alias) &&
    - !isalnum(*(from_name +
    - friendly_name_length)));
    -
    - to_name_matches = to_name && (
    - (purple_str_has_prefix(
    - to_name, server_alias) &&
    - !isalnum(*(to_name +
    - friendly_name_length))));
    + from_name_matches =
    + (g_str_has_prefix(from_name,
    + server_alias) &&
    + !isalnum(*(from_name +
    + friendly_name_length)));
    +
    + to_name_matches =
    + to_name &&
    + ((g_str_has_prefix(to_name, server_alias) &&
    + !isalnum(*(to_name +
    + friendly_name_length))));
    if (from_name_matches) {
    if (!to_name_matches) {
    @@ -1142,7 +1145,7 @@
    }
    *c = '\0';
    - if (purple_str_has_prefix(line, "Session Close ")) {
    + if (g_str_has_prefix(line, "Session Close ")) {
    if (data && !data->length) {
    if (!(data->length = last_line_offset - data->offset)) {
    /* This log had no data, so we remove it. */
    @@ -1156,7 +1159,7 @@
    }
    }
    } else if (line[0] && line[1] && line[2] &&
    - purple_str_has_prefix(&line[3], "sion Start ")) {
    + g_str_has_prefix(&line[3], "sion Start ")) {
    /* The conditional is to make sure we're not reading off
    * the end of the string. We don't want strlen(), as that'd
    * have to count the whole string needlessly.
    @@ -1418,17 +1421,18 @@
    line++;
    }
    - if (purple_str_has_prefix(line, "*** ")) {
    + if (g_str_has_prefix(line, "*** ")) {
    line += (sizeof("*** ") - 1);
    g_string_append(formatted, "<b>");
    footer = "</b>";
    - if (purple_str_has_prefix(line, "NOTE: This user is offline.")) {
    + if (g_str_has_prefix(line, "NOTE: This user is offline.")) {
    line = _("User is offline.");
    - } else if (purple_str_has_prefix(line,
    - "NOTE: Your status is currently set to ")) {
    + } else if (g_str_has_prefix(
    + line,
    + "NOTE: Your status is currently set to ")) {
    line += (sizeof("NOTE: ") - 1);
    - } else if (purple_str_has_prefix(line, "Auto-response sent to ")) {
    + } else if (g_str_has_prefix(line, "Auto-response sent to ")) {
    g_string_append(formatted, _("Auto-response sent:"));
    while (*line && *line != ':')
    line++;
    @@ -1462,8 +1466,8 @@
    g_string_append(formatted, log->name);
    line = " logged in.";
    - } else if (purple_str_has_prefix(line,
    - "One or more messages may have been undeliverable.")) {
    + } else if (g_str_has_prefix(line, "One or more messages may "
    + "have been undeliverable.")) {
    g_string_append(formatted,
    "<span style=\"color: #ff0000;\">");
    @@ -1472,8 +1476,8 @@
    "undeliverable."));
    line = "";
    footer = "</span></b>";
    - } else if (purple_str_has_prefix(line,
    - "You have been disconnected.")) {
    + } else if (g_str_has_prefix(line,
    + "You have been disconnected.")) {
    g_string_append(formatted,
    "<span style=\"color: #ff0000;\">");
    @@ -1481,8 +1485,8 @@
    _("You were disconnected from the server."));
    line = "";
    footer = "</span></b>";
    - } else if (purple_str_has_prefix(line,
    - "You are currently disconnected.")) {
    + } else if (g_str_has_prefix(
    + line, "You are currently disconnected.")) {
    g_string_append(formatted,
    "<span style=\"color: #ff0000;\">");
    @@ -1490,15 +1494,16 @@
    "will not be received unless you are "
    "logged in.");
    footer = "</span></b>";
    - } else if (purple_str_has_prefix(line,
    - "Your previous message has not been sent.")) {
    + } else if (g_str_has_prefix(line, "Your previous message has "
    + "not been sent.")) {
    g_string_append(formatted,
    "<span style=\"color: #ff0000;\">");
    - if (purple_str_has_prefix(line,
    - "Your previous message has not been sent. "
    - "Reason: Maximum length exceeded.")) {
    + if (g_str_has_prefix(
    + line,
    + "Your previous message has not been sent. "
    + "Reason: Maximum length exceeded.")) {
    g_string_append(formatted,
    _("Message could not be sent because "
    @@ -1514,7 +1519,7 @@
    footer = "</span></b>";
    }
    - } else if (purple_str_has_prefix(line, data->their_nickname)) {
    + } else if (g_str_has_prefix(line, data->their_nickname)) {
    if (buddy != NULL) {
    const char *alias = purple_buddy_get_alias(buddy);
    @@ -1684,8 +1689,8 @@
    gboolean add_new_log = FALSE;
    if (c && *c) {
    - if (purple_str_has_prefix(c, QIP_LOG_IN_MESSAGE) ||
    - purple_str_has_prefix(c, QIP_LOG_OUT_MESSAGE)) {
    + if (g_str_has_prefix(c, QIP_LOG_IN_MESSAGE) ||
    + g_str_has_prefix(c, QIP_LOG_OUT_MESSAGE)) {
    char *tmp;
    @@ -1841,12 +1846,12 @@
    while (c && *c) {
    gboolean is_in_message = FALSE;
    - if (purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC) ||
    - purple_str_has_prefix(line, QIP_LOG_OUT_MESSAGE_ESC)) {
    + if (g_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC) ||
    + g_str_has_prefix(line, QIP_LOG_OUT_MESSAGE_ESC)) {
    char *tmp;
    - is_in_message = purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC);
    + is_in_message = g_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC);
    /* find EOL */
    c = strchr(c, '\n');
    @@ -2010,7 +2015,7 @@
    gint year, month, day, hour, minute, second;
    GDateTime *dt;
    while (c && *c) {
    - if (purple_str_has_prefix(c, AMSN_LOG_CONV_START)) {
    + if (g_str_has_prefix(c, AMSN_LOG_CONV_START)) {
    char month_str[4];
    if (sscanf(c + strlen(AMSN_LOG_CONV_START),
    "%u %3s %u %u:%u:%u",
    @@ -2027,7 +2032,7 @@
    offset = c - contents;
    start_log = c;
    }
    - } else if (purple_str_has_prefix(c, AMSN_LOG_CONV_END) && found_start) {
    + } else if (g_str_has_prefix(c, AMSN_LOG_CONV_END) && found_start) {
    data = g_new0(struct amsn_logger_data, 1);
    data->path = g_strdup(filename);
    data->offset = offset;
    @@ -2215,7 +2220,7 @@
    if (!end)
    break;
    *end = '\0';
    - if (purple_str_has_prefix(start, AMSN_LOG_FORMAT_TAG) && in_span) {
    + if (g_str_has_prefix(start, AMSN_LOG_FORMAT_TAG) && in_span) {
    /* New format for this line */
    g_string_append(formatted, "</span><br>");
    in_span = FALSE;
    @@ -2244,19 +2249,19 @@
    old_tag = tag + 7; /* C + xxxxxx */
    } else {
    /* |"Lxxx is a 3-digit colour code */
    - if (purple_str_has_prefix(tag, "RED")) {
    + if (g_str_has_prefix(tag, "RED")) {
    g_string_append(formatted, "<span style=\"color: red;\">");
    in_span = TRUE;
    - } else if (purple_str_has_prefix(tag, "GRA")) {
    + } else if (g_str_has_prefix(tag, "GRA")) {
    g_string_append(formatted, "<span style=\"color: gray;\">");
    in_span = TRUE;
    - } else if (purple_str_has_prefix(tag, "NOR")) {
    + } else if (g_str_has_prefix(tag, "NOR")) {
    g_string_append(formatted, "<span style=\"color: black;\">");
    in_span = TRUE;
    - } else if (purple_str_has_prefix(tag, "ITA")) {
    + } else if (g_str_has_prefix(tag, "ITA")) {
    g_string_append(formatted, "<span style=\"color: blue;\">");
    in_span = TRUE;
    - } else if (purple_str_has_prefix(tag, "GRE")) {
    + } else if (g_str_has_prefix(tag, "GRE")) {
    g_string_append(formatted, "<span style=\"color: darkgreen;\">");
    in_span = TRUE;
    } else {
    @@ -2429,7 +2434,7 @@
    *temp = '\0';
    /* Set path. */
    - if (purple_str_has_suffix(value, "trillian.exe")) {
    + if (g_str_has_suffix(value, "trillian.exe")) {
    value[strlen(value) - (sizeof("trillian.exe") - 1)] = '\0';
    path = g_build_filename(value, "users", "default", "talk.ini", NULL);
    }
    @@ -2472,7 +2477,7 @@
    *cursor = '\0';
    /* XXX: This assumes the first Directory key is under [Logging]. */
    - if (purple_str_has_prefix(line, "Directory=")) {
    + if (g_str_has_prefix(line, "Directory=")) {
    line += (sizeof("Directory=") - 1);
    g_strchomp(line);
    purple_prefs_add_string(
    --- a/libpurple/presence.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/presence.c Mon Nov 04 14:43:49 2019 +0300
    @@ -462,7 +462,7 @@
    purple_presence_get_instance_private(PURPLE_PRESENCE(object));
    if (priv->statuses) {
    - g_list_free_full(priv->statuses, (GDestroyNotify)g_object_unref);
    + g_list_free_full(priv->statuses, g_object_unref);
    priv->statuses = NULL;
    }
    --- a/libpurple/protocols.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols.c Mon Nov 04 14:43:49 2019 +0300
    @@ -690,7 +690,7 @@
    void *handle = purple_protocols_get_handle();
    protocols = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
    - (GDestroyNotify)g_object_unref);
    + g_object_unref);
    purple_signal_register(handle, "protocol-added",
    purple_marshal_VOID__POINTER,
    --- a/libpurple/protocols/bonjour/bonjour_ft.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/bonjour/bonjour_ft.c Mon Nov 04 14:43:49 2019 +0300
    @@ -33,7 +33,7 @@
    bonjour_xfer_init(PurpleXfer *xfer);
    static void
    bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
    - const goffset filesize, const char *filename, int option);
    + goffset filesize, const char *filename, int option);
    /* Look for specific xfer handle */
    static unsigned int next_id = 0;
    @@ -727,7 +727,7 @@
    static void
    bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
    - const goffset filesize, const char *filename, int option)
    + goffset filesize, const char *filename, int option)
    {
    PurpleXfer *xfer;
    XepXfer *xf;
    @@ -877,7 +877,6 @@
    default:
    break;
    }
    - return;
    }
    static void
    @@ -943,8 +942,6 @@
    bonjour_bytestreams_listen, xfer);
    if (xf->listen_data == NULL)
    purple_xfer_cancel_local(xfer);
    -
    - return;
    }
    static void
    --- a/libpurple/protocols/bonjour/mdns_dns_sd.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/bonjour/mdns_dns_sd.c Mon Nov 04 14:43:49 2019 +0300
    @@ -460,7 +460,7 @@
    gboolean _mdns_init_session(BonjourDnsSd *data) {
    data->mdns_impl_data = g_new0(Win32SessionImplData, 1);
    - bonjour_dns_sd_set_jid(data->account, purple_get_host_name());
    + bonjour_dns_sd_set_jid(data->account, g_get_host_name());
    return TRUE;
    }
    --- a/libpurple/protocols/facebook/api.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/facebook/api.c Mon Nov 04 14:43:49 2019 +0300
    @@ -2339,7 +2339,7 @@
    g_signal_emit_by_name(api, "contacts-delta", added, removed);
    g_slist_free_full(added, (GDestroyNotify) fb_api_user_free);
    - g_slist_free_full(removed, (GDestroyNotify) g_free);
    + g_slist_free_full(removed, g_free);
    g_list_free(elms);
    json_array_unref(arr);
    --- a/libpurple/protocols/gg/blist.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/gg/blist.c Mon Nov 04 14:43:49 2019 +0300
    @@ -187,7 +187,7 @@
    }
    /* }}} */
    -const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin)
    +const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, uin_t uin)
    {
    const char *uin_s = ggp_uin_to_str(uin);
    PurpleBuddy *buddy = purple_blist_find_buddy(
    --- a/libpurple/protocols/gg/blist.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/gg/blist.h Mon Nov 04 14:43:49 2019 +0300
    @@ -57,7 +57,7 @@
    * @return Name of the buddy, or UIN converted to string, if there is no such
    * user on the list.
    */
    -const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin);
    +const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, uin_t uin);
    #endif /* PURPLE_GG_BLIST_H */
    --- a/libpurple/protocols/gg/edisc.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/gg/edisc.c Mon Nov 04 14:43:49 2019 +0300
    @@ -358,15 +358,14 @@
    msg = soup_message_new("PUT", "https://drive.mpa.gg.pl/signin");
    ggp_edisc_set_defaults(msg);
    - metadata =
    - g_strdup_printf("{"
    - "\"id\": \"%032x\", "
    - "\"name\": \"%s\", "
    - "\"os_version\": \"" GGP_EDISC_OS "\", "
    - "\"client_version\": \"%s\", "
    - "\"type\": \"" GGP_EDISC_TYPE "\"}",
    - g_random_int_range(1, 1 << 16),
    - purple_get_host_name(), ggp_libgaduw_version(gc));
    + metadata = g_strdup_printf("{"
    + "\"id\": \"%032x\", "
    + "\"name\": \"%s\", "
    + "\"os_version\": \"" GGP_EDISC_OS "\", "
    + "\"client_version\": \"%s\", "
    + "\"type\": \"" GGP_EDISC_TYPE "\"}",
    + g_random_int_range(1, 1 << 16),
    + g_get_host_name(), ggp_libgaduw_version(gc));
    tmp = g_strdup_printf("IMToken %s", imtoken);
    soup_message_headers_replace(msg->request_headers, "Authorization", tmp);
    --- a/libpurple/protocols/irc/cmds.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/irc/cmds.c Mon Nov 04 14:43:49 2019 +0300
    @@ -694,6 +694,4 @@
    irc_send(irc, buf);
    g_free(buf);
    }
    -
    - return;
    }
    --- a/libpurple/protocols/irc/msgs.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/irc/msgs.c Mon Nov 04 14:43:49 2019 +0300
    @@ -333,8 +333,6 @@
    purple_conversation_write_system_message(PURPLE_CONVERSATION(chat), buf, 0);
    g_free(escaped);
    g_free(buf);
    -
    - return;
    }
    void irc_msg_whois(struct irc_conn *irc, const char *name, const char *from, char **args)
    @@ -495,8 +493,8 @@
    }
    realname = g_strdup(cur);
    - g_object_set_data_full(G_OBJECT(cb), "userhost", userhost, (GDestroyNotify)g_free);
    - g_object_set_data_full(G_OBJECT(cb), "realname", realname, (GDestroyNotify)g_free);
    + g_object_set_data_full(G_OBJECT(cb), "userhost", userhost, g_free);
    + g_object_set_data_full(G_OBJECT(cb), "realname", realname, g_free);
    flags = purple_chat_user_get_flags(cb);
    @@ -971,7 +969,7 @@
    cb = purple_chat_conversation_find_user(chat, nick);
    if (cb) {
    - g_object_set_data_full(G_OBJECT(cb), "userhost", userhost, (GDestroyNotify)g_free);
    + g_object_set_data_full(G_OBJECT(cb), "userhost", userhost, g_free);
    }
    if ((ib = g_hash_table_lookup(irc->buddies, nick)) != NULL) {
    @@ -1010,7 +1008,6 @@
    }
    g_free(nick);
    - return;
    }
    void irc_msg_mode(struct irc_conn *irc, const char *name, const char *from, char **args)
    @@ -1362,8 +1359,6 @@
    irc_buddy_status(data[0], ib, irc);
    }
    g_free(data[0]);
    -
    - return;
    }
    void irc_msg_unavailable(struct irc_conn *irc, const char *name, const char *from, char **args)
    --- a/libpurple/protocols/jabber/auth_cyrus.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/auth_cyrus.c Mon Nov 04 14:43:49 2019 +0300
    @@ -402,11 +402,22 @@
    jabber_cyrus_start(JabberStream *js, PurpleXmlNode *mechanisms,
    PurpleXmlNode **reply, char **error)
    {
    - PurpleXmlNode *mechnode;
    + PurpleXmlNode *mechnode, *hostname;
    JabberSaslState ret;
    js->sasl_mechs = g_string_new("");
    js->sasl_password = g_strdup(purple_connection_get_password(js->gc));
    + /* XEP-0233 says we should grab the hostname for Kerberos v5, but there
    + * is no claim about other SASL mechanisms. Fortunately, most don't
    + * care what we use, so just use the domainpart. */
    + hostname = purple_xmlnode_get_child_with_namespace(
    + mechanisms, "hostname", NS_XMPP_SERVER_REGISTRATION);
    + if (hostname) {
    + js->serverFQDN = purple_xmlnode_get_data(hostname);
    + }
    + if (js->serverFQDN == NULL) {
    + js->serverFQDN = g_strdup(js->user->domain);
    + }
    for(mechnode = purple_xmlnode_get_child(mechanisms, "mechanism"); mechnode;
    mechnode = purple_xmlnode_get_next_twin(mechnode))
    --- a/libpurple/protocols/jabber/bosh.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/bosh.c Mon Nov 04 14:43:49 2019 +0300
    @@ -134,12 +134,6 @@
    conn->rid = (((guint64)g_random_int() << 32) | g_random_int());
    conn->rid &= 0xFFFFFFFFFFFFFLL;
    - if (g_hostname_is_ip_address(url_p->host)) {
    - js->serverFQDN = g_strdup(js->user->domain);
    - } else {
    - js->serverFQDN = g_strdup(url_p->host);
    - }
    -
    soup_uri_free(url_p);
    g_object_unref(resolver);
    --- a/libpurple/protocols/jabber/google/gmail.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/google/gmail.c Mon Nov 04 14:43:49 2019 +0300
    @@ -176,7 +176,6 @@
    purple_xmlnode_set_attrib(query, "newer-than-tid", js->gmail_last_tid);
    jabber_iq_send(iq);
    - return;
    }
    void jabber_gmail_init(JabberStream *js) {
    --- a/libpurple/protocols/jabber/google/google_presence.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/google/google_presence.c Mon Nov 04 14:43:49 2019 +0300
    @@ -26,7 +26,7 @@
    {
    if (!js->googletalk)
    return;
    - if (jbr->status && purple_str_has_prefix(jbr->status, "♫ ")) {
    + if (jbr->status && g_str_has_prefix(jbr->status, "♫ ")) {
    purple_protocol_got_user_status(purple_connection_get_account(js->gc), user, "tune",
    PURPLE_TUNE_TITLE, jbr->status + strlen("♫ "), NULL);
    g_free(jbr->status);
    --- a/libpurple/protocols/jabber/jabber.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/jabber.c Mon Nov 04 14:43:49 2019 +0300
    @@ -35,6 +35,7 @@
    #include "proxy.h"
    #include "protocol.h"
    #include "purpleaccountoption.h"
    +#include "purple-gio.h"
    #include "request.h"
    #include "server.h"
    #include "status.h"
    @@ -222,12 +223,13 @@
    PurpleAccount *account = NULL;
    PurpleXmlNode *starttls = NULL;
    - /* It's a secure BOSH connection, just return FALSE and skip, without doing anything extra.
    - * XEP-0206 (XMPP Over BOSH): The client SHOULD ignore any Transport Layer Security (TLS)
    - * feature since BOSH channel encryption SHOULD be negotiated at the HTTP layer.
    + /* It's a secure BOSH connection, just return FALSE and skip, without doing
    + * anything extra. XEP-0206 (XMPP Over BOSH): The client SHOULD ignore any
    + * Transport Layer Security (TLS) feature since BOSH channel encryption
    + * SHOULD be negotiated at the HTTP layer.
    *
    - * Note: we are already receiving STARTTLS at this point from a SSL/TLS BOSH connection,
    - * so it is not necessary to check if purple_ssl_is_supported().
    + * Note: we are already receiving STARTTLS at this point from a SSL/TLS BOSH
    + * connection, so it is not necessary to check if SSL is supported.
    */
    if (js->bosh && jabber_bosh_connection_is_ssl(js->bosh)) {
    return FALSE;
    @@ -366,9 +368,10 @@
    jabber_auth_handle_failure(js, *packet);
    }
    } else if (purple_strequal(xmlns, NS_XMPP_TLS)) {
    - if (js->state != JABBER_STREAM_INITIALIZING_ENCRYPTION || js->gsc)
    + if (js->state != JABBER_STREAM_INITIALIZING_ENCRYPTION ||
    + G_IS_TLS_CONNECTION(js->stream)) {
    purple_debug_warning("jabber", "Ignoring spurious %s\n", name);
    - else {
    + } else {
    if (purple_strequal(name, "proceed"))
    tls_init(js);
    /* TODO: Handle <failure/>, I guess? */
    @@ -378,52 +381,27 @@
    }
    }
    -static int jabber_do_send(JabberStream *js, const char *data, int len)
    +static void
    +jabber_push_bytes_cb(GObject *source, GAsyncResult *res, gpointer data)
    {
    - int ret;
    -
    - if (js->gsc)
    - ret = purple_ssl_write(js->gsc, data, len);
    - else
    - ret = write(js->fd, data, len);
    -
    - return ret;
    -}
    -
    -static void jabber_send_cb(gpointer data, gint source, PurpleInputCondition cond)
    -{
    + PurpleQueuedOutputStream *stream = PURPLE_QUEUED_OUTPUT_STREAM(source);
    JabberStream *js = data;
    - const gchar *output = NULL;
    - int ret, writelen;
    -
    - writelen = purple_circular_buffer_get_max_read(js->write_buffer);
    - output = purple_circular_buffer_get_output(js->write_buffer);
    -
    - if (writelen == 0) {
    - purple_input_remove(js->writeh);
    - js->writeh = 0;
    - return;
    + gboolean result;
    + GError *error = NULL;
    +
    + result = purple_queued_output_stream_push_bytes_finish(stream, res, &error);
    +
    + if (!result) {
    + purple_queued_output_stream_clear_queue(stream);
    +
    + g_prefix_error(&error, "%s", _("Lost connection with server: "));
    + purple_connection_take_error(js->gc, error);
    }
    -
    - ret = jabber_do_send(js, output, writelen);
    -
    - if (ret < 0 && errno == EAGAIN)
    - return;
    - else if (ret <= 0) {
    - gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
    - g_strerror(errno));
    - purple_connection_error(js->gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - return;
    - }
    -
    - purple_circular_buffer_mark_read(js->write_buffer, ret);
    }
    static gboolean do_jabber_send_raw(JabberStream *js, const char *data, int len)
    {
    - int ret;
    + GBytes *output;
    gboolean success = TRUE;
    g_return_val_if_fail(len > 0, FALSE);
    @@ -431,39 +409,11 @@
    if (js->state == JABBER_STREAM_CONNECTED)
    jabber_stream_restart_inactivity_timer(js);
    - if (js->writeh == 0)
    - ret = jabber_do_send(js, data, len);
    - else {
    - ret = -1;
    - errno = EAGAIN;
    - }
    -
    - if (ret < 0 && errno != EAGAIN) {
    - PurpleAccount *account = purple_connection_get_account(js->gc);
    - /*
    - * The server may have closed the socket (on a stream error), so if
    - * we're disconnecting, don't generate (possibly another) error that
    - * (for some UIs) would mask the first.
    - */
    - if (!purple_account_is_disconnecting(account)) {
    - gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"),
    - g_strerror(errno));
    - purple_connection_error(js->gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - }
    -
    - success = FALSE;
    - } else if (ret < len) {
    - if (ret < 0)
    - ret = 0;
    - if (js->writeh == 0)
    - js->writeh = purple_input_add(
    - js->gsc ? js->gsc->fd : js->fd,
    - PURPLE_INPUT_WRITE, jabber_send_cb, js);
    - purple_circular_buffer_append(js->write_buffer,
    - data + ret, len - ret);
    - }
    + output = g_bytes_new(data, len);
    + purple_queued_output_stream_push_bytes_async(
    + js->output, output, G_PRIORITY_DEFAULT, js->cancellable,
    + jabber_push_bytes_cb, js);
    + g_bytes_unref(output);
    return success;
    }
    @@ -531,8 +481,7 @@
    if (js->sasl_maxbuf>0) {
    int pos = 0;
    - if (!js->gsc && js->fd<0)
    - g_return_if_reached();
    + g_return_if_fail(js->input != NULL);
    while (pos < len) {
    int towrite;
    @@ -647,51 +596,21 @@
    }
    static void
    -jabber_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc,
    - PurpleInputCondition cond)
    +jabber_recv_cb(GObject *stream, gpointer data)
    {
    PurpleConnection *gc = data;
    JabberStream *js = purple_connection_get_protocol_data(gc);
    - int len;
    - static char buf[4096];
    + gssize len;
    + gchar buf[4096];
    + GError *error = NULL;
    PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    - while((len = purple_ssl_read(gsc, buf, sizeof(buf) - 1)) > 0) {
    - purple_connection_update_last_received(gc);
    - buf[len] = '\0';
    - purple_debug_misc("jabber", "Recv (ssl)(%d): %s", len, buf);
    - jabber_parser_process(js, buf, len);
    - if(js->reinit)
    - jabber_stream_init(js);
    - }
    -
    - if(len < 0 && errno == EAGAIN)
    - return;
    - else {
    - gchar *tmp;
    - if (len == 0)
    - tmp = g_strdup(_("Server closed the connection"));
    - else
    - tmp = g_strdup_printf(_("Lost connection with server: %s"),
    - g_strerror(errno));
    - purple_connection_error(js->gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    - }
    -}
    -
    -static void
    -jabber_recv_cb(gpointer data, gint source, PurpleInputCondition condition)
    -{
    - PurpleConnection *gc = data;
    - JabberStream *js = purple_connection_get_protocol_data(gc);
    - int len;
    - static char buf[4096];
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - if((len = read(js->fd, buf, sizeof(buf) - 1)) > 0) {
    + len = g_pollable_input_stream_read_nonblocking(
    + G_POLLABLE_INPUT_STREAM(stream), buf, sizeof(buf) - 1,
    + js->cancellable, &error);
    +
    + if (len > 0) {
    purple_connection_update_last_received(gc);
    #ifdef HAVE_CYRUS_SASL
    if (js->sasl_maxbuf > 0) {
    @@ -720,43 +639,20 @@
    }
    #endif
    buf[len] = '\0';
    - purple_debug_misc("jabber", "Recv (%d): %s", len, buf);
    + purple_debug_misc("jabber", "Recv (%" G_GSSIZE_FORMAT "): %s", len,
    + buf);
    jabber_parser_process(js, buf, len);
    if(js->reinit)
    jabber_stream_init(js);
    - } else if(len < 0 && errno == EAGAIN) {
    - return;
    - } else {
    - gchar *tmp;
    - if (len == 0)
    - tmp = g_strdup(_("Server closed the connection"));
    - else
    - tmp = g_strdup_printf(_("Lost connection with server: %s"),
    - g_strerror(errno));
    - purple_connection_error(js->gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp);
    - g_free(tmp);
    + } else if (len == 0) {
    + purple_connection_error(js->gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    + _("Server closed the connection"));
    + } else if (error->code != G_IO_ERROR_WOULD_BLOCK &&
    + error->code != G_IO_ERROR_CANCELLED) {
    + g_prefix_error(&error, "%s", _("Lost connection with server: "));
    + purple_connection_g_error(js->gc, error);
    }
    -}
    -
    -static void
    -jabber_login_callback_ssl(gpointer data, PurpleSslConnection *gsc,
    - PurpleInputCondition cond)
    -{
    - PurpleConnection *gc = data;
    - JabberStream *js;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - js = purple_connection_get_protocol_data(gc);
    -
    - if(js->state == JABBER_STREAM_CONNECTING)
    - jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
    - jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
    - purple_ssl_input_add(gsc, jabber_recv_cb_ssl, gc);
    -
    - /* Tell the app that we're doing encryption */
    - jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
    + g_clear_error(&error);
    }
    static void
    @@ -823,130 +719,184 @@
    }
    static void
    -jabber_login_callback(gpointer data, gint source, const gchar *error)
    +jabber_stream_connect_finish(JabberStream *js, GIOStream *stream)
    +{
    + GSource *source;
    +
    + js->stream = stream;
    + js->input = g_io_stream_get_input_stream(js->stream);
    + js->output = purple_queued_output_stream_new(
    + g_io_stream_get_output_stream(js->stream));
    +
    + if (js->state == JABBER_STREAM_CONNECTING) {
    + jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
    + }
    +
    + jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
    + source = g_pollable_input_stream_create_source(
    + G_POLLABLE_INPUT_STREAM(js->input), js->cancellable);
    + g_source_set_callback(source, (GSourceFunc)jabber_recv_cb, js->gc, NULL);
    + js->inpa = g_source_attach(source, NULL);
    +}
    +
    +static void
    +jabber_login_callback(GObject *source_object, GAsyncResult *res, gpointer data)
    {
    - PurpleConnection *gc = data;
    - JabberStream *js = purple_connection_get_protocol_data(gc);
    -
    - if (source < 0) {
    - GResolver *resolver = g_resolver_get_default();
    - gchar *name = g_strdup_printf("_xmppconnect.%s", js->user->domain);
    -
    - purple_debug_info("jabber", "Couldn't connect directly to %s. Trying to find alternative connection methods, like BOSH.\n", js->user->domain);
    -
    - g_resolver_lookup_records_async(resolver,
    - name,
    - G_RESOLVER_RECORD_TXT,
    - js->cancellable,
    - txt_resolved_cb,
    - js);
    + GSocketClient *client = G_SOCKET_CLIENT(source_object);
    + JabberStream *js = data;
    + GSocketConnection *conn;
    + GIOStream *stream;
    + gboolean is_old_ssl = g_socket_client_get_tls(client);
    + GError *error = NULL;
    +
    + conn = g_socket_client_connect_to_host_finish(client, res, &error);
    + if (conn == NULL) {
    + GResolver *resolver;
    + gchar *name;
    +
    + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + g_error_free(error);
    + return;
    + } else if (is_old_ssl) {
    + /* Old-style SSL only makes a direct connection, or fails. */
    + purple_connection_take_error(js->gc, error);
    + return;
    + }
    + g_error_free(error);
    +
    + name = g_strdup_printf("_xmppconnect.%s", js->user->domain);
    + purple_debug_info("jabber",
    + "Couldn't connect directly to %s. Trying to find "
    + "alternative connection methods, like BOSH.\n",
    + js->user->domain);
    +
    + resolver = g_resolver_get_default();
    + g_resolver_lookup_records_async(resolver, name, G_RESOLVER_RECORD_TXT,
    + js->cancellable, txt_resolved_cb, js);
    g_free(name);
    g_object_unref(resolver);
    return;
    }
    - js->fd = source;
    -
    - if(js->state == JABBER_STREAM_CONNECTING)
    - jabber_send_raw(js, "<?xml version='1.0' ?>", -1);
    -
    - jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING);
    - js->inpa = purple_input_add(js->fd, PURPLE_INPUT_READ, jabber_recv_cb, gc);
    + if (is_old_ssl) {
    + stream = G_IO_STREAM(g_tcp_wrapper_connection_get_base_io_stream(
    + G_TCP_WRAPPER_CONNECTION(conn)));
    + } else {
    + stream = G_IO_STREAM(conn);
    + }
    +
    + jabber_stream_connect_finish(js, stream);
    +
    + if (is_old_ssl) {
    + /* Tell the app that we're doing encryption */
    + jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
    + }
    }
    static void
    -jabber_ssl_connect_failure(PurpleSslConnection *gsc, PurpleSslErrorType error,
    - gpointer data)
    +tls_handshake_cb(GObject *source_object, GAsyncResult *res, gpointer data)
    {
    - PurpleConnection *gc = data;
    - JabberStream *js;
    -
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    -
    - js = purple_connection_get_protocol_data(gc);
    - js->gsc = NULL;
    -
    - purple_connection_ssl_error (gc, error);
    + JabberStream *js = data;
    + GError *error = NULL;
    +
    + if (!g_tls_connection_handshake_finish(G_TLS_CONNECTION(source_object), res,
    + &error)) {
    + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + /* Connection already closed/freed. Escape. */
    + } else if (g_error_matches(error, G_TLS_ERROR, G_TLS_ERROR_HANDSHAKE)) {
    + /* In Gio, a handshake error is because of the cert */
    + purple_connection_ssl_error(js->gc, PURPLE_SSL_CERTIFICATE_INVALID);
    + } else {
    + /* Report any other errors as handshake failing */
    + purple_connection_ssl_error(js->gc, PURPLE_SSL_HANDSHAKE_FAILED);
    + }
    +
    + g_error_free(error);
    + return;
    + }
    +
    + jabber_stream_connect_finish(js, js->stream);
    +
    + /* Tell the app that we're doing encryption */
    + jabber_stream_set_state(js, JABBER_STREAM_INITIALIZING_ENCRYPTION);
    }
    static void tls_init(JabberStream *js)
    {
    - purple_input_remove(js->inpa);
    + GSocketConnectable *identity;
    + GIOStream *tls_conn;
    + GError *error = NULL;
    +
    + g_source_remove(js->inpa);
    js->inpa = 0;
    - js->gsc = purple_ssl_connect_with_host_fd(purple_connection_get_account(js->gc), js->fd,
    - jabber_login_callback_ssl, jabber_ssl_connect_failure, js->certificate_CN, js->gc);
    - /* The fd is no longer our concern */
    - js->fd = -1;
    -}
    -
    -static gboolean jabber_login_connect(JabberStream *js, const char *domain, const char *host, int port,
    - gboolean fatal_failure)
    -{
    - /* host should be used in preference to domain to
    - * allow SASL authentication to work with FQDN of the server,
    - * but we use domain as fallback for when users enter IP address
    - * in connect server */
    - g_free(js->serverFQDN);
    - if (g_hostname_is_ip_address(host)) {
    - js->serverFQDN = g_strdup(domain);
    - } else {
    - js->serverFQDN = g_strdup(host);
    + js->input = NULL;
    + g_filter_output_stream_set_close_base_stream(
    + G_FILTER_OUTPUT_STREAM(js->output), FALSE);
    + g_output_stream_close(G_OUTPUT_STREAM(js->output), js->cancellable, NULL);
    + js->output = NULL;
    +
    + identity = g_network_address_new(js->certificate_CN, 0);
    + tls_conn = g_tls_client_connection_new(js->stream, identity, &error);
    + g_object_unref(identity);
    +
    + if (tls_conn == NULL) {
    + purple_debug_warning("jabber",
    + "Error creating TLS client connection: %s",
    + error->message);
    + g_clear_error(&error);
    + purple_connection_ssl_error(js->gc, PURPLE_SSL_CONNECT_FAILED);
    + return;
    }
    - if (purple_proxy_connect(js->gc, purple_connection_get_account(js->gc),
    - host, port, jabber_login_callback, js->gc) == NULL) {
    - if (fatal_failure) {
    - purple_connection_error(js->gc,
    - PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - }
    -
    - return FALSE;
    - }
    -
    - return TRUE;
    + g_clear_object(&js->stream);
    + js->stream = G_IO_STREAM(tls_conn);
    +
    + g_tls_connection_handshake_async(G_TLS_CONNECTION(tls_conn),
    + G_PRIORITY_DEFAULT, js->cancellable,
    + tls_handshake_cb, js);
    }
    static void
    -srv_resolved_cb(GObject *sender, GAsyncResult *result, gpointer data)
    +srv_resolved_cb(GObject *source_object, GAsyncResult *result, gpointer data)
    {
    - GError *error = NULL;
    - GList *targets = NULL, *l = NULL;
    + GSocketClient *client = G_SOCKET_CLIENT(source_object);
    JabberStream *js = data;
    -
    - targets = g_resolver_lookup_service_finish(G_RESOLVER(sender),
    - result, &error);
    - if(error) {
    - purple_debug_warning("jabber",
    - "SRV lookup failed, proceeding with normal connection : %s",
    - error->message);
    + GSocketConnection *conn;
    + GError *error = NULL;
    +
    + conn = g_socket_client_connect_to_service_finish(client, result, &error);
    + if (error) {
    + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + /* Do nothing; cancelled. */
    +
    + } else if (g_error_matches(error, G_RESOLVER_ERROR,
    + G_RESOLVER_ERROR_NOT_FOUND)) {
    + /* If there was no response, then attempt fallback behaviour of XMPP
    + * Core 3.2.2. */
    + purple_debug_warning(
    + "jabber",
    + "SRV lookup failed, proceeding with normal connection : %s",
    + error->message);
    +
    + g_socket_client_connect_to_host_async(
    + js->client, js->user->domain,
    + purple_account_get_int(
    + purple_connection_get_account(js->gc), "port",
    + 5222),
    + js->cancellable, jabber_login_callback, js);
    +
    + } else {
    + /* If resolving failed or connecting failed, then just error out, as
    + * in XMPP Core 3.2.1 step 8. */
    + purple_connection_g_error(js->gc, error);
    + }
    g_error_free(error);
    -
    - jabber_login_connect(js, js->user->domain, js->user->domain,
    - purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222),
    - TRUE);
    -
    - } else {
    - for(l = targets; l; l = l->next) {
    - GSrvTarget *target = (GSrvTarget *)l->data;
    - const gchar *hostname = g_srv_target_get_hostname(target);
    - guint port = g_srv_target_get_port(target);
    -
    - if(jabber_login_connect(js, hostname, hostname, port, FALSE)) {
    - g_resolver_free_targets(targets);
    -
    - return;
    - }
    - }
    -
    - g_resolver_free_targets(targets);
    -
    - jabber_login_connect(js, js->user->domain, js->user->domain,
    - purple_account_get_int(purple_connection_get_account(js->gc), "port", 5222),
    - TRUE);
    + return;
    }
    +
    + jabber_stream_connect_finish(js, G_IO_STREAM(conn));
    }
    static JabberStream *
    @@ -971,7 +921,6 @@
    js = g_new0(JabberStream, 1);
    purple_connection_set_protocol_data(gc, js);
    js->gc = gc;
    - js->fd = -1;
    js->http_conns = soup_session_new_with_options(SOUP_SESSION_PROXY_RESOLVER,
    resolver, NULL);
    g_object_unref(resolver);
    @@ -1039,7 +988,6 @@
    js->chats = g_hash_table_new_full(g_str_hash, g_str_equal,
    g_free, (GDestroyNotify)jabber_chat_free);
    js->next_id = g_random_int();
    - js->write_buffer = purple_circular_buffer_new(512);
    js->old_length = 0;
    js->keepalive_timeout = 0;
    js->max_inactivity = DEFAULT_INACTIVITY_TIME;
    @@ -1071,6 +1019,7 @@
    "connect_server", "");
    const char *bosh_url = purple_account_get_string(account,
    "bosh_url", "");
    + GError *error = NULL;
    jabber_stream_set_state(js, JABBER_STREAM_CONNECTING);
    @@ -1088,37 +1037,35 @@
    return;
    }
    + js->client = purple_gio_socket_client_new(account, &error);
    + if (js->client == NULL) {
    + purple_connection_take_error(gc, error);
    + return;
    + }
    +
    js->certificate_CN = g_strdup(connect_server[0] ? connect_server : js->user->domain);
    /* if they've got old-ssl mode going, we probably want to ignore SRV lookups */
    if (purple_strequal("old_ssl", purple_account_get_string(account, "connection_security", JABBER_DEFAULT_REQUIRE_TLS))) {
    - js->gsc = purple_ssl_connect(account, js->certificate_CN,
    - purple_account_get_int(account, "port", 5223),
    - jabber_login_callback_ssl, jabber_ssl_connect_failure, gc);
    - if (!js->gsc) {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("Unable to establish SSL connection"));
    - }
    -
    + g_socket_client_set_tls(js->client, TRUE);
    + g_socket_client_connect_to_host_async(
    + js->client, js->certificate_CN,
    + purple_account_get_int(account, "port", 5223), js->cancellable,
    + jabber_login_callback, js);
    return;
    }
    /* no old-ssl, so if they've specified a connect server, we'll use that, otherwise we'll
    * invoke the magic of SRV lookups, to figure out host and port */
    if(connect_server[0]) {
    - jabber_login_connect(js, js->user->domain, connect_server,
    - purple_account_get_int(account, "port", 5222), TRUE);
    + g_socket_client_connect_to_host_async(
    + js->client, connect_server,
    + purple_account_get_int(account, "port", 5222), js->cancellable,
    + jabber_login_callback, js);
    } else {
    - GResolver *resolver = g_resolver_get_default();
    - g_resolver_lookup_service_async(resolver,
    - "xmpp-client",
    - "tcp",
    - js->user->domain,
    - js->cancellable,
    - srv_resolved_cb,
    - js);
    - g_object_unref(resolver);
    + g_socket_client_connect_to_service_async(js->client, js->user->domain,
    + "xmpp-client", js->cancellable,
    + srv_resolved_cb, js);
    }
    }
    @@ -1649,19 +1596,21 @@
    if (js->bosh) {
    jabber_bosh_connection_destroy(js->bosh);
    js->bosh = NULL;
    - } else if ((js->gsc && js->gsc->fd > 0) || js->fd > 0)
    + } else if (js->output != NULL) {
    jabber_send_raw(js, "</stream:stream>", -1);
    - if(js->gsc) {
    - purple_ssl_close(js->gsc);
    - } else if (js->fd > 0) {
    if(js->inpa) {
    - purple_input_remove(js->inpa);
    + g_source_remove(js->inpa);
    js->inpa = 0;
    }
    - close(js->fd);
    + purple_gio_graceful_close(js->stream, js->input,
    + G_OUTPUT_STREAM(js->output));
    }
    + g_clear_object(&js->output);
    + g_clear_object(&js->input);
    + g_clear_object(&js->stream);
    +
    jabber_buddy_remove_all_pending_buddy_info_requests(js);
    jabber_parser_free(js);
    @@ -1690,10 +1639,6 @@
    g_free(js->avatar_hash);
    g_free(js->caps_hash);
    - if (js->write_buffer)
    - g_object_unref(G_OBJECT(js->write_buffer));
    - if(js->writeh)
    - purple_input_remove(js->writeh);
    if (js->auth_mech && js->auth_mech->dispose)
    js->auth_mech->dispose(js);
    #ifdef HAVE_CYRUS_SASL
    @@ -1746,7 +1691,11 @@
    void jabber_stream_set_state(JabberStream *js, JabberStreamState state)
    {
    -#define JABBER_CONNECT_STEPS ((js->gsc || js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION) ? 9 : 5)
    +#define JABBER_CONNECT_STEPS \
    + ((G_IS_TLS_CONNECTION(js->stream) || \
    + js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION) \
    + ? 9 \
    + : 5)
    js->state = state;
    switch(state) {
    @@ -1757,8 +1706,10 @@
    JABBER_CONNECT_STEPS);
    break;
    case JABBER_STREAM_INITIALIZING:
    - purple_connection_update_progress(js->gc, _("Initializing Stream"),
    - js->gsc ? 5 : 2, JABBER_CONNECT_STEPS);
    + purple_connection_update_progress(
    + js->gc, _("Initializing Stream"),
    + G_IS_TLS_CONNECTION(js->stream) ? 5 : 2,
    + JABBER_CONNECT_STEPS);
    jabber_stream_init(js);
    break;
    case JABBER_STREAM_INITIALIZING_ENCRYPTION:
    @@ -1766,12 +1717,16 @@
    6, JABBER_CONNECT_STEPS);
    break;
    case JABBER_STREAM_AUTHENTICATING:
    - purple_connection_update_progress(js->gc, _("Authenticating"),
    - js->gsc ? 7 : 3, JABBER_CONNECT_STEPS);
    + purple_connection_update_progress(
    + js->gc, _("Authenticating"),
    + G_IS_TLS_CONNECTION(js->stream) ? 7 : 3,
    + JABBER_CONNECT_STEPS);
    break;
    case JABBER_STREAM_POST_AUTH:
    - purple_connection_update_progress(js->gc, _("Re-initializing Stream"),
    - (js->gsc ? 8 : 4), JABBER_CONNECT_STEPS);
    + purple_connection_update_progress(
    + js->gc, _("Re-initializing Stream"),
    + (G_IS_TLS_CONNECTION(js->stream) ? 8 : 4),
    + JABBER_CONNECT_STEPS);
    break;
    case JABBER_STREAM_CONNECTED:
    @@ -2105,7 +2060,7 @@
    gboolean jabber_stream_is_ssl(JabberStream *js)
    {
    return (js->bosh && jabber_bosh_connection_is_ssl(js->bosh)) ||
    - (!js->bosh && js->gsc);
    + (!js->bosh && G_IS_TLS_CONNECTION(js->stream));
    }
    static gboolean
    --- a/libpurple/protocols/jabber/jabber.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/jabber.h Mon Nov 04 14:43:49 2019 +0300
    @@ -67,6 +67,7 @@
    #include "media.h"
    #include "mediamanager.h"
    #include "protocol.h"
    +#include "queuedoutputstream.h"
    #include "roomlist.h"
    #include "sslconn.h"
    @@ -119,7 +120,6 @@
    struct _JabberStream
    {
    - int fd;
    guint inpa;
    GCancellable *cancellable;
    @@ -196,7 +196,10 @@
    JabberBuddy *user_jb;
    PurpleConnection *gc;
    - PurpleSslConnection *gsc;
    + GSocketClient *client;
    + GIOStream *stream;
    + GInputStream *input;
    + PurpleQueuedOutputStream *output;
    gboolean registration;
    @@ -206,9 +209,6 @@
    GSList *pending_buddy_info_requests;
    - PurpleCircularBuffer *write_buffer;
    - guint writeh;
    -
    gboolean reinit;
    JabberCapabilities server_caps;
    --- a/libpurple/protocols/jabber/namespaces.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/namespaces.h Mon Nov 04 14:43:49 2019 +0300
    @@ -89,6 +89,9 @@
    /* XEP-0231 BoB (Bits of Binary) */
    #define NS_BOB "urn:xmpp:bob"
    +/* XEP-0233 XMPP Server Registration for use with Kerberos V5 */
    +#define NS_XMPP_SERVER_REGISTRATION "urn:xmpp:domain-based-name:1"
    +
    /* XEP-0237 Roster Versioning */
    #define NS_ROSTER_VERSIONING "urn:xmpp:features:rosterver"
    --- a/libpurple/protocols/jabber/si.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/jabber/si.c Mon Nov 04 14:43:49 2019 +0300
    @@ -416,7 +416,7 @@
    char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
    int len;
    char *dstaddr, *hash;
    - const char *host;
    + gchar *host;
    purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_read_again_cb\n");
    @@ -495,7 +495,8 @@
    g_free(dstaddr);
    g_free(jsx->rxqueue);
    - host = purple_network_get_my_ip(jsx->js->fd);
    + host = purple_network_get_my_ip_from_gio(
    + G_SOCKET_CONNECTION(jsx->js->stream));
    jsx->rxmaxlen = 5 + strlen(host) + 2;
    jsx->rxqueue = g_malloc(jsx->rxmaxlen);
    @@ -514,6 +515,8 @@
    jabber_si_xfer_bytestreams_send_read_again_resp_cb, xfer));
    jabber_si_xfer_bytestreams_send_read_again_resp_cb(xfer, source,
    PURPLE_INPUT_WRITE);
    +
    + g_free(host);
    }
    static void
    @@ -844,7 +847,7 @@
    gchar *jid;
    GList *local_ips =
    purple_network_get_all_local_system_ips();
    - const char *public_ip;
    + gchar *public_ip;
    gboolean has_public_ip = FALSE;
    jsx->local_streamhost_fd = sock;
    @@ -854,7 +857,8 @@
    purple_xfer_set_local_port(xfer, purple_network_get_port_from_fd(sock));
    g_snprintf(port, sizeof(port), "%hu", purple_xfer_get_local_port(xfer));
    - public_ip = purple_network_get_my_ip(jsx->js->fd);
    + public_ip = purple_network_get_my_ip_from_gio(
    + G_SOCKET_CONNECTION(jsx->js->stream));
    /* Include the localhost's IPs (for in-network transfers) */
    while (local_ips) {
    @@ -880,6 +884,7 @@
    }
    g_free(jid);
    + g_free(public_ip);
    /* The listener for the local proxy */
    purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,
    --- a/libpurple/protocols/novell/nmconn.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/novell/nmconn.c Mon Nov 04 14:43:49 2019 +0300
    @@ -20,9 +20,11 @@
    #include <glib.h>
    #include <unistd.h>
    -#include <errno.h>
    #include <string.h>
    #include <ctype.h>
    +
    +#include "purple-gio.h"
    +
    #include "nmconn.h"
    #ifdef _WIN32
    @@ -34,27 +36,6 @@
    #define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || \
    (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a))
    -/* Read data from conn until the end of a line */
    -static NMERR_T
    -read_line(NMConn * conn, char *buff, int len)
    -{
    - NMERR_T rc = NM_OK;
    - int total_bytes = 0;
    -
    - while ((rc == NM_OK) && (total_bytes < (len - 1))) {
    - rc = nm_read_all(conn, &buff[total_bytes], 1);
    - if (rc == NM_OK) {
    - total_bytes += 1;
    - if (buff[total_bytes - 1] == '\n') {
    - break;
    - }
    - }
    - }
    - buff[total_bytes] = '\0';
    -
    - return rc;
    -}
    -
    static char *
    url_escape_string(char *src)
    {
    @@ -180,100 +161,27 @@
    void nm_release_conn(NMConn *conn)
    {
    - if (conn) {
    - g_slist_free_full(conn->requests, (GDestroyNotify)nm_release_request);
    - conn->requests = NULL;
    - g_free(conn->addr);
    - conn->addr = NULL;
    - g_free(conn);
    - }
    -}
    + g_return_if_fail(conn != NULL);
    +
    + g_slist_free_full(conn->requests, (GDestroyNotify)nm_release_request);
    + conn->requests = NULL;
    -int
    -nm_tcp_write(NMConn * conn, const void *buff, int len)
    -{
    - if (conn == NULL || buff == NULL)
    - return -1;
    + if (conn->input) {
    + purple_gio_graceful_close(conn->stream, G_INPUT_STREAM(conn->input),
    + conn->output);
    + }
    + g_clear_object(&conn->input);
    + g_clear_object(&conn->output);
    + g_clear_object(&conn->stream);
    - return conn->write(conn->data, buff, len);
    -}
    -
    -int
    -nm_tcp_read(NMConn * conn, void *buff, int len)
    -{
    - if (conn == NULL || buff == NULL)
    - return -1;
    -
    - return conn->read(conn->data, buff, len);
    + g_clear_pointer(&conn->addr, g_free);
    + g_free(conn);
    }
    NMERR_T
    -nm_read_all(NMConn * conn, char *buff, int len)
    +nm_write_fields(NMUser *user, NMField *fields)
    {
    - NMERR_T rc = NM_OK;
    - int bytes_left = len;
    - int bytes_read;
    - int total_bytes = 0;
    - int retry = 1000;
    -
    - if (conn == NULL || buff == NULL)
    - return NMERR_BAD_PARM;
    -
    - /* Keep reading until buffer is full */
    - while (bytes_left) {
    - bytes_read = nm_tcp_read(conn, &buff[total_bytes], bytes_left);
    - if (bytes_read > 0) {
    - bytes_left -= bytes_read;
    - total_bytes += bytes_read;
    - } else {
    - if (errno == EAGAIN) {
    - if (--retry == 0) {
    - rc = NMERR_TCP_READ;
    - break;
    - }
    -#ifdef _WIN32
    - Sleep(1);
    -#else
    - usleep(1000);
    -#endif
    - } else {
    - rc = NMERR_TCP_READ;
    - break;
    - }
    - }
    - }
    - return rc;
    -}
    -
    -NMERR_T
    -nm_read_uint32(NMConn *conn, guint32 *val)
    -{
    - NMERR_T rc = NM_OK;
    -
    - rc = nm_read_all(conn, (char *)val, sizeof(*val));
    - if (rc == NM_OK) {
    - *val = GUINT32_FROM_LE(*val);
    - }
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_read_uint16(NMConn *conn, guint16 *val)
    -{
    - NMERR_T rc = NM_OK;
    -
    - rc = nm_read_all(conn, (char *)val, sizeof(*val));
    - if (rc == NM_OK) {
    - *val = GUINT16_FROM_LE(*val);
    - }
    -
    - return rc;
    -}
    -
    -NMERR_T
    -nm_write_fields(NMConn * conn, NMField * fields)
    -{
    + NMConn *conn;
    NMERR_T rc = NM_OK;
    NMField *field;
    char *value = NULL;
    @@ -283,9 +191,11 @@
    int bytes_to_send;
    int val = 0;
    - if (conn == NULL || fields == NULL) {
    - return NMERR_BAD_PARM;
    - }
    + g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    + g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    + g_return_val_if_fail(fields != NULL, NMERR_BAD_PARM);
    +
    + conn = user->conn;
    /* Format each field as valid "post" data and write it out */
    for (field = fields; (rc == NM_OK) && (field->tag); field++) {
    @@ -298,7 +208,8 @@
    /* Write the field tag */
    bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&tag=%s", field->tag);
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -307,7 +218,8 @@
    if (rc == NM_OK) {
    method = encode_method(field->method);
    bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&cmd=%s", method);
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -323,9 +235,13 @@
    bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    "&val=%s", value);
    if (bytes_to_send > (int)sizeof(buffer)) {
    - ret = nm_tcp_write(conn, buffer, sizeof(buffer));
    + ret = g_output_stream_write(conn->output, buffer,
    + sizeof(buffer),
    + user->cancellable, NULL);
    } else {
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer,
    + bytes_to_send,
    + user->cancellable, NULL);
    }
    if (ret < 0) {
    @@ -342,7 +258,9 @@
    val = nm_count_fields((NMField *) field->ptr_value);
    bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    "&val=%u", val);
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer,
    + bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -353,7 +271,9 @@
    bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    "&val=%u", field->value);
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer,
    + bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -366,7 +286,8 @@
    if (rc == NM_OK) {
    bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    "&type=%u", field->type);
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -377,8 +298,7 @@
    if (field->type == NMFIELD_TYPE_ARRAY ||
    field->type == NMFIELD_TYPE_MV) {
    - rc = nm_write_fields(conn, (NMField *) field->ptr_value);
    -
    + rc = nm_write_fields(user, (NMField *)field->ptr_value);
    }
    }
    }
    @@ -387,9 +307,10 @@
    }
    NMERR_T
    -nm_send_request(NMConn *conn, char *cmd, NMField *fields,
    - nm_response_cb cb, gpointer data, NMRequest **request)
    +nm_send_request(NMUser *user, char *cmd, NMField *fields, nm_response_cb cb,
    + gpointer data, NMRequest **request)
    {
    + NMConn *conn;
    NMERR_T rc = NM_OK;
    char buffer[512];
    int bytes_to_send;
    @@ -397,13 +318,17 @@
    NMField *request_fields = NULL;
    char *str = NULL;
    - if (conn == NULL || cmd == NULL)
    - return NMERR_BAD_PARM;
    + g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    + g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    + g_return_val_if_fail(cmd != NULL, NMERR_BAD_PARM);
    +
    + conn = user->conn;
    /* Write the post */
    bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    "POST /%s HTTP/1.0\r\n", cmd);
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -413,13 +338,15 @@
    if (purple_strequal("login", cmd)) {
    bytes_to_send = g_snprintf(buffer, sizeof(buffer),
    "Host: %s:%d\r\n\r\n", conn->addr, conn->port);
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    } else {
    bytes_to_send = g_snprintf(buffer, sizeof(buffer), "\r\n");
    - ret = nm_tcp_write(conn, buffer, bytes_to_send);
    + ret = g_output_stream_write(conn->output, buffer, bytes_to_send,
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -439,12 +366,13 @@
    /* Send the request to the server */
    if (rc == NM_OK) {
    - rc = nm_write_fields(conn, request_fields);
    + rc = nm_write_fields(user, request_fields);
    }
    /* Write the CRLF to terminate the data */
    if (rc == NM_OK) {
    - ret = nm_tcp_write(conn, "\r\n", strlen("\r\n"));
    + ret = g_output_stream_write(conn->output, "\r\n", strlen("\r\n"),
    + user->cancellable, NULL);
    if (ret < 0) {
    rc = NMERR_TCP_WRITE;
    }
    @@ -470,22 +398,25 @@
    }
    NMERR_T
    -nm_read_header(NMConn * conn)
    +nm_read_header(NMUser *user)
    {
    + NMConn *conn;
    NMERR_T rc = NM_OK;
    - char buffer[512];
    + gchar *buffer;
    char *ptr = NULL;
    int i;
    char rtn_buf[8];
    int rtn_code = 0;
    + GError *error = NULL;
    - if (conn == NULL)
    - return NMERR_BAD_PARM;
    + g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    + g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    - *buffer = '\0';
    - rc = read_line(conn, buffer, sizeof(buffer));
    - if (rc == NM_OK) {
    + conn = user->conn;
    + buffer = g_data_input_stream_read_line(conn->input, NULL, user->cancellable,
    + &error);
    + if (error == NULL) {
    /* Find the return code */
    ptr = strchr(buffer, ' ');
    if (ptr != NULL) {
    @@ -506,8 +437,19 @@
    /* Finish reading header, in the future we might want to do more processing here */
    /* TODO: handle more general redirects in the future */
    - while ((rc == NM_OK) && (!purple_strequal(buffer, "\r\n"))) {
    - rc = read_line(conn, buffer, sizeof(buffer));
    + while ((error == NULL) && !purple_strequal(buffer, "\r")) {
    + g_free(buffer);
    + buffer = g_data_input_stream_read_line(conn->input, NULL,
    + user->cancellable, &error);
    + }
    + g_free(buffer);
    +
    + if (error != NULL) {
    + if (error->code != G_IO_ERROR_WOULD_BLOCK &&
    + error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    if (rc == NM_OK && rtn_code == 301)
    @@ -517,8 +459,9 @@
    }
    NMERR_T
    -nm_read_fields(NMConn * conn, int count, NMField ** fields)
    +nm_read_fields(NMUser *user, int count, NMField **fields)
    {
    + NMConn *conn;
    NMERR_T rc = NM_OK;
    guint8 type;
    guint8 method;
    @@ -526,9 +469,13 @@
    char tag[64];
    NMField *sub_fields = NULL;
    char *str = NULL;
    + GError *error = NULL;
    - if (conn == NULL || fields == NULL)
    - return NMERR_BAD_PARM;
    + g_return_val_if_fail(user != NULL, NMERR_BAD_PARM);
    + g_return_val_if_fail(user->conn != NULL, NMERR_BAD_PARM);
    + g_return_val_if_fail(fields != NULL, NMERR_BAD_PARM);
    +
    + conn = user->conn;
    do {
    if (count > 0) {
    @@ -536,36 +483,46 @@
    }
    /* Read the field type, method, and tag */
    - rc = nm_read_all(conn, (char *)&type, sizeof(type));
    - if (rc != NM_OK || type == 0)
    + type = g_data_input_stream_read_byte(conn->input, user->cancellable,
    + &error);
    + if (error != NULL || type == 0) {
    break;
    + }
    - rc = nm_read_all(conn, (char *)&method, sizeof(method));
    - if (rc != NM_OK)
    + method = g_data_input_stream_read_byte(conn->input, user->cancellable,
    + &error);
    + if (error != NULL) {
    break;
    + }
    - rc = nm_read_uint32(conn, &val);
    - if (rc != NM_OK)
    + val = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (error != NULL) {
    break;
    + }
    if (val > sizeof(tag)) {
    rc = NMERR_PROTOCOL;
    break;
    }
    - rc = nm_read_all(conn, tag, val);
    - if (rc != NM_OK)
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), tag, val, NULL,
    + user->cancellable, &error);
    + if (error != NULL) {
    break;
    + }
    if (type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY) {
    /* Read the subarray (first read the number of items in the array) */
    - rc = nm_read_uint32(conn, &val);
    - if (rc != NM_OK)
    + val = g_data_input_stream_read_uint32(conn->input,
    + user->cancellable, &error);
    + if (error != NULL) {
    break;
    + }
    if (val > 0) {
    - rc = nm_read_fields(conn, val, &sub_fields);
    + rc = nm_read_fields(user, val, &sub_fields);
    if (rc != NM_OK)
    break;
    }
    @@ -578,9 +535,11 @@
    } else if (type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN) {
    /* Read the string (first read the length) */
    - rc = nm_read_uint32(conn, &val);
    - if (rc != NM_OK)
    + val = g_data_input_stream_read_uint32(conn->input,
    + user->cancellable, &error);
    + if (error != NULL) {
    break;
    + }
    if (val >= NMFIELD_MAX_STR_LENGTH) {
    rc = NMERR_PROTOCOL;
    @@ -590,9 +549,11 @@
    if (val > 0) {
    str = g_new0(char, val + 1);
    - rc = nm_read_all(conn, str, val);
    - if (rc != NM_OK)
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), str, val,
    + NULL, user->cancellable, &error);
    + if (error != NULL) {
    break;
    + }
    *fields = nm_field_add_pointer(*fields, tag, 0, method,
    0, str, type);
    @@ -602,9 +563,11 @@
    } else {
    /* Read the numerical value */
    - rc = nm_read_uint32(conn, &val);
    - if (rc != NM_OK)
    + val = g_data_input_stream_read_uint32(conn->input,
    + user->cancellable, &error);
    + if (error != NULL) {
    break;
    + }
    *fields = nm_field_add_number(*fields, tag, 0, method,
    0, val, type);
    @@ -618,6 +581,13 @@
    nm_free_fields(&sub_fields);
    }
    + if (error != NULL) {
    + if (error->code != G_IO_ERROR_WOULD_BLOCK && error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    + }
    +
    return rc;
    }
    --- a/libpurple/protocols/novell/nmconn.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/novell/nmconn.h Mon Nov 04 14:43:49 2019 +0300
    @@ -21,6 +21,8 @@
    #ifndef PURPLE_NOVELL_NMCONN_H
    #define PURPLE_NOVELL_NMCONN_H
    +#include <gio/gio.h>
    +
    typedef struct _NMConn NMConn;
    #include "nmfield.h"
    @@ -44,12 +46,11 @@
    /* A list of requests currently awaiting a response. */
    GSList *requests;
    - /* Data to pass to the callbacks */
    - gpointer data;
    -
    - /* Callbacks for reading/writing */
    - nm_ssl_read_cb read;
    - nm_ssl_write_cb write;
    + /* Connections to server. */
    + GSocketClient *client;
    + GIOStream *stream;
    + GDataInputStream *input;
    + GOutputStream *output;
    };
    /**
    @@ -72,64 +73,9 @@
    void nm_release_conn(NMConn *conn);
    /**
    - * Write len bytes from the given buffer.
    - *
    - * @param conn The connection to write to.
    - * @param buff The buffer to write from.
    - * @param len The number of bytes to write.
    - *
    - * @return The number of bytes written.
    - */
    -int nm_tcp_write(NMConn * conn, const void *buff, int len);
    -
    -/**
    - * Read at most len bytes into the given buffer.
    - *
    - * @param conn The connection to read from.
    - * @param buff The buffer to write to.
    - * @param len The maximum number of bytes to read.
    - *
    - * @return The number of bytes read.
    - */
    -int nm_tcp_read(NMConn * conn, void *buff, int len);
    -
    -/**
    - * Read exactly len bytes into the given buffer.
    - *
    - * @param conn The connection to read from.
    - * @param buff The buffer to write to.
    - * @param len The number of bytes to read.
    - *
    - * @return NM_OK on success, NMERR_TCP_READ if read fails.
    - */
    -NMERR_T nm_read_all(NMConn * conn, char *buf, int len);
    -
    -/**
    - * Read a 32 bit value and convert it to the host byte order.
    - *
    - * @param conn The connection to read from.
    - * @param val A pointer to unsigned 32 bit integer
    - *
    - * @return NM_OK on success, NMERR_TCP_READ if read fails.
    - */
    -NMERR_T
    -nm_read_uint32(NMConn *conn, guint32 *val);
    -
    -/**
    - * Read a 16 bit value and convert it to the host byte order.
    - *
    - * @param conn The connection to read from.
    - * @param val A pointer to unsigned 16 bit integer
    - *
    - * @return NM_OK on success, NMERR_TCP_READ if read fails.
    - */
    -NMERR_T
    -nm_read_uint16(NMConn *conn, guint16 *val);
    -
    -/**
    * Dispatch a request to the server.
    *
    - * @param conn The connection.
    + * @param user The logged-in user.
    * @param cmd The request to dispatch.
    * @param fields The field list for the request.
    * @param cb The response callback for the new request object.
    @@ -139,32 +85,32 @@
    * @return NM_OK on success.
    */
    NMERR_T
    -nm_send_request(NMConn *conn, char *cmd, NMField *fields,
    - nm_response_cb cb, gpointer data, NMRequest **request);
    +nm_send_request(NMUser *user, char *cmd, NMField *fields, nm_response_cb cb,
    + gpointer data, NMRequest **request);
    /**
    * Write out the given field list.
    *
    - * @param conn The connection to write to.
    + * @param user The logged-in user.
    * @param fields The field list to write.
    *
    * @return NM_OK on success.
    */
    -NMERR_T nm_write_fields(NMConn * conn, NMField * fields);
    +NMERR_T nm_write_fields(NMUser *user, NMField *fields);
    /**
    * Read the headers for a response.
    *
    - * @param conn The connection to read from.
    + * @param user The logged-in user.
    *
    * @return NM_OK on success.
    */
    -NMERR_T nm_read_header(NMConn * conn);
    +NMERR_T nm_read_header(NMUser *user);
    /**
    * Read a field list from the connection.
    *
    - * @param conn The connection to read from.
    + * @param user The logged-in user.
    * @param count The maximum number of fields to read (or -1 for no max).
    * @param fields The field list. This is an out param. It
    * should be freed by calling nm_free_fields
    @@ -172,7 +118,7 @@
    *
    * @return NM_OK on success.
    */
    -NMERR_T nm_read_fields(NMConn * conn, int count, NMField ** fields);
    +NMERR_T nm_read_fields(NMUser *user, int count, NMField **fields);
    /**
    * Add a request to the connections request list.
    --- a/libpurple/protocols/novell/nmevent.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/novell/nmevent.c Mon Nov 04 14:43:49 2019 +0300
    @@ -144,31 +144,43 @@
    char *msg = NULL;
    char *nortf = NULL;
    char *guid = NULL;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    }
    /* Read the conference flags */
    - if (rc == NM_OK) {
    - rc = nm_read_uint32(conn, &flags);
    + if (error == NULL) {
    + flags = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    }
    /* Read the message text */
    - if (rc == NM_OK) {
    - rc = nm_read_uint32(conn, &size);
    - if (size > 100000) return NMERR_PROTOCOL;
    + if (error == NULL) {
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 100000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    msg = g_new0(char, size + 1);
    - rc = nm_read_all(conn, msg, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), msg, size,
    + NULL, user->cancellable, &error);
    purple_debug(PURPLE_DEBUG_INFO, "novell", "Message is %s\n", msg);
    @@ -194,6 +206,16 @@
    }
    }
    + if (error != NULL) {
    + if (error->code == G_IO_ERROR_CANCELLED) {
    + rc = NM_OK;
    + } else {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    + return rc;
    + }
    +
    /* Check to see if we already know about the conference */
    conference = nm_conference_list_find(user, guid);
    if (conference) {
    @@ -260,31 +282,42 @@
    char *msg = NULL;
    NMConn *conn;
    NMUserRecord *user_record;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    }
    /* Read the the message */
    - if (rc == NM_OK) {
    - rc = nm_read_uint32(conn, &size);
    - if (size > 100000) return NMERR_PROTOCOL;
    + if (error == NULL) {
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 100000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    msg = g_new0(char, size + 1);
    - rc = nm_read_all(conn, msg, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), msg, size,
    + NULL, user->cancellable, &error);
    }
    }
    /* Store the event data */
    - if (rc == NM_OK) {
    + if (error == NULL) {
    NMConference *conference;
    nm_event_set_text(event, msg);
    @@ -316,6 +349,11 @@
    nm_release_conference(conference);
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(msg);
    @@ -336,16 +374,32 @@
    NMConn *conn;
    NMConference *conference;
    NMUserRecord *user_record;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    + }
    +
    + if (error != NULL) {
    + if (error->code == G_IO_ERROR_CANCELLED) {
    + rc = NM_OK;
    + } else {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    + return rc;
    }
    conference = nm_conference_list_find(user, guid);
    @@ -387,25 +441,36 @@
    char *guid = NULL;
    NMConn *conn;
    NMConference *conference;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    -
    - if (rc == NM_OK) {
    - guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    + guid = g_new0(char, size + 1);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    + }
    +
    + if (error == NULL) {
    conference = nm_conference_list_find(user, guid);
    if (conference) {
    nm_event_set_conference(event, conference);
    } else {
    rc = NMERR_CONFERENCE_NOT_FOUND;
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(guid);
    @@ -425,24 +490,31 @@
    char *guid = NULL;
    NMConference *conference;
    NMConn *conn;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    }
    /* Read the conference flags */
    - if (rc == NM_OK) {
    - rc = nm_read_uint32(conn, &flags);
    + if (error == NULL) {
    + flags = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    conference = nm_conference_list_find(user, guid);
    if (conference) {
    nm_event_set_conference(event, conference);
    @@ -456,6 +528,11 @@
    } else {
    rc = NMERR_CONFERENCE_NOT_FOUND;
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(guid);
    @@ -474,19 +551,25 @@
    char *guid = NULL;
    NMConference *conference;
    NMConn *conn;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    -
    - if (rc == NM_OK) {
    - guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    + guid = g_new0(char, size + 1);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    + }
    +
    + if (error == NULL) {
    conference = nm_conference_list_find(user, guid);
    if (conference) {
    nm_event_set_conference(event, conference);
    @@ -494,6 +577,11 @@
    } else {
    rc = NMERR_CONFERENCE_NOT_FOUND;
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(guid);
    @@ -513,24 +601,31 @@
    NMConn *conn;
    NMConference *conference;
    NMUserRecord *user_record;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    }
    /* Read the conference flags */
    - if (rc == NM_OK) {
    - rc = nm_read_uint32(conn, &flags);
    + if (error == NULL) {
    + flags = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    conference = nm_conference_list_find(user, guid);
    if (conference) {
    nm_conference_set_flags(conference, flags);
    @@ -555,6 +650,11 @@
    } else {
    rc = NMERR_CONFERENCE_NOT_FOUND;
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(guid);
    @@ -571,25 +671,36 @@
    char *guid = NULL;
    NMConference *conference;
    NMConn *conn;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    -
    - if (rc == NM_OK) {
    - guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    + guid = g_new0(char, size + 1);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size, NULL,
    + user->cancellable, &error);
    + }
    +
    + if (error == NULL) {
    conference = nm_conference_list_find(user, guid);
    if (conference) {
    nm_event_set_conference(event, conference);
    } else {
    rc = NMERR_CONFERENCE_NOT_FOUND;
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(guid);
    @@ -609,24 +720,31 @@
    char *text = NULL;
    NMUserRecord *user_record;
    NMConn *conn;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read new status */
    - rc = nm_read_uint16(conn, &status);
    - if (rc == NM_OK) {
    + status = g_data_input_stream_read_uint16(conn->input, user->cancellable,
    + &error);
    + if (error == NULL) {
    /* Read the status text */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 10000) return NMERR_PROTOCOL;
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 10000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    text = g_new0(char, size + 1);
    - rc = nm_read_all(conn, text, size);
    + g_input_stream_read_all(G_INPUT_STREAM(conn->input), text, size,
    + NULL, user->cancellable, &error);
    }
    }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    nm_event_set_text(event, text);
    /* Get a reference to the user record and store the new status */
    @@ -635,6 +753,11 @@
    nm_event_set_user_record(event, user_record);
    nm_user_record_set_status(user_record, status, text);
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(text);
    @@ -650,16 +773,27 @@
    guint32 size = 0;
    char *guid = NULL;
    NMConn *conn;
    + GError *error = NULL;
    conn = nm_user_get_conn(user);
    /* Read the conference guid */
    - rc = nm_read_uint32(conn, &size);
    - if (size > 1000) return NMERR_PROTOCOL;
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (size > 1000) {
    + g_clear_error(&error);
    + return NMERR_PROTOCOL;
    + }
    - if (rc == NM_OK) {
    + if (error == NULL) {
    guid = g_new0(char, size + 1);
    - rc = nm_read_all(conn, guid, size);
    + rc = g_input_stream_read_all(G_INPUT_STREAM(conn->input), guid, size,
    + NULL, user->cancellable, &error);
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    g_free(guid);
    @@ -803,6 +937,7 @@
    char *source = NULL;
    nm_event_cb cb;
    NMConn *conn;
    + GError *error = NULL;
    if (user == NULL)
    return NMERR_BAD_PARM;
    @@ -813,20 +948,22 @@
    conn = nm_user_get_conn(user);
    /* Read the event source */
    - rc = nm_read_uint32(conn, &size);
    - if (rc == NM_OK) {
    + size = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (error == NULL) {
    if (size > 1000000) {
    /* Size is larger than our 1MB sanity check. Ignore it. */
    rc = NMERR_PROTOCOL;
    } else {
    source = g_new0(char, size);
    - rc = nm_read_all(conn, source, size);
    + rc = g_input_stream_read_all(G_INPUT_STREAM(conn->input), source,
    + size, NULL, user->cancellable, &error);
    }
    }
    /* Read the event data */
    - if (rc == NM_OK) {
    + if (error == NULL) {
    event = nm_create_event(type, source, time(0));
    if (event) {
    @@ -901,6 +1038,11 @@
    break;
    }
    }
    + } else {
    + if (error->code != G_IO_ERROR_CANCELLED) {
    + rc = NMERR_TCP_READ;
    + }
    + g_error_free(error);
    }
    if (rc == (NMERR_T)-1) {
    --- a/libpurple/protocols/novell/nmuser.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/novell/nmuser.c Mon Nov 04 14:43:49 2019 +0300
    @@ -56,7 +56,7 @@
    user = g_new0(NMUser, 1);
    -
    + user->cancellable = g_cancellable_new();
    user->contacts =
    g_hash_table_new_full(g_str_hash, nm_utf8_str_equal,
    @@ -81,6 +81,8 @@
    void
    nm_deinitialize_user(NMUser * user)
    {
    + g_cancellable_cancel(user->cancellable);
    +
    nm_release_conn(user->conn);
    if (user->contacts) {
    @@ -104,6 +106,8 @@
    nm_conference_list_free(user);
    nm_destroy_contact_list(user);
    + g_object_unref(user->cancellable);
    +
    g_free(user);
    }
    @@ -135,7 +139,7 @@
    }
    /* Send the login */
    - rc = nm_send_request(user->conn, "login", fields, callback, data, NULL);
    + rc = nm_send_request(user, "login", fields, callback, data, NULL);
    nm_free_fields(&fields);
    return rc;
    @@ -168,7 +172,7 @@
    NMFIELD_TYPE_UTF8);
    }
    - rc = nm_send_request(user->conn, "setstatus", fields, callback, data, NULL);
    + rc = nm_send_request(user, "setstatus", fields, callback, data, NULL);
    nm_free_fields(&fields);
    return rc;
    @@ -191,7 +195,7 @@
    g_strdup(node->data), NMFIELD_TYPE_UTF8);
    }
    - rc = nm_send_request(user->conn, "getdetails", fields, callback, data, NULL);
    + rc = nm_send_request(user, "getdetails", fields, callback, data, NULL);
    nm_free_fields(&fields);
    return rc;
    @@ -225,7 +229,7 @@
    }
    - rc = nm_send_request(user->conn, "getdetails", fields, callback, data, NULL);
    + rc = nm_send_request(user, "getdetails", fields, callback, data, NULL);
    nm_free_fields(&fields);
    return rc;
    @@ -277,7 +281,7 @@
    NMFIELD_TYPE_DN);
    }
    - rc = nm_send_request(user->conn, "createconf", fields, callback, data, &req);
    + rc = nm_send_request(user, "createconf", fields, callback, data, &req);
    if (rc == NM_OK && req) {
    nm_conference_add_ref(conference);
    nm_request_set_data(req, conference);
    @@ -313,7 +317,7 @@
    tmp = NULL;
    /* Send the request to the server */
    - rc = nm_send_request(user->conn, "leaveconf", fields, callback, data, &req);
    + rc = nm_send_request(user, "leaveconf", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, conference);
    @@ -346,7 +350,7 @@
    tmp = NULL;
    /* Send the request to the server */
    - rc = nm_send_request(user->conn, "joinconf", fields, callback, data, &req);
    + rc = nm_send_request(user, "joinconf", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, conference);
    @@ -380,7 +384,7 @@
    tmp = NULL;
    /* Send the request to the server */
    - rc = nm_send_request(user->conn, "rejectconf", fields, callback, data, &req);
    + rc = nm_send_request(user, "rejectconf", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, conference);
    @@ -424,7 +428,7 @@
    g_strdup(message), NMFIELD_TYPE_UTF8);
    /* Send the request to the server */
    - rc = nm_send_request(user->conn, "sendinvite", fields, callback, data, &req);
    + rc = nm_send_request(user, "sendinvite", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, conference);
    @@ -501,7 +505,7 @@
    }
    /* Send the request */
    - rc = nm_send_request(user->conn, "sendmessage", fields, callback, NULL, NULL);
    + rc = nm_send_request(user, "sendmessage", fields, callback, NULL, NULL);
    }
    nm_free_fields(&fields);
    @@ -541,7 +545,7 @@
    tmp, NMFIELD_TYPE_ARRAY);
    tmp = NULL;
    - rc = nm_send_request(user->conn, "sendtyping", fields, callback, NULL, NULL);
    + rc = nm_send_request(user, "sendtyping", fields, callback, NULL, NULL);
    }
    nm_free_fields(&fields);
    @@ -592,7 +596,7 @@
    g_strdup(display_name), NMFIELD_TYPE_UTF8);
    /* Dispatch the request */
    - rc = nm_send_request(user->conn, "createcontact", fields, callback, data, &req);
    + rc = nm_send_request(user, "createcontact", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, contact);
    @@ -627,7 +631,7 @@
    NMFIELD_TYPE_UTF8);
    /* Dispatch the request */
    - rc = nm_send_request(user->conn, "deletecontact", fields, callback, data, &req);
    + rc = nm_send_request(user, "deletecontact", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, contact);
    @@ -665,7 +669,7 @@
    g_strdup("-1"), NMFIELD_TYPE_UTF8);
    /* Dispatch the request */
    - rc = nm_send_request(user->conn, "createfolder", fields, callback, data, &req);
    + rc = nm_send_request(user, "createfolder", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, g_strdup(name));
    @@ -694,7 +698,7 @@
    NMFIELD_TYPE_UTF8);
    /* Dispatch the request */
    - rc = nm_send_request(user->conn, "deletecontact", fields, callback, data, &req);
    + rc = nm_send_request(user, "deletecontact", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, folder);
    @@ -726,7 +730,7 @@
    g_strdup(dn), NMFIELD_TYPE_UTF8);
    /* Dispatch the request */
    - rc = nm_send_request(user->conn, "getstatus", fields, callback, data, &req);
    + rc = nm_send_request(user, "getstatus", fields, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, user_record);
    @@ -775,7 +779,8 @@
    0, fields, NMFIELD_TYPE_ARRAY);
    fields = NULL;
    - rc = nm_send_request(user->conn, "updateitem", list, callback, data, &req);
    + rc = nm_send_request(user, "updateitem", list, callback, data,
    + &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, contact);
    }
    @@ -828,7 +833,8 @@
    0, fields, NMFIELD_TYPE_ARRAY);
    fields = NULL;
    - rc = nm_send_request(user->conn, "updateitem", list, callback, data, &req);
    + rc = nm_send_request(user, "updateitem", list, callback, data,
    + &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, folder);
    }
    @@ -877,7 +883,7 @@
    NMFIELD_TYPE_UTF8);
    /* Dispatch the request */
    - rc = nm_send_request(user->conn, "movecontact", list, callback, data, &req);
    + rc = nm_send_request(user, "movecontact", list, callback, data, &req);
    if (rc == NM_OK && req)
    nm_request_set_data(req, contact);
    @@ -912,7 +918,7 @@
    fields = nm_field_add_pointer(fields, tag, 0, NMFIELD_METHOD_ADD, 0,
    g_strdup(who), NMFIELD_TYPE_UTF8);
    - rc = nm_send_request(user->conn, "createblock", fields, callback, data, NULL);
    + rc = nm_send_request(user, "createblock", fields, callback, data, NULL);
    nm_free_fields(&fields);
    return rc;
    @@ -946,7 +952,7 @@
    fields = nm_field_add_pointer(fields, tag, 0, NMFIELD_METHOD_DELETE, 0,
    g_strdup(dn), NMFIELD_TYPE_DN);
    - rc = nm_send_request(user->conn, "updateblocks", fields, callback, data, NULL);
    + rc = nm_send_request(user, "updateblocks", fields, callback, data, NULL);
    nm_free_fields(&fields);
    return rc;
    @@ -967,7 +973,7 @@
    (default_deny ? g_strdup("1") : g_strdup("0")),
    NMFIELD_TYPE_UTF8);
    - rc = nm_send_request(user->conn, "updateblocks", fields, callback, data, NULL);
    + rc = nm_send_request(user, "updateblocks", fields, callback, data, NULL);
    nm_free_fields(&fields);
    return rc;
    @@ -981,7 +987,7 @@
    if (user == NULL)
    return NMERR_BAD_PARM;
    - rc = nm_send_request(user->conn, "ping", NULL, callback, data, NULL);
    + rc = nm_send_request(user, "ping", NULL, callback, data, NULL);
    return rc;
    }
    @@ -992,6 +998,7 @@
    NMConn *conn;
    NMERR_T rc = NM_OK;
    guint32 val;
    + GError *error = NULL;
    if (user == NULL)
    return NMERR_BAD_PARM;
    @@ -999,18 +1006,22 @@
    conn = user->conn;
    /* Check to see if this is an event or a response */
    - rc = nm_read_all(conn, (char *) &val, sizeof(val));
    - if (rc == NM_OK) {
    - if (strncmp((char *) &val, "HTTP", strlen("HTTP")) == 0)
    + val = g_data_input_stream_read_uint32(conn->input, user->cancellable,
    + &error);
    + if (error == NULL) {
    + if (val == ('H' + ('T' << 8) + ('T' << 16) + ('P' << 24))) {
    rc = nm_process_response(user);
    - else
    - rc = nm_process_event(user, GUINT32_FROM_LE(val));
    -
    + } else {
    + rc = nm_process_event(user, val);
    + }
    } else {
    - if (errno == EAGAIN)
    + if (error->code == G_IO_ERROR_WOULD_BLOCK || error->code == G_IO_ERROR_CANCELLED) {
    + /* Try again later or ignore. */
    rc = NM_OK;
    - else
    + } else {
    rc = NMERR_PROTOCOL;
    + }
    + g_error_free(error);
    }
    return rc;
    @@ -1753,9 +1764,9 @@
    NMConn *conn = user->conn;
    NMRequest *req = NULL;
    - rc = nm_read_header(conn);
    + rc = nm_read_header(user);
    if (rc == NM_OK) {
    - rc = nm_read_fields(conn, -1, &fields);
    + rc = nm_read_fields(user, -1, &fields);
    }
    if (rc == NM_OK) {
    --- a/libpurple/protocols/novell/nmuser.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/novell/nmuser.h Mon Nov 04 14:43:49 2019 +0300
    @@ -67,6 +67,7 @@
    /* This represents user that we are currently logged in as */
    struct _NMUser
    {
    + GCancellable *cancellable;
    char *name;
    --- a/libpurple/protocols/novell/novell.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/novell/novell.c Mon Nov 04 14:43:49 2019 +0300
    @@ -27,9 +27,9 @@
    #include "nmuser.h"
    #include "notify.h"
    #include "novell.h"
    +#include "purple-gio.h"
    #include "purpleaccountoption.h"
    #include "util.h"
    -#include "sslconn.h"
    #include "request.h"
    #include "network.h"
    #include "status.h"
    @@ -1672,22 +1672,7 @@
    ******************************************************************************/
    static void
    -novell_ssl_connect_error(PurpleSslConnection * gsc,
    - PurpleSslErrorType error, gpointer data)
    -{
    - PurpleConnection *gc;
    - NMUser *user;
    -
    - gc = data;
    - user = purple_connection_get_protocol_data(gc);
    - user->conn->data = NULL;
    -
    - purple_connection_ssl_error (gc, error);
    -}
    -
    -static void
    -novell_ssl_recv_cb(gpointer data, PurpleSslConnection * gsc,
    - PurpleInputCondition condition)
    +novell_ssl_recv_cb(GObject *stream, gpointer data)
    {
    PurpleConnection *gc = data;
    NMUser *user;
    @@ -1716,19 +1701,28 @@
    }
    static void
    -novell_ssl_connected_cb(gpointer data, PurpleSslConnection * gsc,
    - PurpleInputCondition cond)
    +novell_login_callback(GObject *source_object, GAsyncResult *res, gpointer data)
    {
    + GSocketClient *client = G_SOCKET_CLIENT(source_object);
    PurpleConnection *gc = data;
    + GSocketConnection *sockconn;
    NMUser *user;
    NMConn *conn;
    NMERR_T rc = 0;
    const char *pwd = NULL;
    - const char *my_addr = NULL;
    + gchar *my_addr = NULL;
    char *ua = NULL;
    -
    - if (gc == NULL || gsc == NULL)
    + GError *error = NULL;
    +
    + sockconn = g_socket_client_connect_to_host_finish(client, res, &error);
    + if (sockconn == NULL) {
    + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + g_error_free(error);
    + } else {
    + purple_connection_take_error(gc, error);
    + }
    return;
    + }
    user = purple_connection_get_protocol_data(gc);
    if ((user == NULL) || (conn = user->conn) == NULL)
    @@ -1737,13 +1731,27 @@
    purple_connection_update_progress(gc, _("Authenticating..."),
    2, NOVELL_CONNECT_STEPS);
    - my_addr = purple_network_get_my_ip(gsc->fd);
    + conn->stream = G_IO_STREAM(sockconn);
    + conn->input =
    + g_data_input_stream_new(g_io_stream_get_input_stream(conn->stream));
    + conn->output = g_io_stream_get_output_stream(conn->stream);
    +
    + g_data_input_stream_set_byte_order(conn->input,
    + G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN);
    + g_data_input_stream_set_newline_type(conn->input,
    + G_DATA_STREAM_NEWLINE_TYPE_LF);
    +
    + my_addr = purple_network_get_my_ip_from_gio(sockconn);
    pwd = purple_connection_get_password(gc);
    ua = _user_agent_string();
    rc = nm_send_login(user, pwd, my_addr, ua, _login_resp_cb, NULL);
    if (rc == NM_OK) {
    - purple_ssl_input_add(gsc, novell_ssl_recv_cb, gc);
    + GSource *source;
    + source = g_pollable_input_stream_create_source(
    + G_POLLABLE_INPUT_STREAM(conn->input), user->cancellable);
    + g_source_set_callback(source, (GSourceFunc)novell_ssl_recv_cb, gc,
    + NULL);
    } else {
    purple_connection_error(gc,
    PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    @@ -1754,6 +1762,7 @@
    3, NOVELL_CONNECT_STEPS);
    g_free(ua);
    + g_free(my_addr);
    }
    /*******************************************************************************
    @@ -2167,6 +2176,7 @@
    const char *server;
    const char *name;
    int port;
    + GError *error = NULL;
    if (account == NULL)
    return;
    @@ -2204,17 +2214,16 @@
    purple_connection_update_progress(gc, _("Connecting"),
    1, NOVELL_CONNECT_STEPS);
    - user->conn->read = (nm_ssl_read_cb)purple_ssl_read;
    - user->conn->write = (nm_ssl_write_cb)purple_ssl_write;
    -
    - user->conn->data = purple_ssl_connect(
    - user->client_data, user->conn->addr, user->conn->port,
    - novell_ssl_connected_cb, novell_ssl_connect_error, gc);
    - if (user->conn->data == NULL) {
    - purple_connection_error(gc,
    - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT,
    - _("SSL support unavailable"));
    + user->conn->client = purple_gio_socket_client_new(account, &error);
    + if (user->conn->client == NULL) {
    + purple_connection_take_error(gc, error);
    + return;
    }
    +
    + g_socket_client_set_tls(user->conn->client, TRUE);
    + g_socket_client_connect_to_host_async(
    + user->conn->client, user->conn->addr, user->conn->port,
    + user->cancellable, novell_login_callback, gc);
    }
    }
    @@ -2231,7 +2240,7 @@
    if (user) {
    conn = user->conn;
    if (conn) {
    - purple_ssl_close(user->conn->data);
    + nm_release_conn(conn);
    }
    nm_deinitialize_user(user);
    }
    --- a/libpurple/protocols/sametime/sametime.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/sametime/sametime.c Mon Nov 04 14:43:49 2019 +0300
    @@ -812,7 +812,7 @@
    static gboolean buddy_is_external(PurpleBuddy *b) {
    g_return_val_if_fail(b != NULL, FALSE);
    - return purple_str_has_prefix(purple_buddy_get_name(b), "@E ");
    + return g_str_has_prefix(purple_buddy_get_name(b), "@E ");
    }
    @@ -3869,9 +3869,10 @@
    b = purple_blist_find_buddy(acct, who);
    user_info = purple_notify_user_info_new();
    - if(purple_str_has_prefix(who, "@E ")) {
    - purple_notify_user_info_add_pair_html(user_info, _("External User"), NULL);
    - }
    + if (g_str_has_prefix(who, "@E ")) {
    + purple_notify_user_info_add_pair_html(user_info, _("External User"),
    + NULL);
    + }
    purple_notify_user_info_add_pair_plaintext(user_info, _("User ID"), who);
    --- a/libpurple/protocols/silc/ops.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/silc/ops.c Mon Nov 04 14:43:49 2019 +0300
    @@ -182,7 +182,7 @@
    }
    /* Image */
    - if (purple_str_has_prefix(type, "image/")) {
    + if (g_str_has_prefix(type, "image/")) {
    char tmp[32];
    PurpleImage *img;
    guint img_id;
    --- a/libpurple/protocols/silc/silc.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/silc/silc.c Mon Nov 04 14:43:49 2019 +0300
    @@ -42,6 +42,25 @@
    return TRUE;
    }
    +static void
    +silcpurple_free(SilcPurple sg)
    +{
    + g_return_if_fail(sg != NULL);
    +
    + g_cancellable_cancel(sg->cancellable);
    + g_clear_object(&sg->cancellable);
    + g_clear_object(&sg->sockconn);
    +
    + g_clear_pointer(&sg->client, silc_client_free);
    + g_clear_pointer(&sg->sha1hash, silc_hash_free);
    + g_clear_pointer(&sg->mimeass, silc_mime_assembler_free);
    +
    + g_clear_pointer(&sg->public_key, silc_pkcs_public_key_free);
    + g_clear_pointer(&sg->private_key, silc_pkcs_private_key_free);
    +
    + silc_free(sg);
    +}
    +
    static const char *
    silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b)
    {
    @@ -135,15 +154,6 @@
    NULL, 0);
    }
    -#if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1)
    -static gboolean
    -silcpurple_scheduler(gpointer *context)
    -{
    - SilcClient client = (SilcClient)context;
    - silc_client_run_one(client);
    - return TRUE;
    -}
    -#else
    typedef struct {
    SilcPurple sg;
    SilcUInt32 fd;
    @@ -241,7 +251,6 @@
    }
    }
    }
    -#endif /* __SILC_TOOLKIT_VERSION */
    static void
    silcpurple_connect_cb(SilcClient client, SilcClientConnection conn,
    @@ -378,9 +387,7 @@
    purple_connection_error(gc,
    PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    _("Connection failed"));
    - silc_pkcs_public_key_free(sg->public_key);
    - silc_pkcs_private_key_free(sg->private_key);
    - silc_free(sg);
    + silcpurple_free(sg);
    purple_connection_set_protocol_data(gc, NULL);
    return;
    }
    @@ -413,29 +420,43 @@
    }
    static void
    -silcpurple_login_connected(gpointer data, gint source, const gchar *error_message)
    +silcpurple_login_connected(GObject *source, GAsyncResult *res, gpointer data)
    {
    PurpleConnection *gc = data;
    SilcPurple sg;
    + GSocketConnection *conn;
    + GSocket *socket;
    + gint fd;
    + GError *error = NULL;
    g_return_if_fail(gc != NULL);
    sg = purple_connection_get_protocol_data(gc);
    - if (source < 0) {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Connection failed"));
    - silc_pkcs_public_key_free(sg->public_key);
    - silc_pkcs_private_key_free(sg->private_key);
    - silc_free(sg);
    - purple_connection_set_protocol_data(gc, NULL);
    + conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    + res, &error);
    + if (conn == NULL) {
    + if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    + _("Connection failed"));
    + silcpurple_free(sg);
    + purple_connection_set_protocol_data(gc, NULL);
    + }
    +
    + g_clear_error(&error);
    return;
    }
    + socket = g_socket_connection_get_socket(conn);
    + g_assert(socket != NULL);
    +
    + fd = g_socket_get_fd(socket);
    + sg->sockconn = conn;
    +
    silc_hash_alloc((unsigned char *)"sha1", &sg->sha1hash);
    /* Wrap socket to TCP stream */
    - silc_socket_tcp_stream_create(source, TRUE, FALSE,
    + silc_socket_tcp_stream_create(fd, TRUE, FALSE,
    sg->client->schedule,
    silcpurple_stream_created, gc);
    }
    @@ -444,20 +465,27 @@
    {
    PurpleConnection *gc = sg->gc;
    PurpleAccount *account = purple_connection_get_account(gc);
    + GSocketClient *client;
    +
    + client = purple_gio_socket_client_new(account, &error);
    + if (client == NULL) {
    + /* Assume it's a proxy error */
    + purple_notify_error(NULL, NULL, _("Invalid proxy settings"),
    + error->message,
    + purple_request_cpar_from_account(account));
    + purple_connection_take_error(gc, error);
    + purple_connection_set_protocol_data(gc, NULL);
    + silcpurple_free(sg);
    + return;
    + }
    /* Connect to the SILC server */
    - if (purple_proxy_connect(gc, account,
    + g_socket_client_connect_to_host_async(client,
    purple_account_get_string(account, "server",
    "silc.silcnet.org"),
    purple_account_get_int(account, "port", 706),
    - silcpurple_login_connected, gc) == NULL)
    - {
    - purple_connection_error(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
    - _("Unable to connect"));
    - purple_connection_set_protocol_data(gc, NULL);
    - silc_free(sg);
    - return;
    - }
    + sg->cancellable, silcpurple_login_connected, gc);
    + g_object_unref(client);
    }
    static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields *fields)
    @@ -480,7 +508,7 @@
    _("Password is required to sign on."), NULL,
    purple_request_cpar_from_connection(gc));
    purple_connection_set_protocol_data(gc, NULL);
    - silc_free(sg);
    + silcpurple_free(sg);
    return;
    }
    @@ -499,7 +527,7 @@
    purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    _("Unable to load SILC key pair"));
    purple_connection_set_protocol_data(gc, NULL);
    - silc_free(sg);
    + silcpurple_free(sg);
    return;
    }
    silcpurple_continue_running(sg);
    @@ -516,7 +544,7 @@
    purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    _("Unable to load SILC key pair"));
    purple_connection_set_protocol_data(gc, NULL);
    - silc_free(sg);
    + silcpurple_free(sg);
    }
    static void silcpurple_running(SilcClient client, void *context)
    @@ -545,7 +573,7 @@
    purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    _("Unable to load SILC key pair"));
    purple_connection_set_protocol_data(gc, NULL);
    - silc_free(sg);
    + silcpurple_free(sg);
    return;
    }
    silcpurple_continue_running(sg);
    @@ -616,6 +644,7 @@
    sg = silc_calloc(1, sizeof(*sg));
    if (!sg)
    return;
    + sg->cancellable = g_cancellable_new();
    sg->client = client;
    sg->gc = gc;
    sg->account = account;
    @@ -627,7 +656,7 @@
    purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    _("Unable to initialize SILC protocol"));
    purple_connection_set_protocol_data(gc, NULL);
    - silc_free(sg);
    + silcpurple_free(sg);
    silc_free(hostname);
    g_free(username);
    return;
    @@ -640,20 +669,15 @@
    purple_connection_error(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR,
    _("Error loading SILC key pair"));
    purple_connection_set_protocol_data(gc, NULL);
    - silc_free(sg);
    + silcpurple_free(sg);
    return;
    }
    -#if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1)
    - /* Schedule SILC using Glib's event loop */
    - sg->scheduler = g_timeout_add(300, (GSourceFunc)silcpurple_scheduler, client);
    -#else
    /* Run SILC scheduler */
    sg->tasks = silc_dlist_init();
    silc_schedule_set_notify(client->schedule, silcpurple_scheduler,
    client);
    silc_client_run_one(client);
    -#endif /* __SILC_TOOLKIT_VERSION */
    }
    static int
    @@ -664,12 +688,8 @@
    purple_debug_info("silc", "Finalizing SilcPurple %p\n", sg);
    silc_client_stop(sg->client, NULL, NULL);
    - silc_client_free(sg->client);
    - if (sg->sha1hash)
    - silc_hash_free(sg->sha1hash);
    - if (sg->mimeass)
    - silc_mime_assembler_free(sg->mimeass);
    - silc_free(sg);
    +
    + silcpurple_free(sg);
    return 0;
    }
    @@ -677,9 +697,7 @@
    silcpurple_close(PurpleConnection *gc)
    {
    SilcPurple sg = purple_connection_get_protocol_data(gc);
    -#if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
    SilcPurpleTask task;
    -#endif /* __SILC_TOOLKIT_VERSION */
    GHashTable *ui_info;
    const char *ui_name = NULL, *ui_website = NULL;
    char *quit_msg;
    @@ -709,7 +727,6 @@
    if (sg->conn)
    silc_client_close_connection(sg->client, sg->conn);
    -#if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1)
    if (sg->conn)
    silc_client_run_one(sg->client);
    silc_schedule_set_notify(sg->client->schedule, NULL, NULL);
    @@ -720,7 +737,6 @@
    silc_free(task);
    }
    silc_dlist_uninit(sg->tasks);
    -#endif /* __SILC_TOOLKIT_VERSION */
    if (sg->scheduler)
    g_source_remove(sg->scheduler);
    --- a/libpurple/protocols/silc/silcpurple.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/silc/silcpurple.h Mon Nov 04 14:43:49 2019 +0300
    @@ -37,9 +37,6 @@
    #include "server.h"
    #include "util.h"
    -#undef SILC_VERSION
    -#define SILC_VERSION(a, b, c) (((a) << 24) + ((b) << 16) + ((c) << 8))
    -
    #define SILCPURPLE_TYPE_PROTOCOL (silcpurple_protocol_get_type())
    #define SILCPURPLE_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SILCPURPLE_TYPE_PROTOCOL, SilcProtocol))
    #define SILCPURPLE_PROTOCOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SILCPURPLE_TYPE_PROTOCOL, SilcProtocolClass))
    @@ -86,6 +83,9 @@
    /* The SILC Purple plugin context */
    typedef struct SilcPurpleStruct {
    + GCancellable *cancellable;
    + GSocketConnection *sockconn;
    +
    SilcClient client;
    SilcClientConnection conn;
    SilcPublicKey public_key;
    --- a/libpurple/protocols/simple/simple.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/simple/simple.c Mon Nov 04 14:43:49 2019 +0300
    @@ -117,7 +117,6 @@
    purple_debug_error("simple", "failed sending keep alive\n");
    }
    }
    - return;
    }
    static gboolean process_register_response(struct simple_account_data *sip, struct sipmsg *msg, struct transaction *tc);
    @@ -1288,8 +1287,7 @@
    while (ssparts[i])
    {
    g_strchug(ssparts[i]);
    - if (purple_str_has_prefix(ssparts[i], "terminated"))
    - {
    + if (g_str_has_prefix(ssparts[i], "terminated")) {
    purple_debug_info("simple", "Subscription expired!");
    if (b->dialog)
    {
    --- a/libpurple/protocols/zephyr/Zinternal.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/zephyr/Zinternal.c Mon Nov 04 14:43:49 2019 +0300
    @@ -850,7 +850,6 @@
    qptr->prev->next = qptr->next;
    qptr->next->prev = qptr->prev;
    free ((char *)qptr);
    - return;
    }
    Code_t Z_SendFragmentedNotice(notice, len, cert_func, send_func)
    --- a/libpurple/protocols/zephyr/zephyr.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/protocols/zephyr/zephyr.c Mon Nov 04 14:43:49 2019 +0300
    @@ -55,10 +55,7 @@
    static GSList *cmds = NULL;
    extern Code_t ZGetLocations(ZLocations_t *, int *);
    -extern Code_t ZSetLocation(char *);
    -extern Code_t ZUnsetLocation(void);
    extern Code_t ZGetSubscriptions(ZSubscription_t *, int*);
    -extern char __Zephyr_realm[];
    typedef struct _zframe zframe;
    typedef struct _zephyr_triple zephyr_triple;
    typedef struct _zephyr_account zephyr_account;
    @@ -1333,25 +1330,9 @@
    /* XXX add real error reporting */
    /* doesn't matter if this fails or not; we'll just move on to the next one */
    if (use_zeph02(zephyr)) {
    -#ifdef WIN32
    - int numlocs;
    - int one=1;
    - ZLocateUser(chk,&numlocs,ZAUTH);
    - if (numlocs) {
    - int i;
    - for(i=0;i<numlocs;i++) {
    - ZGetLocations(&locations,&one);
    - if (nlocs>0)
    - purple_protocol_got_user_status(account,name,"available",NULL);
    - else
    - purple_protocol_got_user_status(account,name,"offline",NULL);
    - }
    - }
    -#else
    ZRequestLocations(chk, &ald, UNACKED, ZAUTH);
    g_free(ald.user);
    g_free(ald.version);
    -#endif /* WIN32 */
    } else
    if (use_tzc(zephyr)) {
    gchar *zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",chk);
    @@ -1419,8 +1400,6 @@
    }
    g_strlcpy(zephyr->ourhostcanon, hent->h_name, sizeof(zephyr->ourhostcanon));
    -
    - return;
    }
    static void process_zsubs(zephyr_account *zephyr)
    @@ -2601,7 +2580,6 @@
    topic_utf8 = zephyr_recv_convert(gc,(gchar *)topic);
    purple_chat_conversation_set_topic(gcc,sender,topic_utf8);
    g_free(topic_utf8);
    - return;
    }
    /* commands */
    --- a/libpurple/upnp.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/upnp.c Mon Nov 04 14:43:49 2019 +0300
    @@ -340,8 +340,8 @@
    }
    tmp = purple_xmlnode_get_data(controlURLNode);
    - if(baseURL && !purple_str_has_prefix(tmp, "http://") &&
    - !purple_str_has_prefix(tmp, "HTTP://")) {
    + if (baseURL && !g_str_has_prefix(tmp, "http://") &&
    + !g_str_has_prefix(tmp, "HTTP://")) {
    /* Handle absolute paths in a relative URL. This probably
    * belongs in util.c. */
    if (tmp[0] == '/') {
    @@ -355,7 +355,7 @@
    controlURL = g_strdup_printf("%s%s", baseURL, tmp);
    }
    g_free(tmp);
    - }else{
    + } else {
    controlURL = tmp;
    }
    g_free(baseURL);
    --- a/libpurple/util.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/util.c Mon Nov 04 14:43:49 2019 +0300
    @@ -35,15 +35,9 @@
    static gchar *config_dir = NULL;
    static gchar *data_dir = NULL;
    -static JsonNode *escape_js_node = NULL;
    -static JsonGenerator *escape_js_gen = NULL;
    -
    void
    purple_util_init(void)
    {
    - escape_js_node = json_node_new(JSON_NODE_VALUE);
    - escape_js_gen = json_generator_new();
    - json_node_set_boolean(escape_js_node, FALSE);
    }
    void
    @@ -65,12 +59,6 @@
    g_free(data_dir);
    data_dir = NULL;
    -
    - json_node_free(escape_js_node);
    - escape_js_node = NULL;
    -
    - g_object_unref(escape_js_gen);
    - escape_js_gen = NULL;
    }
    /**************************************************************************
    @@ -573,333 +561,6 @@
    return retval;
    }
    -char *
    -purple_uts35_to_str(const char *format, size_t len, struct tm *tm)
    -{
    - GString *string;
    - guint i, count;
    -
    - if (tm == NULL) {
    - time_t now = time(NULL);
    - tm = localtime(&now);
    - }
    -
    - string = g_string_sized_new(len);
    - i = 0;
    - while (i < len) {
    - count = 1;
    - while ((i + count) < len && format[i] == format[i+count])
    - count++;
    -
    - switch (format[i]) {
    - /* Era Designator */
    - case 'G':
    - if (count <= 3) {
    - /* Abbreviated */
    - } else if (count == 4) {
    - /* Full */
    - } else if (count >= 5) {
    - /* Narrow */
    - count = 5;
    - }
    - break;
    -
    -
    - /* Year */
    - case 'y':
    - if (count == 2) {
    - /* Two-digits only */
    - g_string_append(string, purple_utf8_strftime("%y", tm));
    - } else {
    - /* Zero-padding */
    - g_string_append_printf(string, "%0*d",
    - count,
    - tm->tm_year + 1900);
    - }
    - break;
    -
    - /* Year (in "Week of Year" based calendars) */
    - case 'Y':
    - if (count == 2) {
    - /* Two-digits only */
    - } else {
    - /* Zero-padding */
    - }
    - break;
    -
    - /* Extended Year */
    - case 'u':
    - break;
    -
    - /* Cyclic Year Name */
    - case 'U':
    - if (count <= 3) {
    - /* Abbreviated */
    - } else if (count == 4) {
    - /* Full */
    - } else if (count >= 5) {
    - /* Narrow */
    - count = 5;
    - }
    - break;
    -
    -
    - /* Quarter */
    - case 'Q':
    - /* Stand-alone Quarter */
    - case 'q':
    - if (count <= 2) {
    - /* Numerical */
    - } else if (count == 3) {
    - /* Abbreviation */
    - } else if (count >= 4) {
    - /* Full */
    - count = 4;
    - }
    - break;
    -
    - /* Month */
    - case 'M':
    - /* Stand-alone Month */
    - case 'L':
    - if (count <= 2) {
    - /* Numerical */
    - g_string_append(string, purple_utf8_strftime("%m", tm));
    - } else if (count == 3) {
    - /* Abbreviation */
    - g_string_append(string, purple_utf8_strftime("%b", tm));
    - } else if (count == 4) {
    - /* Full */
    - g_string_append(string, purple_utf8_strftime("%B", tm));
    - } else if (count >= 5) {
    - g_string_append_len(string, purple_utf8_strftime("%b", tm), 1);
    - count = 5;
    - }
    - break;
    -
    - /* Ignored */
    - case 'l':
    - break;
    -
    -
    - /* Week of Year */
    - case 'w':
    - g_string_append(string, purple_utf8_strftime("%W", tm));
    - count = MIN(count, 2);
    - break;
    -
    - /* Week of Month */
    - case 'W':
    - count = 1;
    - break;
    -
    -
    - /* Day of Month */
    - case 'd':
    - g_string_append(string, purple_utf8_strftime("%d", tm));
    - count = MIN(count, 2);
    - break;
    -
    - /* Day of Year */
    - case 'D':
    - g_string_append(string, purple_utf8_strftime("%j", tm));
    - count = MIN(count, 3);
    - break;
    -
    - /* Day of Year in Month */
    - case 'F':
    - count = 1;
    - break;
    -
    - /* Modified Julian Day */
    - case 'g':
    - break;
    -
    -
    - /* Day of Week */
    - case 'E':
    - if (count <= 3) {
    - /* Short */
    - g_string_append(string, purple_utf8_strftime("%a", tm));
    - } else if (count == 4) {
    - /* Full */
    - g_string_append(string, purple_utf8_strftime("%A", tm));
    - } else if (count >= 5) {
    - /* Narrow */
    - g_string_append_len(string, purple_utf8_strftime("%a", tm), 1);
    - count = 5;
    - }
    - break;
    -
    - /* Local Day of Week */
    - case 'e':
    - if (count <= 2) {
    - /* Numeric */
    - g_string_append(string, purple_utf8_strftime("%u", tm));
    - } else if (count == 3) {
    - /* Short */
    - g_string_append(string, purple_utf8_strftime("%a", tm));
    - } else if (count == 4) {
    - /* Full */
    - g_string_append(string, purple_utf8_strftime("%A", tm));
    - } else if (count >= 5) {
    - /* Narrow */
    - g_string_append_len(string, purple_utf8_strftime("%a", tm), 1);
    - count = 5;
    - }
    - break;
    -
    - /* Stand-alone Local Day of Week */
    - case 'c':
    - if (count <= 2) {
    - /* Numeric */
    - g_string_append(string, purple_utf8_strftime("%u", tm));
    - count = 1;
    - } else if (count == 3) {
    - /* Short */
    - g_string_append(string, purple_utf8_strftime("%a", tm));
    - } else if (count == 4) {
    - /* Full */
    - g_string_append(string, purple_utf8_strftime("%A", tm));
    - } else if (count >= 5) {
    - /* Narrow */
    - g_string_append_len(string, purple_utf8_strftime("%a", tm), 1);
    - count = 5;
    - }
    - break;
    -
    -
    - /* AM/PM */
    - case 'a':
    - g_string_append(string, purple_utf8_strftime("%p", tm));
    - break;
    -
    -
    - /* Hour (1-12) */
    - case 'h':
    - if (count == 1) {
    - /* No padding */
    - g_string_append(string, purple_utf8_strftime("%I", tm));
    - } else if (count >= 2) {
    - /* Zero-padded */
    - g_string_append(string, purple_utf8_strftime("%I", tm));
    - count = 2;
    - }
    - break;
    -
    - /* Hour (0-23) */
    - case 'H':
    - if (count == 1) {
    - /* No padding */
    - g_string_append(string, purple_utf8_strftime("%H", tm));
    - } else if (count >= 2) {
    - /* Zero-padded */
    - g_string_append(string, purple_utf8_strftime("%H", tm));
    - count = 2;
    - }
    - break;
    -
    - /* Hour (0-11) */
    - case 'K':
    - /* Hour (1-24) */
    - case 'k':
    - if (count == 1) {
    - /* No padding */
    - } else if (count >= 2) {
    - /* Zero-padded */
    - count = 2;
    - }
    - break;
    -
    - /* Hour (hHkK by locale) */
    - case 'j':
    - break;
    -
    -
    - /* Minute */
    - case 'm':
    - g_string_append(string, purple_utf8_strftime("%M", tm));
    - count = MIN(count, 2);
    - break;
    -
    -
    - /* Second */
    - case 's':
    - g_string_append(string, purple_utf8_strftime("%S", tm));
    - count = MIN(count, 2);
    - break;
    -
    - /* Fractional Sub-second */
    - case 'S':
    - break;
    -
    - /* Millisecond */
    - case 'A':
    - break;
    -
    -
    - /* Time Zone (specific non-location format) */
    - case 'z':
    - if (count <= 3) {
    - /* Short */
    - } else if (count >= 4) {
    - /* Full */
    - count = 4;
    - }
    - break;
    -
    - /* Time Zone */
    - case 'Z':
    - if (count <= 3) {
    - /* RFC822 */
    - g_string_append(string, purple_utf8_strftime("%z", tm));
    - } else if (count == 4) {
    - /* Localized GMT */
    - } else if (count >= 5) {
    - /* ISO8601 */
    - g_string_append(string, purple_utf8_strftime("%z", tm));
    - count = 5;
    - }
    - break;
    -
    - /* Time Zone (generic non-location format) */
    - case 'v':
    - if (count <= 3) {
    - /* Short */
    - g_string_append(string, purple_utf8_strftime("%Z", tm));
    - count = 1;
    - } else if (count >= 4) {
    - /* Long */
    - g_string_append(string, purple_utf8_strftime("%Z", tm));
    - count = 4;
    - }
    - break;
    -
    - /* Time Zone */
    - case 'V':
    - if (count <= 3) {
    - /* Same as z */
    - count = 1;
    - } else if (count >= 4) {
    - /* Generic Location Format) */
    - g_string_append(string, purple_utf8_strftime("%Z", tm));
    - count = 4;
    - }
    - break;
    -
    -
    - default:
    - g_string_append_len(string, format + i, count);
    - break;
    - }
    -
    - i += count;
    - }
    -
    - return g_string_free(string, FALSE);
    -}
    -
    -
    /**************************************************************************
    * Markup Functions
    **************************************************************************/
    @@ -1282,100 +943,6 @@
    return found;
    }
    -gboolean
    -purple_markup_extract_info_field(const char *str, int len, PurpleNotifyUserInfo *user_info,
    - const char *start_token, int skip,
    - const char *end_token, char check_value,
    - const char *no_value_token,
    - const char *display_name, gboolean is_link,
    - const char *link_prefix,
    - PurpleInfoFieldFormatCallback format_cb)
    -{
    - const char *p, *q;
    -
    - g_return_val_if_fail(str != NULL, FALSE);
    - g_return_val_if_fail(user_info != NULL, FALSE);
    - g_return_val_if_fail(start_token != NULL, FALSE);
    - g_return_val_if_fail(end_token != NULL, FALSE);
    - g_return_val_if_fail(display_name != NULL, FALSE);
    -
    - p = strstr(str, start_token);
    -
    - if (p == NULL)
    - return FALSE;
    -
    - p += strlen(start_token) + skip;
    -
    - if (p >= str + len)
    - return FALSE;
    -
    - if (check_value != '\0' && *p == check_value)
    - return FALSE;
    -
    - q = strstr(p, end_token);
    -
    - /* Trim leading blanks */
    - while (*p != '\n' && g_ascii_isspace(*p)) {
    - p += 1;
    - }
    -
    - /* Trim trailing blanks */
    - while (q > p && g_ascii_isspace(*(q - 1))) {
    - q -= 1;
    - }
    -
    - /* Don't bother with null strings */
    - if (p == q)
    - return FALSE;
    -
    - if (q != NULL && (!no_value_token ||
    - strncmp(p, no_value_token, strlen(no_value_token)))) {
    - GString *dest = g_string_new("");
    -
    - if (is_link)
    - {
    - g_string_append(dest, "<a href=\"");
    -
    - if (link_prefix)
    - g_string_append(dest, link_prefix);
    -
    - if (format_cb != NULL)
    - {
    - char *reformatted = format_cb(p, q - p);
    - g_string_append(dest, reformatted);
    - g_free(reformatted);
    - }
    - else
    - g_string_append_len(dest, p, q - p);
    - g_string_append(dest, "\">");
    -
    - if (link_prefix)
    - g_string_append(dest, link_prefix);
    -
    - g_string_append_len(dest, p, q - p);
    - g_string_append(dest, "</a>");
    - }
    - else
    - {
    - if (format_cb != NULL)
    - {
    - char *reformatted = format_cb(p, q - p);
    - g_string_append(dest, reformatted);
    - g_free(reformatted);
    - }
    - else
    - g_string_append_len(dest, p, q - p);
    - }
    -
    - purple_notify_user_info_add_pair_html(user_info, display_name, dest->str);
    - g_string_free(dest, TRUE);
    -
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    struct purple_parse_tag {
    char *src_tag;
    char *dest_tag;
    @@ -2599,7 +2166,7 @@
    gboolean xdg_path_exists;
    /* Create destination directory */
    - mkdir_res = purple_build_dir(purple_xdg_dir, S_IRWXU);
    + mkdir_res = g_mkdir_with_parents(purple_xdg_dir, S_IRWXU);
    if (mkdir_res == -1) {
    purple_debug_error("util", "Error creating xdg directory %s: %s; failed migration\n",
    purple_xdg_dir, g_strerror(errno));
    @@ -2648,11 +2215,6 @@
    custom_user_dir = NULL;
    }
    -int purple_build_dir(const char *path, int mode)
    -{
    - return g_mkdir_with_parents(path, mode);
    -}
    -
    static gboolean
    purple_util_write_data_to_file_common(const char *dir, const char *filename, const char *data, gssize size)
    {
    @@ -3112,12 +2674,6 @@
    }
    gboolean
    -purple_str_has_prefix(const char *s, const char *p)
    -{
    - return g_str_has_prefix(s, p);
    -}
    -
    -gboolean
    purple_str_has_caseprefix(const gchar *s, const gchar *p)
    {
    g_return_val_if_fail(s, FALSE);
    @@ -3126,12 +2682,6 @@
    return (g_ascii_strncasecmp(s, p, strlen(p)) == 0);
    }
    -gboolean
    -purple_str_has_suffix(const char *s, const char *x)
    -{
    - return g_str_has_suffix(s, x);
    -}
    -
    char *
    purple_str_add_cr(const char *text)
    {
    @@ -3351,27 +2901,6 @@
    }
    -char *
    -purple_str_binary_to_ascii(const unsigned char *binary, guint len)
    -{
    - GString *ret;
    - guint i;
    -
    - g_return_val_if_fail(len > 0, NULL);
    -
    - ret = g_string_sized_new(len);
    -
    - for (i = 0; i < len; i++)
    - if (binary[i] < 32 || binary[i] > 126)
    - g_string_append_printf(ret, "\\x%02x", binary[i] & 0xFF);
    - else if (binary[i] == '\\')
    - g_string_append(ret, "\\\\");
    - else
    - g_string_append_c(ret, binary[i]);
    -
    - return g_string_free(ret, FALSE);
    -}
    -
    size_t
    purple_utf16_size(const gunichar2 *str)
    {
    @@ -4146,18 +3675,6 @@
    return buf;
    }
    -gchar * purple_escape_js(const gchar *str)
    -{
    - gchar *escaped;
    -
    - json_node_set_string(escape_js_node, str);
    - json_generator_set_root(escape_js_gen, escape_js_node);
    - escaped = json_generator_to_data(escape_js_gen, NULL);
    - json_node_set_boolean(escape_js_node, FALSE);
    -
    - return escaped;
    -}
    -
    void purple_restore_default_signal_handlers(void)
    {
    #ifndef _WIN32
    @@ -4261,12 +3778,6 @@
    g_free(kvp);
    }
    -const gchar *
    -purple_get_host_name(void)
    -{
    - return g_get_host_name();
    -}
    -
    gchar *
    purple_uuid_random(void)
    {
    --- a/libpurple/util.h Mon Nov 04 10:00:14 2019 +0300
    +++ b/libpurple/util.h Mon Nov 04 14:43:49 2019 +0300
    @@ -314,21 +314,6 @@
    */
    GDateTime *purple_str_to_date_time(const char *timestamp, gboolean utc);
    -/**
    - * purple_uts35_to_str:
    - * @format: The formatting string, according to UTS \#35
    - * See http://unicode.org/reports/tr35/
    - * (NOTE: not all formats are supported)
    - * @len: The length of the formatting string
    - * @tm: The time to format, or %NULL to use the current local time
    - *
    - * Formats a datetime according to a UTS-35 Date Format Pattern.
    - *
    - * Returns: The time, formatted as per the user's settings.
    - */
    -char *purple_uts35_to_str(const char *format, size_t len, struct tm *tm);
    -
    -
    /**************************************************************************/
    /* Markup Functions */
    /**************************************************************************/
    @@ -370,38 +355,6 @@
    GData **attributes);
    /**
    - * purple_markup_extract_info_field:
    - * @str: The string to parse.
    - * @len: The size of str.
    - * @user_info: The destination PurpleNotifyUserInfo to which the new
    - * field info should be added.
    - * @start_token: The beginning token.
    - * @skip: The number of characters to skip after the
    - * start token.
    - * @end_token: The ending token.
    - * @check_value: The value that the last character must meet.
    - * @no_value_token: The token indicating no value is given.
    - * @display_name: The short descriptive name to display for this token.
    - * @is_link: TRUE if this should be a link, or FALSE otherwise.
    - * @link_prefix: The prefix for the link.
    - * @format_cb: (scope call): A callback to format the value before adding it.
    - *
    - * Extracts a field of data from HTML.
    - *
    - * This is a scary function. It used to be used for MSN and Yahoo prpls,
    - * but since those prpls have been removed, this is now deprecated.
    - *
    - * Returns: TRUE if successful, or FALSE otherwise.
    - */
    -gboolean purple_markup_extract_info_field(const char *str, int len, PurpleNotifyUserInfo *user_info,
    - const char *start_token, int skip,
    - const char *end_token, char check_value,
    - const char *no_value_token,
    - const char *display_name, gboolean is_link,
    - const char *link_prefix,
    - PurpleInfoFieldFormatCallback format_cb);
    -
    -/**
    * purple_markup_html_to_xhtml:
    * @html: The HTML markup.
    * @dest_xhtml: The destination XHTML output.
    @@ -646,19 +599,6 @@
    void purple_util_set_user_dir(const char *dir);
    /**
    - * purple_build_dir:
    - * @path: The path you wish to create. Note that it must start
    - * from the root or this function will fail.
    - * @mode: Unix-style permissions for this directory.
    - *
    - * Builds a complete path from the root, making any directories along
    - * the path which do not already exist.
    - *
    - * Returns: 0 for success, nonzero on any error.
    - */
    -int purple_build_dir(const char *path, int mode);
    -
    -/**
    * purple_util_write_data_to_file:
    * @filename: The basename of the file to write in the purple_user_dir.
    * @data: A string of data to write.
    @@ -995,18 +935,6 @@
    gboolean purple_validate(const PurpleProtocol *protocol, const char *str);
    /**
    - * purple_str_has_prefix:
    - * @s: The string to check.
    - * @p: The prefix in question.
    - *
    - * Compares two strings to see if the first contains the second as
    - * a proper prefix.
    - *
    - * Returns: TRUE if p is a prefix of s, otherwise FALSE.
    - */
    -gboolean purple_str_has_prefix(const char *s, const char *p);
    -
    -/**
    * purple_str_has_caseprefix:
    * @s: The string to check.
    * @p: The prefix in question.
    @@ -1020,18 +948,6 @@
    purple_str_has_caseprefix(const gchar *s, const gchar *p);
    /**
    - * purple_str_has_suffix:
    - * @s: The string to check.
    - * @x: The suffix in question.
    - *
    - * Compares two strings to see if the second is a proper suffix
    - * of the first.
    - *
    - * Returns: TRUE if x is a a suffix of s, otherwise FALSE.
    - */
    -gboolean purple_str_has_suffix(const char *s, const char *x);
    -
    -/**
    * purple_strdup_withhtml:
    * @src: The source string.
    *
    @@ -1165,22 +1081,6 @@
    char *purple_str_seconds_to_string(guint sec);
    /**
    - * purple_str_binary_to_ascii:
    - * @binary: A string of random data, possibly with embedded NULs
    - * and such.
    - * @len: The length in bytes of the input string. Must not be 0.
    - *
    - * Converts a binary string into a NUL terminated ascii string,
    - * replacing nonascii characters and characters below SPACE (including
    - * NUL) into \\xyy, where yy are two hex digits. Also backslashes are
    - * changed into two backslashes (\\\\). The returned, newly allocated
    - * string can be outputted to the console, and must be g_free()d.
    - *
    - * Returns: A newly allocated ASCIIZ string.
    - */
    -char *purple_str_binary_to_ascii(const unsigned char *binary, guint len);
    -
    -/**
    * purple_utf16_size:
    * @str: String to check.
    *
    @@ -1252,16 +1152,6 @@
    gboolean purple_email_is_valid(const char *address);
    /**
    - * purple_ipv6_address_is_valid:
    - * @ip: The IP address to validate.
    - *
    - * Checks if the given IP address is a syntactically valid IPv6 address.
    - *
    - * Returns: True if the IP address is syntactically correct.
    - */
    -gboolean purple_ipv6_address_is_valid(const char *ip);
    -
    -/**
    * purple_uri_list_extract_uris:
    * @uri_list: An uri-list in the standard format.
    *
    @@ -1429,16 +1319,6 @@
    const char *purple_escape_filename(const char *str);
    /**
    - * purple_escape_js:
    - * @str: The string to escape.
    - *
    - * Escapes javascript-unfriendly substrings from a string.
    - *
    - * Returns: The javascript-safe string (must be g_free'd after use).
    - */
    -gchar * purple_escape_js(const gchar *str);
    -
    -/**
    * purple_restore_default_signal_handlers:
    *
    * Restore default signal handlers for signals which might reasonably have
    @@ -1448,16 +1328,6 @@
    void purple_restore_default_signal_handlers(void);
    /**
    - * purple_get_host_name:
    - *
    - * Gets the host name of the machine. If it not possible to determine the
    - * host name, "localhost" is returned
    - *
    - * Returns: The hostname
    - */
    -const gchar *purple_get_host_name(void);
    -
    -/**
    * purple_uuid_random:
    *
    * Returns a type 4 (random) UUID
    --- a/meson.build Mon Nov 04 10:00:14 2019 +0300
    +++ b/meson.build Mon Nov 04 14:43:49 2019 +0300
    @@ -455,7 +455,7 @@
    #######################################################################
    # Check for SILC client includes and libraries
    #######################################################################
    -silc = dependency('silcclient', version : '>= 1.1', required : get_option('silc'))
    +silc = dependency('silcclient', version : '>= 1.1.1', required : get_option('silc'))
    #######################################################################
    # Check for Gadu-Gadu protocol library (libgadu)
    --- a/pidgin/gtkblist.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtkblist.c Mon Nov 04 14:43:49 2019 +0300
    @@ -5686,8 +5686,9 @@
    enteredstring = g_utf8_casefold(tmp, -1);
    g_free(tmp);
    - if (purple_str_has_prefix(compare, enteredstring))
    + if (g_str_has_prefix(compare, enteredstring)) {
    res = FALSE;
    + }
    g_free(enteredstring);
    }
    --- a/pidgin/gtkconv.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtkconv.c Mon Nov 04 14:43:49 2019 +0300
    @@ -479,7 +479,7 @@
    cmd = talkatu_buffer_get_plain_text(TALKATU_BUFFER(buffer));
    - if (cmd && purple_str_has_prefix(cmd, prefix)) {
    + if (cmd && g_str_has_prefix(cmd, prefix)) {
    PurpleCmdStatus status;
    char *error, *cmdline, *markup, *send_history;
    @@ -2258,7 +2258,7 @@
    win = pidgin_buddy_icon_chooser_new(GTK_WINDOW(toplevel),
    custom_icon_sel_cb, contact);
    g_object_set_data_full(G_OBJECT(contact), "buddy-icon-chooser", win,
    - (GDestroyNotify)g_object_unref);
    + g_object_unref);
    }
    gtk_native_dialog_show(GTK_NATIVE_DIALOG(win));
    }
    @@ -2744,7 +2744,7 @@
    buddy = purple_buddy_new(account, purple_conversation_get_name(conv), NULL);
    purple_blist_node_set_transient((PurpleBlistNode *)buddy, TRUE);
    g_object_set_data_full(G_OBJECT(gtkconv->history), "transient_buddy",
    - buddy, (GDestroyNotify)g_object_unref);
    + buddy, g_object_unref);
    }
    }
    }
    --- a/pidgin/gtkdialogs.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtkdialogs.c Mon Nov 04 14:43:49 2019 +0300
    @@ -417,8 +417,9 @@
    username = g_strdup(purple_normalize(account,
    purple_request_fields_get_string(fields, "screenname")));
    - if (username != NULL && purple_str_has_suffix(username, "rocksmyworld"))
    + if (username != NULL && g_str_has_suffix(username, "rocksmyworld")) {
    found = pidgin_dialogs_ee(username);
    + }
    if (!found && username != NULL && *username != '\0' && account != NULL)
    pidgin_retrieve_user_info(purple_account_get_connection(account), username);
    --- a/pidgin/gtknotify.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtknotify.c Mon Nov 04 14:43:49 2019 +0300
    @@ -360,7 +360,7 @@
    }
    static void
    -reset_mail_dialog(GtkDialog *unused)
    +reset_mail_dialog(gpointer unused)
    {
    g_return_if_fail(mail_dialog != NULL);
    @@ -841,7 +841,7 @@
    pidgin_blist_set_headline(label_text, "mail-unread",
    G_CALLBACK(pidgin_notify_emails_present),
    mail_dialog->dialog,
    - (GDestroyNotify)reset_mail_dialog);
    + reset_mail_dialog);
    mail_dialog->in_use = FALSE;
    g_free(label_text);
    } else if (!gtk_widget_has_focus(mail_dialog->dialog))
    @@ -1555,8 +1555,6 @@
    g_object_unref(icon);
    gtk_widget_show_all(pounce_dialog->dialog);
    -
    - return;
    }
    static PidginNotifyDialog *
    --- a/pidgin/gtkprefs.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtkprefs.c Mon Nov 04 14:43:49 2019 +0300
    @@ -1259,8 +1259,9 @@
    gchar *destdir_escaped = g_shell_quote(destdir);
    gchar *command;
    - if (!g_file_test(destdir, G_FILE_TEST_IS_DIR))
    - purple_build_dir(destdir, S_IRUSR | S_IWUSR | S_IXUSR);
    + if (!g_file_test(destdir, G_FILE_TEST_IS_DIR)) {
    + g_mkdir_with_parents(destdir, S_IRUSR | S_IWUSR | S_IXUSR);
    + }
    command = g_strdup_printf("tar > /dev/null xzf %s -C %s", path_escaped, destdir_escaped);
    g_free(path_escaped);
    @@ -1298,8 +1299,9 @@
    purple_theme_get_name(theme),
    "purple", info->type, NULL);
    - if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
    - purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
    + if (!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) {
    + g_mkdir_with_parents(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
    + }
    g_free(theme_dest);
    theme_dest = g_build_filename(purple_data_dir(), "themes",
    @@ -1344,8 +1346,9 @@
    g_free(source_name);
    }
    - if (!g_file_test(temp_path, G_FILE_TEST_IS_DIR))
    - purple_build_dir(temp_path, S_IRUSR | S_IWUSR | S_IXUSR);
    + if (!g_file_test(temp_path, G_FILE_TEST_IS_DIR)) {
    + g_mkdir_with_parents(temp_path, S_IRUSR | S_IWUSR | S_IXUSR);
    + }
    if (purple_theme_file_copy(path, temp_file)) {
    /* find the theme, could be in subfolder */
    @@ -1357,8 +1360,9 @@
    purple_theme_get_name(theme), "purple",
    info->type, NULL);
    - if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR))
    - purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
    + if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) {
    + g_mkdir_with_parents(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR);
    + }
    if (g_rename(purple_theme_get_dir(theme), theme_dest)) {
    purple_debug_error("gtkprefs", "Error renaming %s to %s: "
    --- a/pidgin/gtkrequest.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtkrequest.c Mon Nov 04 14:43:49 2019 +0300
    @@ -1073,8 +1073,7 @@
    if ((type_hint = purple_request_field_get_field_type_hint(field)) != NULL)
    {
    - if (purple_str_has_prefix(type_hint, "screenname"))
    - {
    + if (g_str_has_prefix(type_hint, "screenname")) {
    GtkWidget *optmenu = NULL;
    PurpleRequestFieldGroup *group = purple_request_field_get_group(field);
    GList *fields = purple_request_field_group_get_fields(group);
    --- a/pidgin/gtkutils.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtkutils.c Mon Nov 04 14:43:49 2019 +0300
    @@ -699,7 +699,7 @@
    if (*s != '\0') *s++ = '\0';
    /* Clear past any whitespace */
    - while (*s != '\0' && *s == ' ')
    + while (*s == ' ')
    s++;
    /* Now let's grab until the end of the line. */
    @@ -1374,7 +1374,7 @@
    #ifndef _WIN32
    /* Are we trying to send a .desktop file? */
    - else if (purple_str_has_suffix(basename, ".desktop")) {
    + else if (g_str_has_suffix(basename, ".desktop")) {
    pidgin_dnd_file_send_desktop(account, who, filename);
    continue;
    @@ -1579,8 +1579,7 @@
    val1.g_type = 0;
    gtk_tree_model_get_value(model, iter, COMPLETION_NORMALIZED_COLUMN, &val1);
    tmp = g_value_get_string(&val1);
    - if (tmp != NULL && purple_str_has_prefix(tmp, key))
    - {
    + if (tmp != NULL && g_str_has_prefix(tmp, key)) {
    g_value_unset(&val1);
    return TRUE;
    }
    @@ -1589,8 +1588,7 @@
    val2.g_type = 0;
    gtk_tree_model_get_value(model, iter, COMPLETION_COMPARISON_COLUMN, &val2);
    tmp = g_value_get_string(&val2);
    - if (tmp != NULL && purple_str_has_prefix(tmp, key))
    - {
    + if (tmp != NULL && g_str_has_prefix(tmp, key)) {
    g_value_unset(&val2);
    return TRUE;
    }
    @@ -2351,15 +2349,13 @@
    normalized = g_utf8_casefold(tmp, -1);
    g_free(tmp);
    - if (purple_str_has_prefix(normalized, enteredstring))
    - {
    + if (g_str_has_prefix(normalized, enteredstring)) {
    g_free(withmarkup);
    g_free(enteredstring);
    g_free(normalized);
    return FALSE;
    }
    -
    /* Use Pango to separate by words. */
    len = g_utf8_strlen(normalized, -1);
    log_attrs = g_new(PangoLogAttr, len + 1);
    @@ -2371,8 +2367,7 @@
    for (i = 0; i < (len - 1) ; i++)
    {
    if (log_attrs[i].is_word_start &&
    - purple_str_has_prefix(word, enteredstring))
    - {
    + g_str_has_prefix(word, enteredstring)) {
    result = FALSE;
    break;
    }
    @@ -2390,7 +2385,7 @@
    if (!g_unichar_isalnum(c))
    {
    word = g_utf8_find_next_char(word, NULL);
    - if (purple_str_has_prefix(word, enteredstring))
    + if (g_str_has_prefix(word, enteredstring))
    {
    result = FALSE;
    break;
    --- a/pidgin/gtkxfer.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/gtkxfer.c Mon Nov 04 14:43:49 2019 +0300
    @@ -482,10 +482,11 @@
    {
    char *escaped = g_shell_quote(filename);
    - if (purple_str_has_suffix(filename, ".desktop"))
    + if (g_str_has_suffix(filename, ".desktop")) {
    command = g_strdup_printf("kfmclient openURL %s 'text/plain'", escaped);
    - else
    + } else {
    command = g_strdup_printf("kfmclient openURL %s", escaped);
    + }
    g_free(escaped);
    }
    else
    --- a/pidgin/libpidgin.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/libpidgin.c Mon Nov 04 14:43:49 2019 +0300
    @@ -152,7 +152,7 @@
    }
    written = write(signal_sockets[0], &sig, sizeof(int));
    - if (written < 0 || written != sizeof(int)) {
    + if (written != sizeof(int)) {
    /* This should never happen */
    purple_debug_error("sighandler", "Received signal %d but only "
    "wrote %" G_GSSIZE_FORMAT " bytes out of %"
    --- a/pidgin/pidgincontactcompletion.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/pidgincontactcompletion.c Mon Nov 04 14:43:49 2019 +0300
    @@ -113,7 +113,7 @@
    return FALSE;
    }
    - if(!purple_str_has_prefix(name, key)) {
    + if (!g_str_has_prefix(name, key)) {
    g_free(name);
    return FALSE;
    }
    --- a/pidgin/pidgintooltip.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/pidgintooltip.c Mon Nov 04 14:43:49 2019 +0300
    @@ -112,6 +112,8 @@
    GtkWidget *tipwindow;
    tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
    + gtk_window_set_transient_for(GTK_WINDOW(tipwindow),
    + GTK_WINDOW(pidgin_tooltip.widget));
    name = gtk_window_get_title(GTK_WINDOW(pidgin_tooltip.widget));
    gtk_window_set_type_hint(GTK_WINDOW(tipwindow), GDK_WINDOW_TYPE_HINT_TOOLTIP);
    gtk_widget_set_app_paintable(tipwindow, TRUE);
    --- a/pidgin/plugins/spellchk.c Mon Nov 04 10:00:14 2019 +0300
    +++ b/pidgin/plugins/spellchk.c Mon Nov 04 14:43:49 2019 +0300
    @@ -677,7 +677,6 @@
    g_signal_connect(G_OBJECT(gtkconv->entry), "message_send",
    G_CALLBACK(message_send_cb), spell);
    - return;
    }
    static int buf_get_line(char *ibuf, char **buf, gsize *position, gsize len)