pidgin/pidgin

Merge default in as this was an old branch and will fail ci without merging
  • +13 -21
    ChangeLog.API
  • +2 -2
    bitbucket-pipelines.yml
  • +58 -0
    convey.yaml
  • +0 -20
    doc/reference/finch/finch-docs.xml
  • +5 -2
    doc/reference/libpurple/libpurple-docs.xml
  • +21 -24
    doc/reference/pidgin/pidgin-docs.xml
  • +1 -1
    finch/finch.c
  • +11 -7
    finch/finch.h
  • +10 -5
    finch/gntaccount.c
  • +7 -4
    finch/gntaccount.h
  • +190 -185
    finch/gntblist.c
  • +10 -12
    finch/gntblist.h
  • +3 -1
    finch/gntconn.c
  • +7 -4
    finch/gntconn.h
  • +12 -5
    finch/gntconv.c
  • +44 -4
    finch/gntconv.h
  • +2 -1
    finch/gntdebug.c
  • +7 -4
    finch/gntdebug.h
  • +0 -1
    finch/gntidle.c
  • +7 -4
    finch/gntidle.h
  • +19 -15
    finch/gntlog.c
  • +8 -4
    finch/gntlog.h
  • +2 -1
    finch/gntmedia.c
  • +6 -4
    finch/gntmedia.h
  • +2 -1
    finch/gntmenuutil.c
  • +7 -4
    finch/gntmenuutil.h
  • +18 -20
    finch/gntnotify.c
  • +6 -4
    finch/gntnotify.h
  • +46 -30
    finch/gntplugin.c
  • +7 -4
    finch/gntplugin.h
  • +4 -3
    finch/gntpounce.c
  • +7 -4
    finch/gntpounce.h
  • +4 -3
    finch/gntprefs.c
  • +7 -4
    finch/gntprefs.h
  • +7 -5
    finch/gntrequest.c
  • +10 -5
    finch/gntrequest.h
  • +6 -4
    finch/gntroomlist.c
  • +6 -4
    finch/gntroomlist.h
  • +3 -1
    finch/gntsound.c
  • +7 -4
    finch/gntsound.h
  • +3 -1
    finch/gntstatus.c
  • +7 -4
    finch/gntstatus.h
  • +4 -3
    finch/gntui.c
  • +5 -3
    finch/gntui.h
  • +18 -12
    finch/gntxfer.c
  • +7 -4
    finch/gntxfer.h
  • +3 -2
    finch/libfinch.c
  • +2 -2
    finch/meson.build
  • +1 -4
    finch/plugins/gntclipboard.c
  • +0 -3
    finch/plugins/gntgf.c
  • +1 -4
    finch/plugins/gnthistory.c
  • +5 -19
    finch/plugins/gnttinyurl.c
  • +13 -9
    finch/plugins/grouping.c
  • +0 -3
    finch/plugins/lastlog.c
  • +1 -1
    finch/plugins/meson.build
  • +32 -54
    libpurple/account.c
  • +4 -61
    libpurple/account.h
  • +4 -10
    libpurple/accountopt.c
  • +3 -3
    libpurple/accountopt.h
  • +2 -4
    libpurple/accounts.c
  • +4 -4
    libpurple/accounts.h
  • +0 -8
    libpurple/action.c
  • +5 -13
    libpurple/action.h
  • +23 -28
    libpurple/attention.h
  • +53 -57
    libpurple/blistnode.c
  • +8 -7
    libpurple/blistnode.h
  • +81 -65
    libpurple/buddy.c
  • +4 -4
    libpurple/buddy.h
  • +17 -22
    libpurple/buddyicon.c
  • +9 -19
    libpurple/buddyicon.h
  • +352 -256
    libpurple/buddylist.c
  • +121 -115
    libpurple/buddylist.h
  • +26 -23
    libpurple/chat.c
  • +3 -2
    libpurple/chat.h
  • +4 -16
    libpurple/circularbuffer.h
  • +38 -10
    libpurple/cmds.h
  • +79 -49
    libpurple/connection.c
  • +3 -3
    libpurple/connection.h
  • +18 -25
    libpurple/contact.c
  • +2 -2
    libpurple/contact.h
  • +83 -98
    libpurple/conversation.c
  • +7 -8
    libpurple/conversation.h
  • +7 -7
    libpurple/conversations.h
  • +124 -127
    libpurple/conversationtypes.c
  • +18 -25
    libpurple/conversationtypes.h
  • +6 -2
    libpurple/core.c
  • +5 -5
    libpurple/core.h
  • +27 -27
    libpurple/countingnode.c
  • +6 -3
    libpurple/debug.c
  • +4 -4
    libpurple/debug.h
  • +3 -3
    libpurple/e2ee.h
  • +3 -3
    libpurple/eventloop.h
  • +15 -35
    libpurple/example/nullclient.c
  • +3 -3
    libpurple/glibcompat.h
  • +14 -13
    libpurple/group.c
  • +3 -3
    libpurple/http.h
  • +3 -3
    libpurple/idle.h
  • +3 -3
    libpurple/image-store.h
  • +1 -0
    libpurple/image.h
  • +3 -3
    libpurple/internal.h
  • +42 -47
    libpurple/keyring.c
  • +3 -3
    libpurple/keyring.h
  • +4 -4
    libpurple/log.c
  • +13 -12
    libpurple/log.h
  • +3 -3
    libpurple/media-gst.h
  • +1 -1
    libpurple/media.c
  • +16 -9
    libpurple/media.h
  • +4 -84
    libpurple/media/backend-fs2.c
  • +3 -3
    libpurple/media/backend-fs2.h
  • +4 -4
    libpurple/media/backend-iface.c
  • +7 -7
    libpurple/media/backend-iface.h
  • +6 -6
    libpurple/media/candidate.h
  • +6 -6
    libpurple/media/codec.h
  • +3 -3
    libpurple/media/enum-types.h
  • +2 -3
    libpurple/mediamanager.c
  • +3 -3
    libpurple/mediamanager.h
  • +7 -8
    libpurple/memorypool.c
  • +2 -2
    libpurple/meson.build
  • +21 -14
    libpurple/message.c
  • +4 -4
    libpurple/message.h
  • +3 -4
    libpurple/nat-pmp.h
  • +4 -4
    libpurple/network.h
  • +1 -3
    libpurple/notify.c
  • +3 -3
    libpurple/notify.h
  • +1 -2
    libpurple/pluginpref.c
  • +3 -3
    libpurple/pluginpref.h
  • +38 -187
    libpurple/plugins.c
  • +10 -313
    libpurple/plugins.h
  • +5 -11
    libpurple/plugins/autoaccept.c
  • +1 -6
    libpurple/plugins/buddynote.c
  • +2 -4
    libpurple/plugins/codeinline.c
  • +1 -8
    libpurple/plugins/filectl.c
  • +2 -10
    libpurple/plugins/idle.c
  • +1 -4
    libpurple/plugins/joinpart.c
  • +10 -16
    libpurple/plugins/keyrings/internalkeyring.c
  • +0 -576
    libpurple/plugins/keyrings/kwallet.cpp
  • +13 -0
    libpurple/plugins/keyrings/kwallet/meson.build
  • +496 -0
    libpurple/plugins/keyrings/kwallet/purplekwallet.cpp
  • +100 -0
    libpurple/plugins/keyrings/kwallet/purplekwallet.h
  • +4 -11
    libpurple/plugins/keyrings/meson.build
  • +4 -8
    libpurple/plugins/keyrings/secretservice.c
  • +1 -4
    libpurple/plugins/keyrings/wincred.c
  • +21 -18
    libpurple/plugins/log_reader.c
  • +6 -0
    libpurple/plugins/meson.build
  • +1 -9
    libpurple/plugins/offlinemsg.c
  • +1 -5
    libpurple/plugins/one_time_password.c
  • +17 -14
    libpurple/plugins/psychic.c
  • +240 -0
    libpurple/plugins/purple-toast.c
  • +1 -10
    libpurple/plugins/signals-test.c
  • +18 -3
    libpurple/plugins/simple.c
  • +18 -10
    libpurple/plugins/statenotify.c
  • +1 -3
    libpurple/plugins/test-request-input.c
  • +4 -4
    libpurple/pounce.h
  • +4 -9
    libpurple/prefs.c
  • +9 -8
    libpurple/prefs.h
  • +38 -26
    libpurple/presence.c
  • +7 -7
    libpurple/presence.h
  • +42 -128
    libpurple/protocol.c
  • +60 -194
    libpurple/protocol.h
  • +10 -10
    libpurple/protocols.c
  • +8 -5
    libpurple/protocols.h
  • +31 -30
    libpurple/protocols/bonjour/bonjour.c
  • +4 -4
    libpurple/protocols/bonjour/bonjour.h
  • +119 -85
    libpurple/protocols/bonjour/bonjour_ft.c
  • +17 -32
    libpurple/protocols/bonjour/bonjour_ft.h
  • +18 -21
    libpurple/protocols/bonjour/buddy.c
  • +5 -4
    libpurple/protocols/bonjour/buddy.h
  • +2 -1
    libpurple/protocols/bonjour/dns_sd_proxy.c
  • +3 -6
    libpurple/protocols/bonjour/dns_sd_proxy.h
  • +364 -341
    libpurple/protocols/bonjour/jabber.c
  • +10 -14
    libpurple/protocols/bonjour/jabber.h
  • +3 -5
    libpurple/protocols/bonjour/mdns_avahi.c
  • +23 -21
    libpurple/protocols/bonjour/mdns_common.c
  • +3 -3
    libpurple/protocols/bonjour/mdns_common.h
  • +1 -2
    libpurple/protocols/bonjour/mdns_dns_sd.c
  • +3 -3
    libpurple/protocols/bonjour/mdns_interface.h
  • +5 -4
    libpurple/protocols/bonjour/mdns_types.h
  • +1 -4
    libpurple/protocols/bonjour/parser.c
  • +4 -3
    libpurple/protocols/bonjour/parser.h
  • +37 -19
    libpurple/protocols/facebook/api.c
  • +3 -3
    libpurple/protocols/facebook/api.h
  • +3 -3
    libpurple/protocols/facebook/data.h
  • +34 -26
    libpurple/protocols/facebook/facebook.c
  • +3 -3
    libpurple/protocols/facebook/facebook.h
  • +3 -3
    libpurple/protocols/facebook/http.h
  • +3 -3
    libpurple/protocols/facebook/id.h
  • +3 -3
    libpurple/protocols/facebook/json.h
  • +22 -9
    libpurple/protocols/facebook/mqtt.c
  • +3 -3
    libpurple/protocols/facebook/mqtt.h
  • +3 -3
    libpurple/protocols/facebook/thrift.h
  • +3 -3
    libpurple/protocols/facebook/util.h
  • +3 -3
    libpurple/protocols/gg/account.h
  • +3 -3
    libpurple/protocols/gg/avatar.h
  • +3 -5
    libpurple/protocols/gg/blist.h
  • +4 -3
    libpurple/protocols/gg/chat.h
  • +114 -87
    libpurple/protocols/gg/edisc.c
  • +13 -3
    libpurple/protocols/gg/edisc.h
  • +43 -34
    libpurple/protocols/gg/gg.c
  • +3 -4
    libpurple/protocols/gg/gg.h
  • +4 -4
    libpurple/protocols/gg/html.h
  • +3 -3
    libpurple/protocols/gg/image-prpl.h
  • +4 -3
    libpurple/protocols/gg/keymapper.h
  • +3 -3
    libpurple/protocols/gg/libgadu-events.h
  • +3 -3
    libpurple/protocols/gg/libgaduw.h
  • +4 -3
    libpurple/protocols/gg/message-prpl.h
  • +3 -3
    libpurple/protocols/gg/multilogon.h
  • +7 -7
    libpurple/protocols/gg/oauth/oauth-parameter.c
  • +0 -2
    libpurple/protocols/gg/oauth/oauth-purple.c
  • +2 -2
    libpurple/protocols/gg/oauth/oauth.c
  • +3 -3
    libpurple/protocols/gg/pubdir-prpl.h
  • +2 -1
    libpurple/protocols/gg/purplew.c
  • +3 -3
    libpurple/protocols/gg/purplew.h
  • +2 -2
    libpurple/protocols/gg/resolver-purple.c
  • +3 -3
    libpurple/protocols/gg/resolver-purple.h
  • +3 -3
    libpurple/protocols/gg/roster.h
  • +3 -3
    libpurple/protocols/gg/servconn.h
  • +3 -3
    libpurple/protocols/gg/status.h
  • +4 -3
    libpurple/protocols/gg/tcpsocket.h
  • +3 -3
    libpurple/protocols/gg/utils.h
  • +3 -3
    libpurple/protocols/gg/validator.h
  • +3 -3
    libpurple/protocols/gg/xml.h
  • +1 -7
    libpurple/protocols/irc/cmds.c
  • +152 -122
    libpurple/protocols/irc/dcc_send.c
  • +49 -61
    libpurple/protocols/irc/irc.c
  • +10 -9
    libpurple/protocols/irc/irc.h
  • +1 -7
    libpurple/protocols/irc/msgs.c
  • +1 -8
    libpurple/protocols/irc/parse.c
  • +3 -3
    libpurple/protocols/jabber/adhoccommands.h
  • +4 -3
    libpurple/protocols/jabber/auth.h
  • +1 -1
    libpurple/protocols/jabber/auth_digest_md5.c
  • +4 -3
    libpurple/protocols/jabber/auth_digest_md5.h
  • +4 -3
    libpurple/protocols/jabber/auth_scram.h
  • +4 -3
    libpurple/protocols/jabber/bosh.h
  • +4 -3
    libpurple/protocols/jabber/buddy.h
  • +4 -5
    libpurple/protocols/jabber/caps.c
  • +3 -3
    libpurple/protocols/jabber/caps.h
  • +4 -3
    libpurple/protocols/jabber/chat.h
  • +4 -3
    libpurple/protocols/jabber/disco.h
  • +24 -9
    libpurple/protocols/jabber/gtalk.c
  • +6 -4
    libpurple/protocols/jabber/gtalk.h
  • +0 -1
    libpurple/protocols/jabber/ibb.c
  • +3 -3
    libpurple/protocols/jabber/ibb.h
  • +4 -3
    libpurple/protocols/jabber/iq.h
  • +61 -50
    libpurple/protocols/jabber/jabber.c
  • +4 -3
    libpurple/protocols/jabber/jabber.h
  • +6 -11
    libpurple/protocols/jabber/jingle/rtp.c
  • +3 -2
    libpurple/protocols/jabber/jutil.c
  • +4 -3
    libpurple/protocols/jabber/jutil.h
  • +3 -4
    libpurple/protocols/jabber/meson.build
  • +4 -3
    libpurple/protocols/jabber/message.h
  • +3 -3
    libpurple/protocols/jabber/namespaces.h
  • +65 -44
    libpurple/protocols/jabber/oob.c
  • +13 -3
    libpurple/protocols/jabber/oob.h
  • +4 -3
    libpurple/protocols/jabber/parser.h
  • +3 -3
    libpurple/protocols/jabber/pep.h
  • +4 -3
    libpurple/protocols/jabber/ping.h
  • +6 -11
    libpurple/protocols/jabber/presence.c
  • +4 -3
    libpurple/protocols/jabber/presence.h
  • +0 -2
    libpurple/protocols/jabber/roster.c
  • +4 -3
    libpurple/protocols/jabber/roster.h
  • +195 -154
    libpurple/protocols/jabber/si.c
  • +14 -3
    libpurple/protocols/jabber/si.h
  • +3 -3
    libpurple/protocols/jabber/useravatar.h
  • +3 -3
    libpurple/protocols/jabber/usermood.h
  • +3 -3
    libpurple/protocols/jabber/usernick.h
  • +3 -3
    libpurple/protocols/jabber/usertune.h
  • +1 -2
    libpurple/protocols/jabber/xdata.c
  • +4 -3
    libpurple/protocols/jabber/xdata.h
  • +17 -3
    libpurple/protocols/jabber/xmpp.c
  • +6 -4
    libpurple/protocols/jabber/xmpp.h
  • +3 -3
    libpurple/protocols/novell/nmconference.h
  • +1 -2
    libpurple/protocols/novell/nmconn.c
  • +3 -3
    libpurple/protocols/novell/nmconn.h
  • +3 -3
    libpurple/protocols/novell/nmcontact.h
  • +3 -3
    libpurple/protocols/novell/nmevent.h
  • +4 -5
    libpurple/protocols/novell/nmfield.h
  • +3 -3
    libpurple/protocols/novell/nmmessage.h
  • +3 -3
    libpurple/protocols/novell/nmrequest.h
  • +3 -3
    libpurple/protocols/novell/nmrtf.h
  • +8 -15
    libpurple/protocols/novell/nmuser.c
  • +3 -3
    libpurple/protocols/novell/nmuser.h
  • +1 -7
    libpurple/protocols/novell/nmuserrecord.c
  • +3 -3
    libpurple/protocols/novell/nmuserrecord.h
  • +54 -49
    libpurple/protocols/novell/novell.c
  • +4 -3
    libpurple/protocols/novell/novell.h
  • +1 -1
    libpurple/protocols/null/meson.build
  • +73 -75
    libpurple/protocols/null/nullprpl.c
  • +7 -5
    libpurple/protocols/null/nullprpl.h
  • +28 -12
    libpurple/protocols/oscar/aim.c
  • +6 -4
    libpurple/protocols/oscar/aim.h
  • +3 -3
    libpurple/protocols/oscar/encoding.h
  • +1 -2
    libpurple/protocols/oscar/family_chatnav.c
  • +2 -1
    libpurple/protocols/oscar/family_feedbag.c
  • +7 -7
    libpurple/protocols/oscar/family_icbm.c
  • +3 -6
    libpurple/protocols/oscar/family_locate.c
  • +1 -2
    libpurple/protocols/oscar/family_userlookup.c
  • +25 -9
    libpurple/protocols/oscar/icq.c
  • +6 -4
    libpurple/protocols/oscar/icq.h
  • +2 -2
    libpurple/protocols/oscar/odc.c
  • +8 -8
    libpurple/protocols/oscar/oft.c
  • +86 -104
    libpurple/protocols/oscar/oscar.c
  • +3 -3
    libpurple/protocols/oscar/oscar.h
  • +5 -0
    libpurple/protocols/oscar/oscarcommon.h
  • +98 -44
    libpurple/protocols/oscar/peer.c
  • +16 -3
    libpurple/protocols/oscar/peer.h
  • +1 -2
    libpurple/protocols/oscar/peer_proxy.c
  • +4 -3
    libpurple/protocols/oscar/snactypes.h
  • +5 -3
    libpurple/protocols/oscar/userinfo.c
  • +3 -3
    libpurple/protocols/oscar/visibility.h
  • +3 -3
    libpurple/protocols/sametime/im_mime.h
  • +1 -1
    libpurple/protocols/sametime/meson.build
  • +267 -237
    libpurple/protocols/sametime/sametime.c
  • +3 -3
    libpurple/protocols/sametime/sametime.h
  • +34 -28
    libpurple/protocols/silc/silc.c
  • +3 -3
    libpurple/protocols/silc/silcpurple.h
  • +3 -3
    libpurple/protocols/silc/wb.h
  • +3 -3
    libpurple/protocols/simple/ntlm.c
  • +5 -4
    libpurple/protocols/simple/ntlm.h
  • +29 -35
    libpurple/protocols/simple/simple.c
  • +4 -9
    libpurple/protocols/simple/simple.h
  • +1 -12
    libpurple/protocols/simple/sipmsg.c
  • +4 -5
    libpurple/protocols/simple/sipmsg.h
  • +1 -1
    libpurple/protocols/zephyr/ZCkIfNot.c
  • +15 -13
    libpurple/protocols/zephyr/ZGetLocs.c
  • +16 -13
    libpurple/protocols/zephyr/ZGetSubs.c
  • +1 -1
    libpurple/protocols/zephyr/ZIfNotice.c
  • +1 -8
    libpurple/protocols/zephyr/ZRetSubs.c
  • +4 -5
    libpurple/protocols/zephyr/ZSubs.c
  • +34 -33
    libpurple/protocols/zephyr/ZVariables.c
  • +1 -1
    libpurple/protocols/zephyr/ZWait4Not.c
  • +5 -7
    libpurple/protocols/zephyr/Zinternal.c
  • +10 -22
    libpurple/protocols/zephyr/com_err.h
  • +3 -5
    libpurple/protocols/zephyr/error_message.c
  • +5 -3
    libpurple/protocols/zephyr/error_table.h
  • +20 -24
    libpurple/protocols/zephyr/internal.h
  • +10 -74
    libpurple/protocols/zephyr/sysdep.h
  • +78 -66
    libpurple/protocols/zephyr/zephyr.c
  • +4 -3
    libpurple/protocols/zephyr/zephyr.h
  • +3 -8
    libpurple/protocols/zephyr/zephyr_err.c
  • +75 -90
    libpurple/protocols/zephyr/zephyr_internal.h
  • +0 -1
    libpurple/proxy.c
  • +3 -3
    libpurple/proxy.h
  • +3 -3
    libpurple/purple-gio.h
  • +3 -11
    libpurple/purple.h.in
  • +3 -3
    libpurple/queuedoutputstream.h
  • +3 -3
    libpurple/request-datasheet.h
  • +8 -22
    libpurple/request.c
  • +14 -11
    libpurple/request.h
  • +106 -40
    libpurple/roomlist.c
  • +108 -30
    libpurple/roomlist.h
  • +3 -4
    libpurple/savedstatuses.h
  • +6 -5
    libpurple/server.c
  • +4 -5
    libpurple/server.h
  • +1 -3
    libpurple/signals.c
  • +3 -4
    libpurple/signals.h
  • +5 -5
    libpurple/smiley-custom.h
  • +2 -1
    libpurple/smiley-list.h
  • +6 -6
    libpurple/smiley-parser.h
  • +5 -5
    libpurple/smiley-theme.h
  • +3 -3
    libpurple/sound.h
  • +0 -1
    libpurple/sslconn.c
  • +3 -3
    libpurple/sslconn.h
  • +18 -12
    libpurple/status.c
  • +4 -5
    libpurple/status.h
  • +3 -3
    libpurple/stringref.h
  • +10 -9
    libpurple/stun.c
  • +7 -7
    libpurple/stun.h
  • +2 -0
    libpurple/theme-manager.c
  • +46 -42
    libpurple/trie.c
  • +12 -12
    libpurple/upnp.h
  • +9 -77
    libpurple/util.c
  • +10 -58
    libpurple/util.h
  • +3 -4
    libpurple/version.h.in
  • +43 -43
    libpurple/whiteboard.c
  • +7 -28
    libpurple/whiteboard.h
  • +3 -3
    libpurple/win32/libc_interface.h
  • +3 -3
    libpurple/win32/libc_internal.h
  • +3 -4
    libpurple/win32/win32dep.h
  • +3 -3
    libpurple/win32/wpurpleerror.h
  • +473 -494
    libpurple/xfer.c
  • +35 -156
    libpurple/xfer.h
  • +3 -2
    libpurple/xmlnode.c
  • +3 -4
    libpurple/xmlnode.h
  • +93 -318
    meson.build
  • +87 -80
    meson_options.txt
  • +0 -0
    pidgin/data/icons/hicolor/16x16/apps/im.pidgin.Pidgin3.png
  • +0 -0
    pidgin/data/icons/hicolor/16x16/apps/pidgin.png
  • +0 -0
    pidgin/data/icons/hicolor/22x22/apps/im.pidgin.Pidgin3.png
  • +0 -0
    pidgin/data/icons/hicolor/22x22/apps/pidgin.png
  • +0 -0
    pidgin/data/icons/hicolor/24x24/apps/im.pidgin.Pidgin3.png
  • +0 -0
    pidgin/data/icons/hicolor/24x24/apps/pidgin.png
  • +0 -0
    pidgin/data/icons/hicolor/32x32/apps/im.pidgin.Pidgin3.png
  • +0 -0
    pidgin/data/icons/hicolor/32x32/apps/pidgin.png
  • +0 -0
    pidgin/data/icons/hicolor/48x48/apps/im.pidgin.Pidgin3.png
  • +0 -0
    pidgin/data/icons/hicolor/48x48/apps/pidgin.png
  • +610 -0
    pidgin/data/icons/hicolor/scalable/apps/im.pidgin.Pidgin3.svg
  • +0 -610
    pidgin/data/icons/hicolor/scalable/apps/pidgin.svg
  • +5 -5
    pidgin/data/icons/meson.build
  • +1 -1
    pidgin/data/im.pidgin.Pidgin3.desktop.in.in
  • +6 -0
    pidgin/glade/meson.build
  • +15 -0
    pidgin/glade/pidgin3.xml.in
  • +4 -8
    pidgin/gtk3compat.h
  • +10 -11
    pidgin/gtkaccount.c
  • +3 -2
    pidgin/gtkblist-theme.c
  • +221 -233
    pidgin/gtkblist.c
  • +5 -17
    pidgin/gtkblist.h
  • +0 -7
    pidgin/gtkcellrendererexpander.c
  • +0 -306
    pidgin/gtkconv-theme-loader.c
  • +0 -53
    pidgin/gtkconv-theme-loader.h
  • +0 -766
    pidgin/gtkconv-theme.c
  • +0 -194
    pidgin/gtkconv-theme.h
  • +253 -2054
    pidgin/gtkconv.c
  • +4 -19
    pidgin/gtkconv.h
  • +31 -33
    pidgin/gtkdialogs.c
  • +7 -8
    pidgin/gtkdocklet.c
  • +3 -7
    pidgin/gtknotify.c
  • +67 -37
    pidgin/gtkplugin.c
  • +14 -10
    pidgin/gtkpounce.c
  • +556 -858
    pidgin/gtkprefs.c
  • +8 -6
    pidgin/gtkprivacy.c
  • +57 -75
    pidgin/gtkrequest.c
  • +15 -7
    pidgin/gtkroomlist.c
  • +2 -4
    pidgin/gtksavedstatuses.c
  • +19 -10
    pidgin/gtkscrollbook.c
  • +3 -2
    pidgin/gtksmiley-manager.c
  • +14 -16
    pidgin/gtksmiley-theme.c
  • +13 -33
    pidgin/gtkstatusbox.c
  • +1 -1
    pidgin/gtkstatusbox.h
  • +38 -714
    pidgin/gtkutils.c
  • +12 -88
    pidgin/gtkutils.h
  • +0 -2369
    pidgin/gtkwebview.c
  • +0 -676
    pidgin/gtkwebview.h
  • +0 -1780
    pidgin/gtkwebviewtoolbar.c
  • +0 -131
    pidgin/gtkwebviewtoolbar.h
  • +377 -495
    pidgin/gtkwhiteboard.c
  • +152 -356
    pidgin/gtkxfer.c
  • +4 -11
    pidgin/gtkxfer.h
  • +6 -8
    pidgin/libpidgin.c
  • +8 -14
    pidgin/meson.build
  • +42 -66
    pidgin/pidginabout.c
  • +1 -1
    pidgin/pidginabout.h
  • +273 -0
    pidgin/pidginaccountchooser.c
  • +98 -0
    pidgin/pidginaccountchooser.h
  • +28 -63
    pidgin/pidgindebug.c
  • +7 -4
    pidgin/pidgindebugplugininfo.c
  • +15 -13
    pidgin/pidginlog.c
  • +191 -0
    pidgin/pidginmessage.c
  • +63 -0
    pidgin/pidginmessage.h
  • +3 -3
    pidgin/pidgintooltip.c
  • +4 -4
    pidgin/plugins/cap/cap.c
  • +5 -5
    pidgin/plugins/cap/meson.build
  • +12 -10
    pidgin/plugins/contact_priority.c
  • +162 -220
    pidgin/plugins/disco/gtkdisco.c
  • +21 -7
    pidgin/plugins/disco/gtkdisco.h
  • +6 -0
    pidgin/plugins/disco/meson.build
  • +248 -0
    pidgin/plugins/disco/resources/disco.ui
  • +6 -0
    pidgin/plugins/disco/resources/xmppdisco.gresource.xml
  • +4 -1
    pidgin/plugins/disco/xmppdisco.c
  • +2 -1
    pidgin/plugins/gestures/meson.build
  • +10 -10
    pidgin/plugins/gestures/stroke-draw.c
  • +18 -19
    pidgin/plugins/gestures/stroke.c
  • +3 -9
    pidgin/plugins/gevolution/add_buddy_dialog.c
  • +3 -9
    pidgin/plugins/gevolution/assoc-buddy.c
  • +1 -1
    pidgin/plugins/gevolution/eds-utils.c
  • +3 -5
    pidgin/plugins/gevolution/gevo-util.c
  • +1 -2
    pidgin/plugins/gevolution/gevolution.c
  • +12 -11
    pidgin/plugins/gevolution/meson.build
  • +6 -6
    pidgin/plugins/gevolution/new_person_dialog.c
  • +1 -2
    pidgin/plugins/history.c
  • +4 -4
    pidgin/plugins/mailchk.c
  • +0 -243
    pidgin/plugins/markerline.c
  • +12 -23
    pidgin/plugins/meson.build
  • +1 -1
    pidgin/plugins/musicmessaging/musicmessaging.c
  • +6 -5
    pidgin/plugins/raw.c
  • +0 -10
    pidgin/plugins/screencap.c
  • +0 -188
    pidgin/plugins/sendbutton.c
  • +2 -2
    pidgin/plugins/spellchk.c
  • +13 -1
    pidgin/plugins/ticker/gtkticker.c
  • +2 -1
    pidgin/plugins/ticker/gtkticker.h
  • +3 -5
    pidgin/plugins/ticker/ticker.c
  • +7 -4
    pidgin/plugins/win32/winprefs/winprefs.c
  • +0 -975
    pidgin/plugins/xmppconsole.c
  • +597 -0
    pidgin/plugins/xmppconsole/console.ui
  • +11 -0
    pidgin/plugins/xmppconsole/meson.build
  • +788 -0
    pidgin/plugins/xmppconsole/xmppconsole.c
  • +6 -0
    pidgin/plugins/xmppconsole/xmppconsole.gresource.xml
  • +1 -1
    pidgin/resources/About/about.ui
  • +33 -0
    pidgin/resources/Accounts/chooser.ui
  • +1 -1
    pidgin/resources/Conversations/invite_dialog.ui
  • +42 -1
    pidgin/resources/Debug/debug.ui
  • +0 -43
    pidgin/resources/Debug/filter-popover.ui
  • +1 -1
    pidgin/resources/Debug/plugininfo.ui
  • +1 -1
    pidgin/resources/Log/log-viewer.ui
  • +2821 -1979
    pidgin/resources/Prefs/prefs.ui
  • +572 -0
    pidgin/resources/Prefs/vv.ui
  • +96 -0
    pidgin/resources/Whiteboard/whiteboard.ui
  • +534 -0
    pidgin/resources/Xfer/xfer.ui
  • +4 -1
    pidgin/resources/pidgin.gresource.xml
  • +0 -16
    pidgin/themes/Contents/Info.plist
  • +0 -6
    pidgin/themes/Contents/Resources/Content.html
  • +0 -8
    pidgin/themes/Contents/Resources/Incoming/Content.html
  • +0 -7
    pidgin/themes/Contents/Resources/Status.html
  • +0 -2
    pidgin/themes/Contents/Resources/Variants/Default.css
  • +0 -8
    pidgin/themes/Contents/Resources/Variants/No-Timestamps.css
  • +0 -95
    pidgin/themes/Contents/Resources/main.css
  • +0 -376
    pidgin/themes/Template.html
  • +0 -6
    pidgin/themes/meson.build
  • +1 -1
    pidgin/win32/gtkwin32dep.c
  • +0 -3
    pidgin/win32/nsis/generate_gtk_zip.sh
  • +0 -1
    pidgin/win32/nsis/pidgin-installer.nsi
  • +0 -3
    pidgin/win32/prepare-workspace.sh
  • +255 -25
    po/POTFILES.in
  • +1 -1
    po/meson.build
  • +1 -1
    subprojects/gplugin.wrap
  • +0 -34
    valgrind-suppressions
  • --- a/ChangeLog.API Tue Oct 08 21:47:58 2019 -0500
    +++ b/ChangeLog.API Tue Oct 08 21:48:28 2019 -0500
    @@ -23,6 +23,7 @@
    * purple_account_presence_new
    * purple_buddy_presence_new
    * purple_account_register_completed
    + * purple_blist_get_default_root
    * purple_blist_node_is_transient
    * purple_blist_node_set_transient
    * purple_certificate_get_der_data
    @@ -67,36 +68,16 @@
    * PurplePluginInfoFlags (PURPLE_PLUGIN_INFO_FLAGS_INTERNAL and
    PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD)
    * purple_plugin_get_dependent_plugins
    - * purple_plugin_get_filename
    * purple_plugin_is_internal
    * purple_plugin_info_new
    * purple_plugin_get_info
    - * purple_plugin_info_get_abi_version
    * purple_plugin_info_get_actions_cb
    - * purple_plugin_info_get_category
    - * purple_plugin_info_get_dependencies
    * purple_plugin_info_get_error
    * purple_plugin_info_get_extra_cb
    - * purple_plugin_info_get_flags
    - * purple_plugin_info_get_icon
    - * purple_plugin_info_get_license_id
    - * purple_plugin_info_get_license_text
    - * purple_plugin_info_get_license_url
    * purple_plugin_info_get_pref_frame_cb
    * purple_plugin_info_get_pref_request_cb
    * purple_plugin_info_get_ui_data
    * purple_plugin_info_set_ui_data
    - * purple_plugin_register_type
    - * purple_plugin_add_interface
    - * PURPLE_DEFINE_TYPE
    - * PURPLE_DEFINE_TYPE_EXTENDED
    - * PURPLE_IMPLEMENT_INTERFACE_STATIC
    - * PURPLE_IMPLEMENT_INTERFACE
    - * PURPLE_DEFINE_DYNAMIC_TYPE
    - * PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED
    - * PURPLE_IMPLEMENT_INTERFACE_DYNAMIC
    - * PURPLE_DEFINE_STATIC_TYPE
    - * PURPLE_DEFINE_STATIC_TYPE_EXTENDED
    * PurpleProtocol, inherits GObject. Please see the documentation for
    details.
    * PurpleProtocolAction
    @@ -205,6 +186,9 @@
    * purple_blist_alias_buddy renamed to purple_buddy_set_local_alias
    * purple_blist_alias_chat renamed to purple_chat_set_alias
    * purple_blist_alias_contact renamed to purple_contact_set_alias
    + * purple_blist_get_root now takes a PurpleBuddyList parameter;
    + use purple_blist_get_default_root to work on the default buddy
    + list as before
    * purple_blist_merge_contact renamed to purple_contact_merge
    * purple_blist_rename_buddy renamed to purple_buddy_set_name
    * purple_blist_rename_group renamed to purple_group_set_name
    @@ -220,7 +204,7 @@
    * purple_find_buddy_in_group renamed to purple_blist_find_buddy_in_group
    * purple_find_buddy renamed to purple_blist_find_buddy
    * purple_find_group renamed to purple_blist_find_group
    - * purple_get_blist renamed to purple_blist_get_buddy_list
    + * purple_get_blist renamed to purple_blist_get_default
    * PurpleBuddyIconSpec has been moved to buddyicon.h
    * purple_certificate_check_signature_chain now returns a list of failing
    PurpleCertificate*s as the second parameter
    @@ -392,12 +376,17 @@
    * _XMLNodeType
    * account-authorization-requested-with-message signal
    * network-configuration-changed signal
    + * PurpleAccoun->ui_data
    * purple_account_add_buddies_with_invite
    * purple_account_add_buddy_with_invite
    + * purple_account_get_ui_data
    * purple_account_set_current_error
    + * purple_account_set_ui_data
    * purple_base64_*. Use g_base64_* instead
    + * purple_blist_get_ui_data
    * purple_blist_load
    * purple_blist_new
    + * purple_blist_set_ui_data
    * purple_set_blist
    * purple_blist_update_buddy_icon
    * purple_buddy_get_local_alias
    @@ -504,6 +493,7 @@
    * purple_status_type_set_primary_attr
    * purple_strlcat
    * purple_strlcpy
    + * purple_str_size_to_units. Use g_format_size() instead.
    * purple_timeout_*. Use g_timeout_* or g_idle_* instead.
    * purple_txt_cancel
    * purple_txt_resolve_account
    @@ -571,6 +561,8 @@
    * FULL_CIRCLE_DEGREES renamed to PIDGIN_FULL_CIRCLE_DEGREES
    * NUM_NICK_SEED_COLORS renamed to PIDGIN_NUM_NICK_SEED_COLORS
    * PALETTE_NUM_COLORS renamed to PIDGIN_PALETTE_NUM_COLORS
    + * pidgin_account_option_menu_* renamed to
    + pidgin_account_chooser_*
    * pidgin_new_item_from_stock renamed to pidgin_new_menu_item and
    removed the accel related parameters.
    * pidgin_setup_screenname_autocomplete now takes a filter function and
    --- a/bitbucket-pipelines.yml Tue Oct 08 21:47:58 2019 -0500
    +++ b/bitbucket-pipelines.yml Tue Oct 08 21:48:28 2019 -0500
    @@ -1,11 +1,11 @@
    # vi:et:ts=2 sw=2 sts=2
    -image: pidgin/builder-debian:buster
    +image: pidgin/builders:subproject-build
    pipelines:
    default:
    - step:
    script:
    - set -ex
    - - meson -Dsilc=false -Ddoc=true -Dconsole-logging=true build-pipelines
    + - meson -Ddoc=true -Dconsole-logging=true build-pipelines
    - ninja -C build-pipelines
    - ninja -C build-pipelines test
    - "ninja -C build-pipelines $(ninja -C build-pipelines -t targets | grep -E '[a-z]+-doc:' | cut -d: -f1)"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/convey.yaml Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,58 @@
    +environment:
    + - PACKAGECLOUD_REPO=experimental
    + - ARCH=amd64
    + - BUILD_NUMBER
    + - REGISTRY_HOST=docker.io
    + - REPOSITORY=pidgin/builders
    +
    +tasks:
    + import:
    + type: docker/import
    + files: .:.
    +
    + build:
    + type: docker/run
    + image: ${REGISTRY_HOST}/${REPOSITORY}:${DISTRO}-${VERSION}-${ARCH}
    +
    + clean:
    + type: convey/clean
    + files:
    + - pvs-studio
    +
    + export:
    + type: docker/export
    + files: ${DISTRO}-${VERSION}-${ARCH}
    +
    + build-target:
    + type: docker/run
    + image: ${REGISTRY_HOST}/${REPOSITORY}:${TARGET}
    +
    + export-target:
    + type: docker/export
    + files: ${TARGET}
    +
    +plans:
    + clean:
    + stages:
    + - tasks: [clean]
    +
    + mingw-w64-x86_64:
    + environment: [DISTRO=mingw, VERSION=w64, ARCH=x86_64]
    + stages:
    + - tasks: [import, build]
    +
    + pvs-studio:
    + environment:
    + - TARGET=pvs-studio
    + - PVS_STUDIO_USERNAME
    + - PVS_STUDIO_KEY
    + stages:
    + - tasks: [import, build-target]
    + - tasks: [export-target]
    + run: always
    +
    + subproject-build:
    + environment: [TARGET=subproject-build]
    + stages:
    + - tasks: [import, build-target]
    +
    --- a/doc/reference/finch/finch-docs.xml Tue Oct 08 21:47:58 2019 -0500
    +++ b/doc/reference/finch/finch-docs.xml Tue Oct 08 21:48:28 2019 -0500
    @@ -53,26 +53,6 @@
    <title>Index of deprecated symbols</title>
    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
    </index>
    - <index id="api-index-2.8.0" role="2.8.0">
    - <title>Index of new symbols in 2.8.0</title>
    - <xi:include href="xml/api-index-2.8.0.xml"><xi:fallback /></xi:include>
    - </index>
    - <index id="api-index-2.4.0" role="2.4.0">
    - <title>Index of new symbols in 2.4.0</title>
    - <xi:include href="xml/api-index-2.4.0.xml"><xi:fallback /></xi:include>
    - </index>
    - <index id="api-index-2.3.0" role="2.3.0">
    - <title>Index of new symbols in 2.3.0</title>
    - <xi:include href="xml/api-index-2.3.0.xml"><xi:fallback /></xi:include>
    - </index>
    - <index id="api-index-2.2.0" role="2.2.0">
    - <title>Index of new symbols in 2.2.0</title>
    - <xi:include href="xml/api-index-2.2.0.xml"><xi:fallback /></xi:include>
    - </index>
    - <index id="api-index-2.0.0" role="2.0.0">
    - <title>Index of new symbols in 2.0.0</title>
    - <xi:include href="xml/api-index-2.0.0.xml"><xi:fallback /></xi:include>
    - </index>
    <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
    </book>
    --- a/doc/reference/libpurple/libpurple-docs.xml Tue Oct 08 21:47:58 2019 -0500
    +++ b/doc/reference/libpurple/libpurple-docs.xml Tue Oct 08 21:48:28 2019 -0500
    @@ -89,7 +89,6 @@
    <xi:include href="xml/version.xml" />
    <xi:include href="xml/util.xml" />
    <xi:include href="xml/enums.xml" />
    - <xi:include href="xml/purple.xml" />
    </part>
    <part id="Submodules">
    @@ -165,10 +164,14 @@
    <title>Index of deprecated symbols</title>
    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
    </index>
    - <index id="api-index-2.11.0" role="2.11.0">
    + <index id="api-index-2-11-0" role="2.11.0">
    <title>Index of new symbols in 2.11.0</title>
    <xi:include href="xml/api-index-2.11.0.xml"><xi:fallback /></xi:include>
    </index>
    + <index id="api-index-3-0-0" role="3.0.0">
    + <title>Index of new symbols in 3.0.0</title>
    + <xi:include href="xml/api-index-3.0.0.xml"><xi:fallback /></xi:include>
    + </index>
    <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
    </book>
    --- a/doc/reference/pidgin/pidgin-docs.xml Tue Oct 08 21:47:58 2019 -0500
    +++ b/doc/reference/pidgin/pidgin-docs.xml Tue Oct 08 21:48:28 2019 -0500
    @@ -19,36 +19,27 @@
    <part id="API">
    <title>API Reference</title>
    - <xi:include href="xml/pidginabout.xml" />
    <xi:include href="xml/gtkaccount.xml" />
    + <xi:include href="xml/gtkblist-theme-loader.xml" />
    + <xi:include href="xml/gtkblist-theme.xml" />
    <xi:include href="xml/gtkblist.xml" />
    - <xi:include href="xml/gtkblist-theme.xml" />
    - <xi:include href="xml/gtkblist-theme-loader.xml" />
    - <xi:include href="xml/gtkpounce.xml" />
    <xi:include href="xml/gtkcellrendererexpander.xml" />
    <xi:include href="xml/gtkconn.xml" />
    <xi:include href="xml/gtkconv.xml" />
    <xi:include href="xml/gtkconvwin.xml" />
    - <xi:include href="xml/gtknickcolors.xml" />
    - <xi:include href="xml/gtkconv-theme.xml" />
    - <xi:include href="xml/gtkconv-theme-loader.xml" />
    - <xi:include href="xml/pidgindebug.xml" />
    + <xi:include href="xml/gtkdialogs.xml" />
    + <xi:include href="xml/gtkdnd-hints.xml" />
    <xi:include href="xml/gtkdocklet.xml" />
    - <xi:include href="xml/gtkdnd-hints.xml" />
    - <xi:include href="xml/gtkxfer.xml" />
    - <xi:include href="xml/pidgingdkpixbuf.xml" />
    + <xi:include href="xml/gtkicon-theme-loader.xml" />
    <xi:include href="xml/gtkicon-theme.xml" />
    <xi:include href="xml/gtkidle.xml" />
    - <xi:include href="xml/pidginlog.xml" />
    <xi:include href="xml/gtkmedia.xml" />
    - <xi:include href="xml/minidialog.xml" />
    + <xi:include href="xml/gtkmenutray.xml" />
    + <xi:include href="xml/gtknickcolors.xml" />
    <xi:include href="xml/gtknotify.xml" />
    - <xi:include href="xml/gtkdialogs.xml" />
    - <xi:include href="xml/gtkstatus-icon-theme.xml" />
    - <xi:include href="xml/gtkicon-theme-loader.xml" />
    - <xi:include href="xml/pidgintooltip.xml" />
    <xi:include href="xml/gtkplugin.xml" />
    <xi:include href="xml/gtkpluginpref.xml" />
    + <xi:include href="xml/gtkpounce.xml" />
    <xi:include href="xml/gtkprefs.xml" />
    <xi:include href="xml/gtkprivacy.xml" />
    <xi:include href="xml/gtkrequest.xml" />
    @@ -58,16 +49,22 @@
    <xi:include href="xml/gtksmiley-manager.xml" />
    <xi:include href="xml/gtksmiley-theme.xml" />
    <xi:include href="xml/gtksound.xml" />
    + <xi:include href="xml/gtkstatus-icon-theme.xml" />
    <xi:include href="xml/gtkstatusbox.xml" />
    + <xi:include href="xml/gtkstyle.xml" />
    + <xi:include href="xml/gtkutils.xml" />
    + <xi:include href="xml/gtkwhiteboard.xml" />
    + <xi:include href="xml/gtkxfer.xml" />
    + <xi:include href="xml/minidialog.xml" />
    + <xi:include href="xml/pidgin.xml" />
    + <xi:include href="xml/pidginabout.xml" />
    + <xi:include href="xml/pidgindebug.xml" />
    + <xi:include href="xml/pidgingdkpixbuf.xml" />
    + <xi:include href="xml/pidginlog.xml" />
    + <xi:include href="xml/pidginmessage.xml" />
    <xi:include href="xml/pidginstock.xml" />
    - <xi:include href="xml/gtkstyle.xml" />
    <xi:include href="xml/pidgintalkatu.xml" />
    - <xi:include href="xml/gtkmenutray.xml" />
    - <xi:include href="xml/gtkutils.xml" />
    - <xi:include href="xml/gtkwebview.xml" />
    - <xi:include href="xml/gtkwebviewtoolbar.xml" />
    - <xi:include href="xml/gtkwhiteboard.xml" />
    - <xi:include href="xml/pidgin.xml" />
    + <xi:include href="xml/pidgintooltip.xml" />
    </part>
    <part id="signals">
    --- a/finch/finch.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/finch.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,4 @@
    -/**
    +/*
    * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    --- a/finch/finch.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/finch.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FINCH_H_
    -#define _FINCH_H_
    +#ifndef FINCH_H
    +#define FINCH_H
    +
    /**
    * SECTION:finch
    * @section_id: finch-finch
    @@ -34,14 +36,16 @@
    #define FINCH_PREFS_ROOT "/finch"
    -#define FINCH_GET_DATA(obj) (obj)->ui_data
    -#define FINCH_SET_DATA(obj, data) (obj)->ui_data = data
    -
    /**
    * finch_start:
    + * @argc: Address of the argc parameter of your main() function (or 0 if argv
    + * is %NULL). This will be changed if any arguments were handled.
    + * @argv: Address of the argv parameter of main(), or %NULL. Any options
    + * understood by Finch are stripped before return.
    *
    * Start finch with the given command line arguments.
    */
    gboolean finch_start(int *argc, char ***argv);
    -#endif
    +#endif /* FINCH_H */
    +
    --- a/finch/gntaccount.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntaccount.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include <gnt.h>
    @@ -170,6 +172,7 @@
    "server."),
    purple_request_cpar_from_account(
    account));
    + g_string_free(username, TRUE);
    return;
    }
    @@ -183,6 +186,7 @@
    purple_request_cpar_from_account(
    account));
    g_free(oldproto);
    + g_string_free(username, TRUE);
    return;
    }
    g_free(oldproto);
    @@ -258,7 +262,7 @@
    gnt_box_give_focus_to_child(GNT_BOX(accounts.window), accounts.tree);
    }
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user) &&
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, register_user) &&
    gnt_check_box_get_checked(GNT_CHECK_BOX(dialog->regserver))) {
    purple_account_register(account);
    } else if (dialog->account == NULL) {
    @@ -493,7 +497,7 @@
    /* Show the registration checkbox only in a new account dialog,
    * and when the selected protocol has the support for it. */
    gnt_widget_set_visible(dialog->regserver, account == NULL &&
    - PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user));
    + PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, register_user));
    }
    static void
    @@ -510,10 +514,11 @@
    if (dialog->account)
    gnt_check_box_set_checked(GNT_CHECK_BOX(dialog->newmail),
    purple_account_get_check_mail(dialog->account));
    - if (!protocol || !(purple_protocol_get_options(protocol) & OPT_PROTO_MAIL_CHECK))
    + if (!(purple_protocol_get_options(protocol) & OPT_PROTO_MAIL_CHECK)) {
    gnt_widget_set_visible(dialog->newmail, FALSE);
    - else
    + } else {
    gnt_widget_set_visible(dialog->newmail, TRUE);
    + }
    if (dialog->remember == NULL)
    dialog->remember = gnt_check_box_new(_("Remember password"));
    --- a/finch/gntaccount.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntaccount.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_ACCOUNT_H
    -#define _GNT_ACCOUNT_H
    +#ifndef FINCH_ACCOUNT_H
    +#define FINCH_ACCOUNT_H
    +
    /**
    * SECTION:gntaccount
    * @section_id: finch-gntaccount
    @@ -72,4 +74,5 @@
    */
    void finch_account_dialog_show(PurpleAccount *account);
    -#endif
    +#endif /* FINCH_ACCOUNT_H */
    +
    --- a/finch/gntblist.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntblist.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include "finch.h"
    @@ -65,8 +67,9 @@
    #define SHOW_EMPTY_GROUP_TIMEOUT 60
    -typedef struct
    -{
    +struct _FinchBuddyList {
    + PurpleBuddyList parent;
    +
    GntWidget *window;
    GntWidget *tree;
    @@ -96,7 +99,7 @@
    guint new_group_timeout;
    FinchBlistManager *manager;
    -} FinchBlist;
    +};
    typedef struct
    {
    @@ -122,25 +125,29 @@
    } u;
    } StatusBoxItem;
    -static FinchBlist *ggblist;
    -
    -static void add_buddy(PurpleBuddy *buddy, FinchBlist *ggblist);
    -static void add_contact(PurpleContact *contact, FinchBlist *ggblist);
    -static void add_group(PurpleGroup *group, FinchBlist *ggblist);
    -static void add_chat(PurpleChat *chat, FinchBlist *ggblist);
    -static void add_node(PurpleBlistNode *node, FinchBlist *ggblist);
    +static FinchBuddyList *ggblist;
    +
    +static void add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist);
    +static void add_contact(PurpleContact *contact, FinchBuddyList *ggblist);
    +static void add_group(PurpleGroup *group, FinchBuddyList *ggblist);
    +static void add_chat(PurpleChat *chat, FinchBuddyList *ggblist);
    +static void add_node(PurpleBlistNode *node, FinchBuddyList *ggblist);
    static void node_update(PurpleBuddyList *list, PurpleBlistNode *node);
    -static void draw_tooltip(FinchBlist *ggblist);
    +static void draw_tooltip(FinchBuddyList *ggblist);
    static void tooltip_for_buddy(PurpleBuddy *buddy, GString *str, gboolean full);
    static gboolean remove_typing_cb(gpointer null);
    -static void remove_peripherals(FinchBlist *ggblist);
    +static void remove_peripherals(FinchBuddyList *ggblist);
    static const char * get_display_name(PurpleBlistNode *node);
    static void savedstatus_changed(PurpleSavedStatus *now, PurpleSavedStatus *old);
    static void blist_show(PurpleBuddyList *list);
    -static void update_node_display(PurpleBlistNode *buddy, FinchBlist *ggblist);
    -static void update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist);
    +static void update_node_display(PurpleBlistNode *buddy,
    + FinchBuddyList *ggblist);
    +static void update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist);
    static gboolean account_autojoin_cb(PurpleConnection *pc, gpointer null);
    -static void finch_request_add_buddy(PurpleAccount *account, const char *username, const char *grp, const char *alias);
    +static void finch_request_add_buddy(PurpleBuddyList *list,
    + PurpleAccount *account,
    + const char *username, const char *grp,
    + const char *alias);
    static void menu_group_set_cb(GntMenuItem *item, gpointer null);
    /* Sort functions */
    @@ -227,10 +234,6 @@
    int lastseen = 0;
    char *title;
    - if (!node || !(PURPLE_IS_BUDDY(node) || PURPLE_IS_CONTACT(node) ||
    - PURPLE_IS_GROUP(node) || PURPLE_IS_CHAT(node)))
    - return FALSE;
    -
    str = g_string_new("");
    if (PURPLE_IS_CONTACT(node)) {
    @@ -369,7 +372,7 @@
    }
    static GntTextFormatFlags
    -get_blist_node_flag(PurpleBlistNode *node)
    +get_blist_node_flag(FinchBuddyList *ggblist, PurpleBlistNode *node)
    {
    GntTextFormatFlags flag = 0;
    FinchBlistNode *fnode = purple_blist_node_get_ui_data(node);
    @@ -390,19 +393,20 @@
    }
    static void
    -blist_update_row_flags(PurpleBlistNode *node)
    +blist_update_row_flags(FinchBuddyList *ggblist, PurpleBlistNode *node)
    {
    - gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, get_blist_node_flag(node));
    + gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node,
    + get_blist_node_flag(ggblist, node));
    gnt_tree_set_row_color(GNT_TREE(ggblist->tree), node, get_display_color(node));
    }
    static void
    -new_node(PurpleBlistNode *node)
    +new_node(PurpleBuddyList *list, PurpleBlistNode *node)
    {
    }
    static void
    -add_node(PurpleBlistNode *node, FinchBlist *ggblist)
    +add_node(PurpleBlistNode *node, FinchBuddyList *ggblist)
    {
    if (purple_blist_node_get_ui_data(node))
    return;
    @@ -428,7 +432,7 @@
    }
    static void
    -remove_tooltip(FinchBlist *ggblist)
    +remove_tooltip(FinchBuddyList *ggblist)
    {
    gnt_widget_destroy(ggblist->tooltip);
    ggblist->tooltip = NULL;
    @@ -438,7 +442,7 @@
    static void
    node_remove(PurpleBuddyList *list, PurpleBlistNode *node)
    {
    - FinchBlist *ggblist = FINCH_GET_DATA(list);
    + FinchBuddyList *ggblist = FINCH_BUDDY_LIST(list);
    PurpleBlistNode *parent;
    if (ggblist == NULL || purple_blist_node_get_ui_data(node) == NULL)
    @@ -471,14 +475,15 @@
    static void
    node_update(PurpleBuddyList *list, PurpleBlistNode *node)
    {
    + FinchBuddyList *ggblist;
    +
    + g_return_if_fail(FINCH_IS_BUDDY_LIST(list));
    /* It really looks like this should never happen ... but it does.
    This will at least emit a warning to the log when it
    happens, so maybe someone will figure it out. */
    g_return_if_fail(node != NULL);
    - if (FINCH_GET_DATA(list)== NULL)
    - return; /* XXX: this is probably the place to auto-join chats */
    -
    + ggblist = FINCH_BUDDY_LIST(list);
    if (ggblist->window == NULL)
    return;
    @@ -486,7 +491,7 @@
    gnt_tree_change_text(GNT_TREE(ggblist->tree), node,
    0, get_display_name(node));
    gnt_tree_sort_row(GNT_TREE(ggblist->tree), node);
    - blist_update_row_flags(node);
    + blist_update_row_flags(ggblist, node);
    if (gnt_tree_get_parent_key(GNT_TREE(ggblist->tree), node) !=
    ggblist->manager->find_parent(node))
    node_remove(list, node);
    @@ -494,60 +499,36 @@
    if (PURPLE_IS_BUDDY(node)) {
    PurpleBuddy *buddy = (PurpleBuddy*)node;
    - add_node((PurpleBlistNode*)buddy, FINCH_GET_DATA(list));
    + add_node((PurpleBlistNode *)buddy, FINCH_BUDDY_LIST(list));
    node_update(list, purple_blist_node_get_parent(node));
    } else if (PURPLE_IS_CHAT(node)) {
    - add_node(node, FINCH_GET_DATA(list));
    + add_node(node, FINCH_BUDDY_LIST(list));
    } else if (PURPLE_IS_CONTACT(node)) {
    if (purple_blist_node_get_ui_data(node)== NULL) {
    /* The core seems to expect the UI to add the buddies. */
    for (node = purple_blist_node_get_first_child(node); node; node = purple_blist_node_get_sibling_next(node))
    - add_node(node, FINCH_GET_DATA(list));
    + add_node(node, FINCH_BUDDY_LIST(list));
    }
    } else if (PURPLE_IS_GROUP(node)) {
    if (!ggblist->manager->can_add_node(node))
    node_remove(list, node);
    else
    - add_node(node, FINCH_GET_DATA(list));
    + add_node(node, FINCH_BUDDY_LIST(list));
    }
    if (ggblist->tnode == node) {
    draw_tooltip(ggblist);
    }
    }
    -static void
    -new_list(PurpleBuddyList *list)
    -{
    - if (ggblist)
    - return;
    -
    - ggblist = g_new0(FinchBlist, 1);
    - FINCH_SET_DATA(list, ggblist);
    - ggblist->manager = finch_blist_manager_find(purple_prefs_get_string(PREF_ROOT "/grouping"));
    - if (!ggblist->manager)
    - ggblist->manager = &default_manager;
    -}
    -
    -static void destroy_list(PurpleBuddyList *list)
    -{
    - if (ggblist == NULL)
    - return;
    -
    - gnt_widget_destroy(ggblist->window);
    - g_free(ggblist);
    - ggblist = NULL;
    -}
    -
    static gboolean
    remove_new_empty_group(gpointer data)
    {
    PurpleBuddyList *list;
    -
    - if (!ggblist)
    - return FALSE;
    -
    - list = purple_blist_get_buddy_list();
    + FinchBuddyList *ggblist;
    +
    + list = purple_blist_get_default();
    g_return_val_if_fail(list, FALSE);
    + ggblist = FINCH_BUDDY_LIST(list);
    ggblist->new_group_timeout = 0;
    while (ggblist->new_group) {
    @@ -582,7 +563,8 @@
    if (error)
    {
    - finch_request_add_buddy(account, username, group, alias);
    + finch_request_add_buddy(purple_blist_get_default(), account,
    + username, group, alias);
    purple_notify_error(NULL, _("Error"), _("Error adding buddy"),
    error, purple_request_cpar_from_account(account));
    return;
    @@ -607,7 +589,9 @@
    }
    static void
    -finch_request_add_buddy(PurpleAccount *account, const char *username, const char *grp, const char *alias)
    +finch_request_add_buddy(PurpleBuddyList *list, PurpleAccount *account,
    + const char *username, const char *grp,
    + const char *alias)
    {
    PurpleRequestFields *fields = purple_request_fields_new();
    PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
    @@ -705,7 +689,8 @@
    }
    static void
    -finch_request_add_chat(PurpleAccount *account, PurpleGroup *grp, const char *alias, const char *name)
    +finch_request_add_chat(PurpleBuddyList *list, PurpleAccount *account,
    + PurpleGroup *grp, const char *alias, const char *name)
    {
    PurpleRequestFields *fields = purple_request_fields_new();
    PurpleRequestFieldGroup *group = purple_request_field_group_new(NULL);
    @@ -739,13 +724,14 @@
    }
    static void
    -add_group_cb(gpointer null, const char *group)
    +add_group_cb(FinchBuddyList *ggblist, const char *group)
    {
    PurpleGroup *grp;
    if (!group || !*group) {
    purple_notify_error(NULL, _("Error"), _("Error adding group"),
    _("You must give a name for the group to add."), NULL);
    + g_object_unref(ggblist);
    return;
    }
    @@ -755,9 +741,6 @@
    purple_blist_add_group(grp, NULL);
    }
    - if (!ggblist)
    - return;
    -
    /* Treat the group as a new group even if it had existed before. This should
    * make things easier to add buddies to empty groups (new or old) without having
    * to turn on 'show empty groups' setting */
    @@ -774,34 +757,19 @@
    add_node((PurpleBlistNode*)grp, ggblist);
    gnt_tree_set_selected(GNT_TREE(ggblist->tree), grp);
    }
    +
    + g_object_unref(ggblist);
    }
    static void
    -finch_request_add_group(void)
    -{
    - purple_request_input(NULL, _("Add Group"), NULL, _("Enter the name of the group"),
    - NULL, FALSE, FALSE, NULL,
    - _("Add"), G_CALLBACK(add_group_cb), _("Cancel"), NULL,
    - NULL, NULL);
    -}
    -
    -static PurpleBlistUiOps blist_ui_ops =
    +finch_request_add_group(PurpleBuddyList *list)
    {
    - new_list,
    - new_node,
    - blist_show,
    - node_update,
    - node_remove,
    - destroy_list,
    - NULL,
    - finch_request_add_buddy,
    - finch_request_add_chat,
    - finch_request_add_group,
    - NULL,
    - NULL,
    - NULL,
    - NULL, NULL, NULL, NULL
    -};
    + purple_request_input(NULL, _("Add Group"), NULL,
    + _("Enter the name of the group"), NULL, FALSE,
    + FALSE, NULL, _("Add"), G_CALLBACK(add_group_cb),
    + _("Cancel"), G_CALLBACK(g_object_unref), NULL,
    + g_object_ref(list));
    +}
    static gpointer
    finch_blist_get_handle(void)
    @@ -812,7 +780,7 @@
    }
    static void
    -add_group(PurpleGroup *group, FinchBlist *ggblist)
    +add_group(PurpleGroup *group, FinchBuddyList *ggblist)
    {
    gpointer parent;
    PurpleBlistNode *node = (PurpleBlistNode *)group;
    @@ -885,7 +853,7 @@
    }
    static void
    -add_chat(PurpleChat *chat, FinchBlist *ggblist)
    +add_chat(PurpleChat *chat, FinchBuddyList *ggblist)
    {
    gpointer parent;
    PurpleBlistNode *node = (PurpleBlistNode *)chat;
    @@ -902,7 +870,7 @@
    }
    static void
    -add_contact(PurpleContact *contact, FinchBlist *ggblist)
    +add_contact(PurpleContact *contact, FinchBuddyList *ggblist)
    {
    gpointer parent;
    PurpleBlistNode *node = (PurpleBlistNode*)contact;
    @@ -925,7 +893,7 @@
    }
    static void
    -add_buddy(PurpleBuddy *buddy, FinchBlist *ggblist)
    +add_buddy(PurpleBuddy *buddy, FinchBuddyList *ggblist)
    {
    gpointer parent;
    PurpleBlistNode *node = (PurpleBlistNode *)buddy;
    @@ -941,18 +909,14 @@
    gnt_tree_create_row(GNT_TREE(ggblist->tree), get_display_name(node)),
    parent, NULL));
    - blist_update_row_flags((PurpleBlistNode*)buddy);
    - if (buddy == purple_contact_get_priority_buddy(contact))
    - blist_update_row_flags((PurpleBlistNode*)contact);
    -}
    -
    -PurpleBlistUiOps *finch_blist_get_ui_ops()
    -{
    - return &blist_ui_ops;
    + blist_update_row_flags(ggblist, (PurpleBlistNode *)buddy);
    + if (buddy == purple_contact_get_priority_buddy(contact)) {
    + blist_update_row_flags(ggblist, (PurpleBlistNode *)contact);
    + }
    }
    static void
    -selection_activate(GntWidget *widget, FinchBlist *ggblist)
    +selection_activate(GntWidget *widget, FinchBuddyList *ggblist)
    {
    GntTree *tree = GNT_TREE(ggblist->tree);
    PurpleBlistNode *node = gnt_tree_get_selection_data(tree);
    @@ -990,8 +954,9 @@
    GList *list;
    PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    - if(!protocol || !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, blist_node_menu))
    + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, blist_node_menu)) {
    return;
    + }
    for(list = purple_protocol_client_iface_blist_node_menu(protocol, node); list;
    list = g_list_delete_link(list, list))
    @@ -1193,9 +1158,9 @@
    purple_blist_node_set_bool(buddy, "show_offline",
    !purple_blist_node_get_bool(buddy, "show_offline"));
    if (!ggblist->manager->can_add_node(buddy))
    - node_remove(purple_blist_get_buddy_list(), buddy);
    + node_remove(purple_blist_get_default(), buddy);
    else
    - node_update(purple_blist_get_buddy_list(), buddy);
    + node_update(purple_blist_get_default(), buddy);
    }
    static void
    @@ -1208,7 +1173,7 @@
    PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(buddy));
    protocol = purple_connection_get_protocol(gc);
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info))
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info))
    {
    add_custom_action(menu, _("Get Info"),
    PURPLE_CALLBACK(finch_blist_get_buddy_info_cb), buddy);
    @@ -1487,19 +1452,18 @@
    PurpleGroup *tg = NULL;
    PurpleContact *tc = NULL;
    - if (target == NULL || !(PURPLE_IS_BUDDY(target) || PURPLE_IS_CONTACT(target) ||
    - PURPLE_IS_GROUP(target) || PURPLE_IS_CHAT(target)))
    - return;
    -
    if (PURPLE_IS_GROUP(target))
    tg = (PurpleGroup*)target;
    else if (PURPLE_IS_BUDDY(target)) {
    tc = (PurpleContact*)purple_blist_node_get_parent(target);
    tg = (PurpleGroup*)purple_blist_node_get_parent((PurpleBlistNode*)tc);
    + } else if (PURPLE_IS_CONTACT(target)) {
    + tc = (PurpleContact *)target;
    + tg = (PurpleGroup *)purple_blist_node_get_parent(target);
    + } else if (PURPLE_IS_CHAT(target)) {
    + tg = (PurpleGroup*)purple_blist_node_get_parent(target);
    } else {
    - if (PURPLE_IS_CONTACT(target))
    - tc = (PurpleContact*)target;
    - tg = (PurpleGroup*)purple_blist_node_get_parent(target);
    + return;
    }
    if (ggblist->tagged) {
    @@ -1552,13 +1516,13 @@
    }
    static void
    -context_menu_destroyed(GntWidget *widget, FinchBlist *ggblist)
    +context_menu_destroyed(GntWidget *widget, FinchBuddyList *ggblist)
    {
    ggblist->context = NULL;
    }
    static void
    -draw_context_menu(FinchBlist *ggblist)
    +draw_context_menu(FinchBuddyList *ggblist)
    {
    PurpleBlistNode *node = NULL;
    GntWidget *context = NULL;
    @@ -1713,7 +1677,7 @@
    }
    static gboolean
    -draw_tooltip_real(FinchBlist *ggblist)
    +draw_tooltip_real(FinchBuddyList *ggblist)
    {
    PurpleBlistNode *node;
    int x, y, top, width, w, h;
    @@ -1783,7 +1747,7 @@
    }
    static void
    -draw_tooltip(FinchBlist *ggblist)
    +draw_tooltip(FinchBuddyList *ggblist)
    {
    /* When an account has signed off, it removes one buddy at a time.
    * Drawing the tooltip after removing each buddy is expensive. On
    @@ -1796,21 +1760,22 @@
    }
    static void
    -selection_changed(GntWidget *widget, gpointer old, gpointer current, FinchBlist *ggblist)
    +selection_changed(GntWidget *widget, gpointer old, gpointer current,
    + FinchBuddyList *ggblist)
    {
    remove_peripherals(ggblist);
    draw_tooltip(ggblist);
    }
    static gboolean
    -context_menu(GntWidget *widget, FinchBlist *ggblist)
    +context_menu(GntWidget *widget, FinchBuddyList *ggblist)
    {
    draw_context_menu(ggblist);
    return TRUE;
    }
    static gboolean
    -key_pressed(GntWidget *widget, const char *text, FinchBlist *ggblist)
    +key_pressed(GntWidget *widget, const char *text, FinchBuddyList *ggblist)
    {
    if (text[0] == 27 && text[1] == 0) {
    /* Escape was pressed */
    @@ -1837,14 +1802,14 @@
    }
    static void
    -update_node_display(PurpleBlistNode *node, FinchBlist *ggblist)
    +update_node_display(PurpleBlistNode *node, FinchBuddyList *ggblist)
    {
    - GntTextFormatFlags flag = get_blist_node_flag(node);
    + GntTextFormatFlags flag = get_blist_node_flag(ggblist, node);
    gnt_tree_set_row_flags(GNT_TREE(ggblist->tree), node, flag);
    }
    static void
    -update_buddy_display(PurpleBuddy *buddy, FinchBlist *ggblist)
    +update_buddy_display(PurpleBuddy *buddy, FinchBuddyList *ggblist)
    {
    PurpleContact *contact;
    @@ -1853,28 +1818,31 @@
    gnt_tree_change_text(GNT_TREE(ggblist->tree), buddy, 0, get_display_name((PurpleBlistNode*)buddy));
    gnt_tree_change_text(GNT_TREE(ggblist->tree), contact, 0, get_display_name((PurpleBlistNode*)contact));
    - blist_update_row_flags((PurpleBlistNode *)buddy);
    + blist_update_row_flags(ggblist, (PurpleBlistNode *)buddy);
    if (buddy == purple_contact_get_priority_buddy(contact))
    - blist_update_row_flags((PurpleBlistNode *)contact);
    -
    - if (ggblist->tnode == (PurpleBlistNode*)buddy)
    + blist_update_row_flags(ggblist, (PurpleBlistNode *)contact);
    +
    + if (ggblist->tnode == (PurpleBlistNode *)buddy) {
    draw_tooltip(ggblist);
    + }
    }
    static void
    -buddy_status_changed(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *now, FinchBlist *ggblist)
    +buddy_status_changed(PurpleBuddy *buddy, PurpleStatus *old, PurpleStatus *now,
    + FinchBuddyList *ggblist)
    {
    update_buddy_display(buddy, ggblist);
    }
    static void
    -buddy_idle_changed(PurpleBuddy *buddy, int old, int new, FinchBlist *ggblist)
    +buddy_idle_changed(PurpleBuddy *buddy, int old, int new,
    + FinchBuddyList *ggblist)
    {
    update_buddy_display(buddy, ggblist);
    }
    static void
    -remove_peripherals(FinchBlist *ggblist)
    +remove_peripherals(FinchBuddyList *ggblist)
    {
    if (ggblist->tooltip)
    remove_tooltip(ggblist);
    @@ -1903,9 +1871,8 @@
    {
    PurpleBlistNode *node;
    purple_signals_disconnect_by_handle(finch_blist_get_handle());
    - FINCH_SET_DATA(purple_blist_get_buddy_list(), NULL);
    -
    - node = purple_blist_get_root();
    +
    + node = purple_blist_get_default_root();
    while (node) {
    reset_blist_node_ui_data(node);
    node = purple_blist_node_next(node, TRUE);
    @@ -1922,7 +1889,6 @@
    if (ggblist->new_group)
    g_list_free(ggblist->new_group);
    - g_free(ggblist);
    ggblist = NULL;
    }
    @@ -1946,8 +1912,8 @@
    (GCompareFunc)blist_node_compare_log);
    }
    - list = purple_blist_get_buddy_list();
    - node = purple_blist_get_root();
    + list = purple_blist_get_default();
    + node = purple_blist_get_root(list);
    while (node)
    {
    node_update(list, node);
    @@ -1958,8 +1924,7 @@
    static void
    destroy_status_list(GList *list)
    {
    - g_list_foreach(list, (GFunc)g_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_free);
    }
    static void
    @@ -2049,9 +2014,10 @@
    sel = gnt_tree_get_selection_data(GNT_TREE(ggblist->tree));
    gnt_tree_remove_all(GNT_TREE(ggblist->tree));
    - node = purple_blist_get_root();
    - for (; node; node = purple_blist_node_next(node, TRUE))
    + for (node = purple_blist_get_default_root(); node;
    + node = purple_blist_node_next(node, TRUE)) {
    reset_blist_node_ui_data(node);
    + }
    populate_buddylist();
    gnt_tree_set_selected(GNT_TREE(ggblist->tree), sel);
    draw_tooltip(ggblist);
    @@ -2452,7 +2418,7 @@
    fnode->signed_timer = 0;
    if (!ggblist->manager->can_add_node(node)) {
    - node_remove(purple_blist_get_buddy_list(), node);
    + node_remove(purple_blist_get_default(), node);
    } else {
    update_node_display(node, ggblist);
    if (purple_blist_node_get_parent(node) && PURPLE_IS_CONTACT(purple_blist_node_get_parent(node)))
    @@ -2513,7 +2479,8 @@
    if (!purple_plugin_info_get_actions_cb(info))
    continue;
    - item = gnt_menuitem_new(_(purple_plugin_info_get_name(info)));
    + item = gnt_menuitem_new(_(gplugin_plugin_info_get_name(
    + GPLUGIN_PLUGIN_INFO(info))));
    gnt_menu_add_item(GNT_MENU(sub), item);
    build_plugin_actions(item, plugin);
    }
    @@ -2546,7 +2513,7 @@
    continue;
    protocol = purple_connection_get_protocol(gc);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_actions)) {
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) {
    item = gnt_menuitem_new(purple_account_get_username(account));
    gnt_menu_add_item(GNT_MENU(sub), item);
    build_protocol_actions(item, protocol, gc);
    @@ -2585,8 +2552,8 @@
    PurpleConnection *pc = data;
    PurpleAccount *account = purple_connection_get_account(pc);
    - for (node = purple_blist_get_root(); node;
    - node = purple_blist_node_next(node, FALSE)) {
    + for (node = purple_blist_get_default_root(); node;
    + node = purple_blist_node_next(node, FALSE)) {
    if (PURPLE_IS_CHAT(node)) {
    PurpleChat *chat = (PurpleChat*)node;
    if (purple_chat_get_account(chat) == account &&
    @@ -2658,14 +2625,12 @@
    purple_request_field_choice_add(field, _("Unblock"), GINT_TO_POINTER(2));
    purple_request_field_group_add_field(group, field);
    - purple_request_fields(purple_blist_get_buddy_list(), _("Block/Unblock"),
    - NULL,
    - _("Please enter the username or alias of the person "
    - "you would like to Block/Unblock."),
    - fields,
    - _("OK"), G_CALLBACK(block_select_cb),
    - _("Cancel"), NULL,
    - NULL, NULL);
    + purple_request_fields(
    + purple_blist_get_default(), _("Block/Unblock"), NULL,
    + _("Please enter the username or alias of the person "
    + "you would like to Block/Unblock."),
    + fields, _("OK"), G_CALLBACK(block_select_cb), _("Cancel"), NULL,
    + NULL, NULL);
    }
    /* send_im_select* -- Xerox */
    @@ -2708,14 +2673,12 @@
    purple_request_field_set_required(field, TRUE);
    purple_request_field_group_add_field(group, field);
    - purple_request_fields(purple_blist_get_buddy_list(), _("New Instant Message"),
    - NULL,
    - _("Please enter the username or alias of the person "
    - "you would like to IM."),
    - fields,
    - _("OK"), G_CALLBACK(send_im_select_cb),
    - _("Cancel"), NULL,
    - NULL, NULL);
    + purple_request_fields(
    + purple_blist_get_default(), _("New Instant Message"), NULL,
    + _("Please enter the username or alias of the person "
    + "you would like to IM."),
    + fields, _("OK"), G_CALLBACK(send_im_select_cb), _("Cancel"),
    + NULL, NULL, NULL);
    }
    static void
    @@ -2781,13 +2744,11 @@
    purple_request_field_set_required(field, TRUE);
    purple_request_field_group_add_field(group, field);
    - purple_request_fields(purple_blist_get_buddy_list(), _("Join a Chat"),
    - NULL,
    - _("Please enter the name of the chat you want to join."),
    - fields,
    - _("Join"), G_CALLBACK(join_chat_select_cb),
    - _("Cancel"), NULL,
    - NULL, NULL);
    + purple_request_fields(
    + purple_blist_get_default(), _("Join a Chat"), NULL,
    + _("Please enter the name of the chat you want to join."),
    + fields, _("Join"), G_CALLBACK(join_chat_select_cb), _("Cancel"),
    + NULL, NULL, NULL);
    }
    static void
    @@ -2841,14 +2802,12 @@
    purple_request_field_group_add_field(group, field);
    purple_request_field_account_set_show_all(field, TRUE);
    - purple_request_fields(purple_blist_get_buddy_list(), _("View Log"),
    - NULL,
    - _("Please enter the username or alias of the person "
    - "whose log you would like to view."),
    - fields,
    - _("OK"), G_CALLBACK(view_log_select_cb),
    - _("Cancel"), NULL,
    - NULL, NULL);
    + purple_request_fields(
    + purple_blist_get_default(), _("View Log"), NULL,
    + _("Please enter the username or alias of the person "
    + "whose log you would like to view."),
    + fields, _("OK"), G_CALLBACK(view_log_select_cb), _("Cancel"),
    + NULL, NULL, NULL);
    }
    static void
    @@ -3000,7 +2959,7 @@
    void finch_blist_show()
    {
    - blist_show(purple_blist_get_buddy_list());
    + blist_show(purple_blist_get_default());
    }
    static void
    @@ -3013,9 +2972,7 @@
    static void
    blist_show(PurpleBuddyList *list)
    {
    - if (ggblist == NULL)
    - new_list(list);
    - else if (ggblist->window) {
    + if (ggblist->window) {
    gnt_window_present(ggblist->window);
    return;
    }
    @@ -3164,6 +3121,54 @@
    }
    /**************************************************************************
    + * GObject code
    + **************************************************************************/
    +G_DEFINE_TYPE(FinchBuddyList, finch_buddy_list, PURPLE_TYPE_BUDDY_LIST)
    +
    +static void
    +finch_buddy_list_init(FinchBuddyList *self)
    +{
    + if (!ggblist) {
    + /* The first buddy list object becomes the default. */
    + ggblist = self;
    + }
    +
    + self->manager = finch_blist_manager_find(
    + purple_prefs_get_string(PREF_ROOT "/grouping"));
    + if (!self->manager) {
    + self->manager = &default_manager;
    + }
    +}
    +
    +static void
    +finch_buddy_list_finalize(GObject *obj)
    +{
    + FinchBuddyList *ggblist = FINCH_BUDDY_LIST(obj);
    +
    + gnt_widget_destroy(ggblist->window);
    +
    + G_OBJECT_CLASS(finch_buddy_list_parent_class)->finalize(obj);
    +}
    +
    +static void
    +finch_buddy_list_class_init(FinchBuddyListClass *klass)
    +{
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + PurpleBuddyListClass *purple_blist_class;
    +
    + obj_class->finalize = finch_buddy_list_finalize;
    +
    + purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
    + purple_blist_class->new_node = new_node;
    + purple_blist_class->show = blist_show;
    + purple_blist_class->update = node_update;
    + purple_blist_class->remove = node_remove;
    + purple_blist_class->request_add_buddy = finch_request_add_buddy;
    + purple_blist_class->request_add_chat = finch_request_add_chat;
    + purple_blist_class->request_add_group = finch_request_add_group;
    +}
    +
    +/**************************************************************************
    * GBoxed code
    **************************************************************************/
    static FinchBlistManager *
    --- a/finch/gntblist.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntblist.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_BLIST_H
    -#define _GNT_BLIST_H
    +#ifndef FINCH_BLIST_H
    +#define FINCH_BLIST_H
    +
    /**
    * SECTION:gntblist
    * @section_id: finch-gntblist
    @@ -32,6 +34,7 @@
    #include "gnt.h"
    #include "gnttree.h"
    +#define FINCH_TYPE_BUDDY_LIST (finch_buddy_list_get_type())
    #define FINCH_TYPE_BLIST_MANAGER (finch_blist_manager_get_type())
    /**********************************************************************
    @@ -74,14 +77,8 @@
    */
    GType finch_blist_manager_get_type(void);
    -/**
    - * finch_blist_get_ui_ops:
    - *
    - * Get the ui-functions.
    - *
    - * Returns: The PurpleBlistUiOps structure populated with the appropriate functions.
    - */
    -PurpleBlistUiOps * finch_blist_get_ui_ops(void);
    +G_DECLARE_FINAL_TYPE(FinchBuddyList, finch_buddy_list, FINCH, BUDDY_LIST,
    + PurpleBuddyList)
    /**
    * finch_blist_init:
    @@ -200,4 +197,5 @@
    */
    void finch_blist_manager_add_node(PurpleBlistNode *node);
    -#endif
    +#endif /* FINCH_BLIST_H */
    +
    --- a/finch/gntconn.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntconn.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include "finch.h"
    --- a/finch/gntconn.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntconn.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_CONN_H
    -#define _GNT_CONN_H
    +#ifndef FINCH_CONN_H
    +#define FINCH_CONN_H
    +
    /**
    * SECTION:gntconn
    * @section_id: finch-gntconn
    @@ -57,4 +59,5 @@
    */
    void finch_connections_uninit(void);
    -#endif
    +#endif /* FINCH_CONN_H */
    +
    --- a/finch/gntconv.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntconv.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -711,7 +712,7 @@
    PurpleProtocol *protocol =
    gc ? purple_connection_get_protocol(gc) : NULL;
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) {
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info)) {
    item = gnt_menuitem_new(_("Get Info"));
    gnt_menu_add_item(GNT_MENU(sub), item);
    gnt_menuitem_set_callback(item, get_info_cb, ggc);
    @@ -802,7 +803,7 @@
    }
    static void
    -completion_cb(GntEntry *entry, const char *start, const char *end)
    +completion_cb(GntEntry *entry, const char *start, G_GNUC_UNUSED const char *end)
    {
    if (start == gnt_entry_get_text(entry) && *start != '/')
    gnt_widget_key_pressed(GNT_WIDGET(entry), ": ");
    @@ -1061,6 +1062,7 @@
    name, msgflags);
    gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(ggconv->tv), me ? " " : ": ", GNT_TEXT_FLAG_NORMAL);
    g_free(name);
    + g_free(msg_text);
    } else
    fl = GNT_TEXT_FLAG_DIM;
    @@ -1283,8 +1285,13 @@
    const GList *plugins = purple_plugins_get_loaded();
    if (plugins) {
    for (; plugins; plugins = plugins->next) {
    - PurplePluginInfo *plugin_info = purple_plugin_get_info(plugins->data);
    - str = g_string_append(str, purple_plugin_info_get_name(plugin_info));
    + GPluginPluginInfo *plugin_info =
    + GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(
    + plugins->data));
    + str = g_string_append(
    + str, gplugin_plugin_info_get_name(
    + plugin_info));
    if (plugins->next)
    str = g_string_append(str, ", ");
    --- a/finch/gntconv.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntconv.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_CONV_H
    -#define _GNT_CONV_H
    +#ifndef FINCH_CONV_H
    +#define FINCH_CONV_H
    +
    /**
    * SECTION:gntconv
    * @section_id: finch-gntconv
    @@ -45,11 +47,31 @@
    typedef struct _FinchConvChat FinchConvChat;
    typedef struct _FinchConvIm FinchConvIm;
    +/**
    + * FinchConversationFlag:
    + * @FINCH_CONV_NO_SOUND: A flag to mute a conversation.
    + *
    + * Flags that can be set for each conversation.
    + */
    typedef enum
    {
    FINCH_CONV_NO_SOUND = 1 << 0,
    } FinchConversationFlag;
    +/**
    + * FinchConv:
    + * @list: A list of conversations being displayed in this window.
    + * @active_conv: The active conversation.
    + * @window: The #GntWindow for the conversation.
    + * @entry: The #GntEntry for input.
    + * @tv: The #GntTextView that displays the history.
    + * @menu: The menu for the conversation.
    + * @info: The info widget that shows the information about the conversation.
    + * @plugins: The #GntMenuItem for plugins.
    + * @flags: The flags for the conversation.
    + *
    + * A Finch conversation.
    + */
    struct _FinchConv
    {
    GList *list;
    @@ -70,21 +92,38 @@
    } u;
    };
    +/**
    + * FinchConvChat:
    + * @userlist: The widget that displays the users in the chat.
    + *
    + * The chat specific implementation for a conversation.
    + */
    struct _FinchConvChat
    {
    GntWidget *userlist; /* the userlist */
    + /*< private >*/
    void *finch_reserved1;
    void *finch_reserved2;
    void *finch_reserved3;
    void *finch_reserved4;
    };
    +/**
    + * FinchConvIm:
    + * @sendto: The sendto widget which allows the user to select who they're
    + * messaging.
    + * @e2ee_menu: The end-to-end-encryption widget which lets the user configure
    + * the encryption.
    + *
    + * The instant message implementation for a conversation.
    + */
    struct _FinchConvIm
    {
    GntMenuItem *sendto;
    GntMenuItem *e2ee_menu;
    + /*< private >*/
    void *finch_reserved1;
    void *finch_reserved2;
    void *finch_reserved3;
    @@ -132,4 +171,5 @@
    */
    void finch_conversation_set_info_widget(PurpleConversation *conv, GntWidget *widget);
    -#endif
    +#endif /* FINCH_CONV_H */
    +
    --- a/finch/gntdebug.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntdebug.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    --- a/finch/gntdebug.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntdebug.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_DEBUG_H
    -#define _GNT_DEBUG_H
    +#ifndef FINCH_DEBUG_H
    +#define FINCH_DEBUG_H
    +
    /**
    * SECTION:gntdebug
    * @section_id: finch-gntdebug
    @@ -55,4 +57,5 @@
    G_END_DECLS
    -#endif
    +#endif /* FINCH_DEBUG_H */
    +
    --- a/finch/gntidle.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntidle.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * You should have received a copy of the GNU General Public License
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    */
    #include <internal.h>
    --- a/finch/gntidle.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntidle.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Pidgin is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_IDLE_H_
    -#define _GNT_IDLE_H_
    +#ifndef FINCH_IDLE_H
    +#define FINCH_IDLE_H
    +
    /**
    * SECTION:gntidle
    * @section_id: finch-gntidle
    @@ -43,4 +45,5 @@
    */
    PurpleIdleUiOps *finch_idle_get_ui_ops(void);
    -#endif /* _Finch_IDLE_H_ */
    +#endif /* FINCH_IDLE_H */
    +
    --- a/finch/gntlog.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntlog.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include "finch.h"
    @@ -43,7 +45,7 @@
    static void populate_log_tree(FinchLogViewer *lv);
    static FinchLogViewer *syslog_viewer = NULL;
    -struct log_viewer_hash_t {
    +struct log_viewer_hash {
    PurpleLogType type;
    char *username;
    PurpleAccount *account;
    @@ -52,7 +54,7 @@
    static guint log_viewer_hash(gconstpointer data)
    {
    - const struct log_viewer_hash_t *viewer = data;
    + const struct log_viewer_hash *viewer = data;
    if (viewer->contact != NULL)
    return g_direct_hash(viewer->contact);
    @@ -67,7 +69,7 @@
    static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
    {
    - const struct log_viewer_hash_t *a, *b;
    + const struct log_viewer_hash *a, *b;
    int ret;
    char *normal;
    @@ -147,7 +149,8 @@
    }
    -static void destroy_cb(GntWidget *w, struct log_viewer_hash_t *ht)
    +static void
    +destroy_cb(GntWidget *w, struct log_viewer_hash *ht)
    {
    FinchLogViewer *lv = syslog_viewer;
    @@ -162,8 +165,7 @@
    purple_request_close_with_handle(lv);
    - g_list_foreach(lv->logs, (GFunc)purple_log_free, NULL);
    - g_list_free(lv->logs);
    + g_list_free_full(lv->logs, (GDestroyNotify)purple_log_free);
    g_free(lv->search);
    g_free(lv);
    @@ -268,8 +270,9 @@
    }
    }
    -static FinchLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *logs,
    - const char *title, int log_size)
    +static FinchLogViewer *
    +display_log_viewer(struct log_viewer_hash *ht, GList *logs, const char *title,
    + int log_size)
    {
    FinchLogViewer *lv;
    char *text;
    @@ -342,7 +345,7 @@
    gnt_box_add_widget(GNT_BOX(vbox), hbox);
    /* Log size ************/
    if (log_size) {
    - char *sz_txt = purple_str_size_to_units(log_size);
    + char *sz_txt = g_format_size(log_size);
    text = g_strdup_printf("%s %s", _("Total log size:"), sz_txt);
    size_label = gnt_label_new(text);
    gnt_box_add_widget(GNT_BOX(hbox), size_label);
    @@ -375,7 +378,7 @@
    void finch_log_show(PurpleLogType type, const char *username, PurpleAccount *account)
    {
    - struct log_viewer_hash_t *ht;
    + struct log_viewer_hash *ht;
    FinchLogViewer *lv = NULL;
    const char *name = username;
    char *title;
    @@ -387,7 +390,7 @@
    g_return_if_fail(username != NULL);
    }
    - ht = g_new0(struct log_viewer_hash_t, 1);
    + ht = g_new0(struct log_viewer_hash, 1);
    ht->type = type;
    ht->username = g_strdup(username);
    @@ -442,7 +445,7 @@
    void finch_log_show_contact(PurpleContact *contact)
    {
    - struct log_viewer_hash_t *ht;
    + struct log_viewer_hash *ht;
    PurpleBlistNode *child;
    FinchLogViewer *lv = NULL;
    GList *logs = NULL;
    @@ -452,7 +455,7 @@
    g_return_if_fail(contact != NULL);
    - ht = g_new0(struct log_viewer_hash_t, 1);
    + ht = g_new0(struct log_viewer_hash, 1);
    ht->type = PURPLE_LOG_IM;
    ht->contact = contact;
    @@ -488,8 +491,9 @@
    * There is probably a better way to deal with this. */
    if (name == NULL) {
    child = purple_blist_node_get_first_child((PurpleBlistNode*)contact);
    - if (child != NULL && PURPLE_IS_BUDDY(child))
    + if (PURPLE_IS_BUDDY(child)) {
    name = purple_buddy_get_contact_alias((PurpleBuddy *)child);
    + }
    if (name == NULL)
    name = "";
    }
    --- a/finch/gntlog.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntlog.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FINCHLOG_H_
    -#define _FINCHLOG_H_
    +#ifndef FINCH_LOG_H
    +#define FINCH_LOG_H
    +
    /**
    * SECTION:gntlog
    * @section_id: finch-gntlog
    @@ -41,6 +43,7 @@
    * @tree: The tree representing said treestore
    * @text: The text to display said logs
    * @entry: The search entry, in which search terms are entered
    + * @label: The label for the log viewer
    * @flags: The most recently used log flags
    * @search: The string currently being searched for
    *
    @@ -92,4 +95,5 @@
    */
    void finch_log_uninit(void);
    -#endif
    +#endif /* FINCH_LOG_H */
    +
    --- a/finch/gntmedia.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntmedia.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    --- a/finch/gntmedia.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntmedia.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef GNT_MEDIA_H
    -#define GNT_MEDIA_H
    +#ifndef FINCH_MEDIA_H
    +#define FINCH_MEDIA_H
    +
    /**
    * SECTION:gntmedia
    * @section_id: finch-gntmedia
    @@ -39,5 +41,5 @@
    G_END_DECLS
    -#endif /* GNT_MEDIA_H */
    +#endif /* FINCH_MEDIA_H */
    --- a/finch/gntmenuutil.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntmenuutil.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    --- a/finch/gntmenuutil.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntmenuutil.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_MENUUTIL_H
    -#define _GNT_MENUUTIL_H
    +#ifndef FINCH_MENUUTIL_H
    +#define FINCH_MENUUTIL_H
    +
    /**
    * SECTION:gntmenuutil
    * @section_id: finch-gntmenuutil
    @@ -48,4 +50,5 @@
    */
    void finch_append_menu_action(GntMenu *menu, PurpleActionMenu *action, gpointer ctx);
    -#endif
    +#endif /* FINCH_MENUUTIL_H */
    +
    --- a/finch/gntnotify.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntnotify.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include <gnt.h>
    @@ -209,24 +211,24 @@
    const char **urls)
    {
    PurpleAccount *account = purple_connection_get_account(gc);
    - GString *message = g_string_new(NULL);
    - void *ret;
    + void *ret = NULL;
    static int key = 0;
    if (count == 0)
    return NULL;
    - if (!detailed)
    - {
    - g_string_append_printf(message,
    - ngettext("%s (%s) has %d new message.",
    - "%s (%s) has %d new messages.",
    - (int)count),
    - tos ? *tos : purple_account_get_username(account),
    - purple_account_get_protocol_name(account), (int)count);
    - }
    - else
    - {
    + if (!detailed) {
    + gchar *message;
    + message = g_strdup_printf(
    + ngettext("%s (%s) has %d new message.",
    + "%s (%s) has %d new messages.", (int)count),
    + tos ? *tos : purple_account_get_username(account),
    + purple_account_get_protocol_name(account), (int)count);
    + ret = finch_notify_common(PURPLE_NOTIFY_EMAIL, PURPLE_NOTIFY_MSG_INFO,
    + _("New Mail"), _("You have mail!"), message,
    + NULL);
    + g_free(message);
    + } else {
    char *to;
    gboolean newwin = (emaildialog.window == NULL);
    @@ -245,12 +247,9 @@
    gnt_widget_show(emaildialog.window);
    else
    gnt_window_present(emaildialog.window);
    - return NULL;
    + ret = NULL;
    }
    - ret = finch_notify_common(PURPLE_NOTIFY_EMAIL, PURPLE_NOTIFY_MSG_INFO,
    - _("New Mail"), _("You have mail!"), message->str, NULL);
    - g_string_free(message, TRUE);
    return ret;
    }
    @@ -376,8 +375,7 @@
    list = gnt_tree_get_selection_text_list(GNT_TREE(g_object_get_data(G_OBJECT(widget), "notify-tree")));
    b->callback(purple_account_get_connection(account), list, data);
    - g_list_foreach(list, (GFunc)g_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_free);
    }
    static void
    --- a/finch/gntnotify.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntnotify.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_NOTIFY_H
    -#define _GNT_NOTIFY_H
    +#ifndef FINCH_NOTIFY_H
    +#define FINCH_NOTIFY_H
    +
    /**
    * SECTION:gntnotify
    * @section_id: finch-gntnotify
    @@ -57,5 +59,5 @@
    */
    void finch_notify_uninit(void);
    -#endif
    +#endif /* FINCH_NOTIFY_H */
    --- a/finch/gntplugin.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntplugin.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include <gnt.h>
    @@ -158,8 +160,7 @@
    static void
    free_stringlist(GList *list)
    {
    - g_list_foreach(list, (GFunc)g_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_free);
    }
    static gboolean
    @@ -264,7 +265,8 @@
    selection_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null)
    {
    PurplePlugin *plugin = current;
    - PurplePluginInfo *info;
    + const gchar *filename;
    + GPluginPluginInfo *info;
    char *text, *authors = NULL;
    const char * const *authorlist;
    GList *list = NULL, *iter = NULL;
    @@ -272,8 +274,9 @@
    if (!plugin)
    return;
    - info = purple_plugin_get_info(plugin);
    - authorlist = purple_plugin_info_get_authors(info);
    + filename = gplugin_plugin_get_filename(GPLUGIN_PLUGIN(plugin));
    + info = GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin));
    + authorlist = gplugin_plugin_info_get_authors(info);
    if (authorlist)
    authors = g_strjoinv(", ", (gchar **)authorlist);
    @@ -284,25 +287,27 @@
    * quickly find the plugin in the list.
    * I probably mean 'plugin developers' by 'users' here. */
    list = g_object_get_data(G_OBJECT(widget), "seen-list");
    - if (list)
    - iter = g_list_find_custom(list, purple_plugin_get_filename(plugin),
    - (GCompareFunc)strcmp);
    + if (list) {
    + iter = g_list_find_custom(list, filename, (GCompareFunc)strcmp);
    + }
    if (!iter) {
    - list = g_list_prepend(list, g_strdup(purple_plugin_get_filename(plugin)));
    + list = g_list_prepend(list, g_strdup(filename));
    g_object_set_data(G_OBJECT(widget), "seen-list", list);
    }
    /* XXX: Use formatting and stuff */
    gnt_text_view_clear(GNT_TEXT_VIEW(plugins.aboot));
    - text = g_strdup_printf((g_strv_length((gchar **)authorlist) > 1 ?
    - _("Name: %s\nVersion: %s\nDescription: %s\nAuthors: %s\nWebsite: %s\nFilename: %s\n") :
    - _("Name: %s\nVersion: %s\nDescription: %s\nAuthor: %s\nWebsite: %s\nFilename: %s\n")),
    - SAFE(_(purple_plugin_info_get_name(info))),
    - SAFE(_(purple_plugin_info_get_version(info))),
    - SAFE(_(purple_plugin_info_get_description(info))),
    - SAFE(authors),
    - SAFE(_(purple_plugin_info_get_website(info))),
    - SAFE(purple_plugin_get_filename(plugin)));
    + text = g_strdup_printf(
    + (authorlist && g_strv_length((gchar **)authorlist) > 1
    + ? _("Name: %s\nVersion: %s\nDescription: %s\nAuthors: "
    + "%s\nWebsite: %s\nFilename: %s\n")
    + : _("Name: %s\nVersion: %s\nDescription: %s\nAuthor: "
    + "%s\nWebsite: %s\nFilename: %s\n")),
    + SAFE(_(gplugin_plugin_info_get_name(info))),
    + SAFE(_(gplugin_plugin_info_get_version(info))),
    + SAFE(_(gplugin_plugin_info_get_description(info))),
    + SAFE(authors), SAFE(_(gplugin_plugin_info_get_website(info))),
    + SAFE(filename));
    gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(plugins.aboot),
    text, GNT_TEXT_FLAG_NORMAL);
    @@ -319,8 +324,7 @@
    {
    GList *list = g_object_get_data(G_OBJECT(plugins.tree), "seen-list");
    purple_prefs_set_path_list("/finch/plugins/seen", list);
    - g_list_foreach(list, (GFunc)g_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_free);
    plugins.window = NULL;
    plugins.tree = NULL;
    @@ -330,8 +334,14 @@
    static int
    plugin_compare(PurplePlugin *p1, PurplePlugin *p2)
    {
    - char *s1 = g_utf8_strup(purple_plugin_info_get_name(purple_plugin_get_info(p1)), -1);
    - char *s2 = g_utf8_strup(purple_plugin_info_get_name(purple_plugin_get_info(p2)), -1);
    + char *s1 =
    + g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(p1))),
    + -1);
    + char *s2 =
    + g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(p2))),
    + -1);
    int ret = g_utf8_collate(s1, s2);
    g_free(s1);
    g_free(s2);
    @@ -385,7 +395,8 @@
    gnt_box_set_toplevel(GNT_BOX(window), TRUE);
    gnt_box_set_title(GNT_BOX(window),
    - purple_plugin_info_get_name(info));
    + gplugin_plugin_info_get_name(
    + GPLUGIN_PLUGIN_INFO(info)));
    gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
    box = priv->pref_frame_cb();
    @@ -483,14 +494,19 @@
    if (purple_plugin_is_internal(plug))
    continue;
    - gnt_tree_add_choice(GNT_TREE(tree), plug,
    - gnt_tree_create_row(GNT_TREE(tree),
    - purple_plugin_info_get_name(purple_plugin_get_info(plug))),
    - NULL, NULL);
    + gnt_tree_add_choice(
    + GNT_TREE(tree), plug,
    + gnt_tree_create_row(
    + GNT_TREE(tree),
    + gplugin_plugin_info_get_name(
    + GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(plug)))),
    + NULL, NULL);
    gnt_tree_set_choice(GNT_TREE(tree), plug, purple_plugin_is_loaded(plug));
    - if (!g_list_find_custom(seen, purple_plugin_get_filename(plug),
    - (GCompareFunc)strcmp))
    + if (!g_list_find_custom(seen, gplugin_plugin_get_filename(plug),
    + (GCompareFunc)strcmp)) {
    gnt_tree_set_row_flags(GNT_TREE(tree), plug, GNT_TEXT_FLAG_BOLD);
    + }
    }
    g_list_free(plugin_list);
    --- a/finch/gntplugin.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntplugin.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_PLUGIN_H
    -#define _GNT_PLUGIN_H
    +#ifndef FINCH_PLUGIN_H
    +#define FINCH_PLUGIN_H
    +
    /**
    * SECTION:gntplugin
    * @section_id: finch-gntplugin
    @@ -125,4 +127,5 @@
    */
    void finch_plugins_save_loaded(void);
    -#endif
    +#endif /* FINCH_PLUGIN_H */
    +
    --- a/finch/gntpounce.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntpounce.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -17,8 +18,8 @@
    * You should have received a copy of the GNU General Public License
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    */
    +
    #include <internal.h>
    #include NCURSES_HEADER
    @@ -168,7 +169,7 @@
    static void
    setup_buddy_list_suggestion(GntEntry *entry, gboolean offline)
    {
    - PurpleBlistNode *node = purple_blist_get_root();
    + PurpleBlistNode *node = purple_blist_get_default_root();
    for (; node; node = purple_blist_node_next(node, offline)) {
    if (!PURPLE_IS_BUDDY(node))
    continue;
    --- a/finch/gntpounce.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntpounce.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FINCHPOUNCE_H_
    -#define _FINCHPOUNCE_H_
    +#ifndef FINCH_POUNCE_H
    +#define FINCH_POUNCE_H
    +
    /**
    * SECTION:gntpounce
    * @section_id: finch-gntpounce
    @@ -78,4 +80,5 @@
    */
    void finch_pounces_uninit(void);
    -#endif /* _PURPLE_FINCHPOUNCE_H_ */
    +#endif /* FINCH_POUNCE_H */
    +
    --- a/finch/gntprefs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntprefs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include "finch.h"
    #include <internal.h>
    @@ -211,8 +213,7 @@
    static void
    free_strings(void)
    {
    - g_list_foreach(pref_request.freestrings, (GFunc)g_free, NULL);
    - g_list_free(pref_request.freestrings);
    + g_list_free_full(pref_request.freestrings, g_free);
    pref_request.freestrings = NULL;
    pref_request.showing = FALSE;
    }
    --- a/finch/gntprefs.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntprefs.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_PREFS_H
    -#define _GNT_PREFS_H
    +#ifndef FINCH_PREFS_H
    +#define FINCH_PREFS_H
    +
    /**
    * SECTION:gntprefs
    * @section_id: finch-gntprefs
    @@ -60,4 +62,5 @@
    */
    void finch_prefs_update_old(void);
    -#endif
    +#endif /* FINCH_PREFS_H */
    +
    --- a/finch/gntrequest.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntrequest.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include <gnt.h>
    @@ -432,7 +434,7 @@
    gnt_entry_set_masked(GNT_ENTRY(entry),
    purple_request_field_string_is_masked(field));
    if (hint && purple_str_has_prefix(hint, "screenname")) {
    - PurpleBlistNode *node = purple_blist_get_root();
    + PurpleBlistNode *node = purple_blist_get_default_root();
    gboolean offline = purple_str_has_suffix(hint, "all");
    for (; node; node = purple_blist_node_next(node, offline)) {
    if (!PURPLE_IS_BUDDY(node))
    @@ -444,8 +446,8 @@
    *username = entry;
    } else if (purple_strequal(hint, "group")) {
    PurpleBlistNode *node;
    - for (node = purple_blist_get_root(); node;
    - node = purple_blist_node_get_sibling_next(node)) {
    + for (node = purple_blist_get_default_root(); node;
    + node = purple_blist_node_get_sibling_next(node)) {
    if (PURPLE_IS_GROUP(node))
    gnt_entry_add_suggest(GNT_ENTRY(entry), purple_group_get_name((PurpleGroup *)node));
    }
    @@ -858,7 +860,7 @@
    {
    }
    -void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *allfields)
    +void finch_request_save_in_prefs(gpointer data, PurpleRequestFields *allfields)
    {
    GList *list;
    for (list = purple_request_fields_get_groups(allfields); list; list = list->next) {
    --- a/finch/gntrequest.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntrequest.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_REQUEST_H
    -#define _GNT_REQUEST_H
    +#ifndef FINCH_REQUEST_H
    +#define FINCH_REQUEST_H
    +
    /**
    * SECTION:gntrequest
    * @section_id: finch-gntrequest
    @@ -60,11 +62,13 @@
    /**
    * finch_request_save_in_prefs:
    + * @data: No longer used, set to %NULL.
    + * @fields: The #PurpleRequestFields to save.
    *
    * Save the request fields in preferences where the id attribute of each field is the
    * id of a preference.
    */
    -void finch_request_save_in_prefs(gpointer null, PurpleRequestFields *fields);
    +void finch_request_save_in_prefs(gpointer data, PurpleRequestFields *fields);
    /**
    * finch_request_field_get_widget:
    @@ -76,4 +80,5 @@
    */
    GntWidget *finch_request_field_get_widget(PurpleRequestField *field);
    -#endif
    +#endif /* FINCH_REQUEST_H */
    +
    --- a/finch/gntroomlist.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntroomlist.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -115,10 +116,11 @@
    protocol = purple_connection_get_protocol(gc);
    - if(protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, room_serialize))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, room_serialize)) {
    name = purple_protocol_roomlist_iface_room_serialize(protocol, room);
    - else
    + } else {
    name = g_strdup(purple_roomlist_room_get_name(room));
    + }
    purple_blist_request_add_chat(froomlist.account, NULL, NULL, name);
    @@ -237,7 +239,7 @@
    protocol = purple_connection_get_protocol(gc);
    if (PURPLE_CONNECTION_IS_CONNECTED(gc) &&
    - PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, get_list)) {
    + PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, get_list)) {
    PurpleAccount *account = purple_connection_get_account(gc);
    char *text = g_strdup_printf("%s (%s)",
    purple_account_get_username(account),
    --- a/finch/gntroomlist.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntroomlist.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_ROOMLIST_H
    -#define _GNT_ROOMLIST_H
    +#ifndef FINCH_ROOMLIST_H
    +#define FINCH_ROOMLIST_H
    +
    /**
    * SECTION:gntroomlist
    * @section_id: finch-gntroomlist
    @@ -64,5 +66,5 @@
    */
    void finch_roomlist_uninit(void);
    -#endif
    +#endif /* FINCH_ROOMLIST_H */
    --- a/finch/gntsound.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntsound.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include "finch.h"
    #include <internal.h>
    --- a/finch/gntsound.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntsound.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_SOUND_H
    -#define _GNT_SOUND_H
    +#ifndef FINCH_SOUND_H
    +#define FINCH_SOUND_H
    +
    /**
    * SECTION:gntsound
    * @section_id: finch-gntsound
    @@ -88,4 +90,5 @@
    */
    void finch_sounds_show_all(void);
    -#endif
    +#endif /* FINCH_SOUND_H */
    +
    --- a/finch/gntstatus.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntstatus.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include <gnt.h>
    --- a/finch/gntstatus.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntstatus.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_STATUS_H
    -#define _GNT_STATUS_H
    +#ifndef FINCH_STATUS_H
    +#define FINCH_STATUS_H
    +
    /**
    * SECTION:gntstatus
    * @section_id: finch-gntstatus
    @@ -50,4 +52,5 @@
    */
    void finch_savedstatus_edit(PurpleSavedStatus *saved);
    -#endif
    +#endif /* FINCH_STATUS_H */
    +
    --- a/finch/gntui.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntui.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,4 @@
    -/**
    +/*
    * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    @@ -19,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include "finch.h"
    #include <internal.h>
    @@ -65,7 +66,7 @@
    /* Initialize the buddy list */
    finch_blist_init();
    - purple_blist_set_ui_ops(finch_blist_get_ui_ops());
    + purple_blist_set_ui(FINCH_TYPE_BUDDY_LIST);
    /* Initialize sound */
    purple_sound_set_ui_ops(finch_sound_get_ui_ops());
    @@ -122,7 +123,7 @@
    purple_connections_set_ui_ops(NULL);
    finch_connections_uninit();
    - purple_blist_set_ui_ops(NULL);
    + purple_blist_set_ui(G_TYPE_INVALID);
    finch_blist_uninit();
    purple_conversations_set_ui_ops(NULL);
    --- a/finch/gntui.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntui.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_UI_H
    -#define _GNT_UI_H
    +#ifndef FINCH_UI_H
    +#define FINCH_UI_H
    +
    /**
    * SECTION:gntui
    * @section_id: finch-gntui
    @@ -34,4 +35,5 @@
    void finch_ui_init(void);
    void finch_ui_uninit(void);
    -#endif
    +#endif /* FINCH_UI_H */
    +
    --- a/finch/gntxfer.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntxfer.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -18,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include "finch.h"
    @@ -303,8 +305,8 @@
    type = purple_xfer_get_xfer_type(xfer);
    - size_str = purple_str_size_to_units(purple_xfer_get_size(xfer));
    - remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer));
    + size_str = g_format_size(purple_xfer_get_size(xfer));
    + remaining_str = g_format_size(purple_xfer_get_bytes_remaining(xfer));
    lfilename = g_path_get_basename(purple_xfer_get_local_filename(xfer));
    utf8 = g_filename_to_utf8(lfilename, -1, NULL, NULL, NULL);
    @@ -394,16 +396,20 @@
    char prog_str[5];
    double kb_sent;
    double kbps = 0.0;
    - time_t elapsed, now;
    + gint64 now;
    + gint64 elapsed = 0;
    char *kbsec;
    gboolean send;
    - if ((now = purple_xfer_get_end_time(xfer)) == 0)
    - now = time(NULL);
    + if (purple_xfer_get_start_time(xfer) > 0) {
    + if ((now = purple_xfer_get_end_time(xfer)) == 0) {
    + now = g_get_monotonic_time();
    + }
    + elapsed = now - purple_xfer_get_start_time(xfer);
    + }
    - kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0;
    - elapsed = (purple_xfer_get_start_time(xfer) > 0 ? now - purple_xfer_get_start_time(xfer) : 0);
    - kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0);
    + kb_sent = purple_xfer_get_bytes_sent(xfer) / 1000.0;
    + kbps = (elapsed > 0 ? (kb_sent * G_USEC_PER_SEC) / elapsed : 0);
    g_return_if_fail(xfer_dialog != NULL);
    g_return_if_fail(xfer != NULL);
    @@ -423,9 +429,9 @@
    data->last_updated_time = current_time;
    send = (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND);
    - size_str = purple_str_size_to_units(purple_xfer_get_size(xfer));
    - remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer));
    - kbsec = g_strdup_printf(_("%.2f KiB/s"), kbps);
    + size_str = g_format_size(purple_xfer_get_size(xfer));
    + remaining_str = g_format_size(purple_xfer_get_bytes_remaining(xfer));
    + kbsec = g_strdup_printf(_("%.2f KB/s"), kbps);
    gnt_tree_change_text(GNT_TREE(xfer_dialog->tree), xfer, COLUMN_PROGRESS,
    g_ascii_dtostr(prog_str, sizeof(prog_str), purple_xfer_get_progress(xfer) * 100.));
    --- a/finch/gntxfer.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/gntxfer.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* finch
    +/*
    + * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,8 +20,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GNT_XFER_H_
    -#define _GNT_XFER_H_
    +#ifndef FINCH_XFER_H
    +#define FINCH_XFER_H
    +
    /**
    * SECTION:gntxfer
    * @section_id: finch-gntxfer
    @@ -123,4 +125,5 @@
    */
    PurpleXferUiOps *finch_xfers_get_ui_ops(void);
    -#endif /* _GNT_XFER_H_ */
    +#endif /* FINCH_XFER_H */
    +
    --- a/finch/libfinch.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/libfinch.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,4 @@
    -/**
    +/*
    * finch
    *
    * Finch is the legal property of its developers, whose names are too numerous
    @@ -19,6 +19,7 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    +
    #include <internal.h>
    #include <locale.h>
    #include "finch.h"
    @@ -230,7 +231,7 @@
    abort();
    }
    - path = g_build_filename(purple_user_dir(), "plugins", NULL);
    + path = g_build_filename(purple_data_dir(), "plugins", NULL);
    if (g_mkdir(path, S_IRUSR | S_IWUSR | S_IXUSR) != 0 && errno != EEXIST)
    fprintf(stderr, "Couldn't create plugins dir\n");
    purple_plugins_add_search_path(path);
    --- a/finch/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -6,7 +6,7 @@
    else
    libgnt_proj = subproject('libgnt',
    default_options : [
    - 'introspection=' + get_option('introspection').to_string(),
    + 'introspection=' + enable_introspection.to_string(),
    ]
    )
    libgnt_dep = libgnt_proj.get_variable('libgnt_dep')
    @@ -196,7 +196,7 @@
    gnome.generate_gir(libfinch,
    sources : introspection_sources,
    - includes : [libgnt_gir[0], libpurple_gir[0]],
    + includes : [libgnt_gir[0], libpurple_gir[0], gplugin_gir],
    namespace : 'Finch',
    symbol_prefix : 'finch',
    identifier_prefix : 'Finch',
    --- a/finch/plugins/gntclipboard.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/plugins/gntclipboard.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file gntclipboard.c
    - *
    * Copyright (C) 2007 Richard Nelson <wabz@whatsbeef.net>
    *
    * This program is free software; you can redistribute it and/or modify
    @@ -18,7 +16,6 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -
    #include "internal.h"
    #include <glib.h>
    @@ -102,7 +99,7 @@
    if (child) {
    kill(child, SIGTERM);
    }
    - if ((child = fork() == 0)) {
    + if ((child = fork()) == 0) {
    set_clip(string);
    _exit(0);
    }
    --- a/finch/plugins/gntgf.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/plugins/gntgf.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file gntgf.c Minimal toaster plugin in Gnt.
    - *
    * Copyright (C) 2006 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
    *
    * This program is free software; you can redistribute it and/or modify
    @@ -18,7 +16,6 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -
    #include "internal.h"
    #include NCURSES_HEADER
    --- a/finch/plugins/gnthistory.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/plugins/gnthistory.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file gnthistory.c Show log from previous conversation
    - *
    * Copyright (C) 2006 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
    *
    * This program is free software; you can redistribute it and/or modify
    @@ -129,8 +127,7 @@
    purple_conversation_write_system_message(c, "<hr>", mflag);
    - g_list_foreach(logs, (GFunc)purple_log_free, NULL);
    - g_list_free(logs);
    + g_list_free_full(logs, (GDestroyNotify)purple_log_free);
    }
    static void
    --- a/finch/plugins/gnttinyurl.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/plugins/gnttinyurl.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file gnttinyurl.c
    - *
    * Copyright (C) 2009 Richard Nelson <wabz@whatsbeef.net>
    *
    * This program is free software; you can redistribute it and/or modify
    @@ -18,7 +16,6 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -
    #include "internal.h"
    #include <glib.h>
    @@ -27,7 +24,6 @@
    #define PREF_LENGTH PREFS_BASE "/length"
    #define PREF_URL PREFS_BASE "/url"
    -
    #include <conversation.h>
    #include <http.h>
    #include <signals.h>
    @@ -241,11 +237,6 @@
    purple_debug_info("TinyURL", "Conversation no longer exists... :(\n");
    }
    -static void free_urls(gpointer data, gpointer null)
    -{
    - g_free(data);
    -}
    -
    static gboolean writing_msg(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
    {
    GString *t;
    @@ -256,9 +247,7 @@
    return FALSE;
    urls = g_object_get_data(G_OBJECT(conv), "TinyURLs");
    - if (urls != NULL) /* message was cancelled somewhere? Reset. */
    - g_list_foreach(urls, free_urls, NULL);
    - g_list_free(urls);
    + g_list_free_full(urls, g_free);
    urls = extract_urls(purple_message_get_contents(msg));
    if (!urls)
    return FALSE;
    @@ -322,7 +311,7 @@
    for (iter = urls, c = 1; iter; iter = iter->next, c++) {
    int i;
    CbInfo *cbdata;
    - gchar *url, *str;
    + gchar *url;
    gchar *original_url;
    const gchar *tiny_url;
    @@ -351,9 +340,8 @@
    url = g_strdup_printf("%s%s", purple_prefs_get_string(PREF_URL), purple_url_encode(original_url));
    }
    purple_http_get(NULL, url_fetched, cbdata, url);
    - str = g_strdup_printf(_("\nFetching TinyURL..."));
    - gnt_text_view_append_text_with_tag((tv), str, GNT_TEXT_FLAG_DIM, cbdata->tag);
    - g_free(str);
    + gnt_text_view_append_text_with_tag((tv), _("\nFetching TinyURL..."),
    + GNT_TEXT_FLAG_DIM, cbdata->tag);
    if (i == 0)
    gnt_text_view_scroll(tv, 0);
    g_free(iter->data);
    @@ -366,9 +354,7 @@
    free_conv_urls(PurpleConversation *conv)
    {
    GList *urls = g_object_get_data(G_OBJECT(conv), "TinyURLs");
    - if (urls)
    - g_list_foreach(urls, free_urls, NULL);
    - g_list_free(urls);
    + g_list_free_full(urls, g_free);
    }
    static void
    --- a/finch/plugins/grouping.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/plugins/grouping.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file grouping.c Provides different grouping options.
    - *
    * Copyright (C) 2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
    *
    * This program is free software; you can redistribute it and/or modify
    @@ -43,18 +41,23 @@
    * GObject code
    */
    static void
    -finch_grouping_node_init(FinchGroupingNode *node)
    +finch_grouping_node_init(G_GNUC_UNUSED FinchGroupingNode *node)
    {
    }
    static void
    -finch_grouping_node_class_init(FinchGroupingNodeClass *klass)
    +finch_grouping_node_class_init(G_GNUC_UNUSED FinchGroupingNodeClass *klass)
    +{
    +}
    +
    +static void
    +finch_grouping_node_class_finalize(G_GNUC_UNUSED FinchGroupingNodeClass *klass)
    {
    }
    GType finch_grouping_node_get_type(void);
    -PURPLE_DEFINE_TYPE(FinchGroupingNode, finch_grouping_node,
    - PURPLE_TYPE_BLIST_NODE);
    +G_DEFINE_DYNAMIC_TYPE(FinchGroupingNode, finch_grouping_node,
    + PURPLE_TYPE_BLIST_NODE);
    /**
    * Online/Offline
    @@ -292,8 +295,9 @@
    nested_group_create_tooltip(gpointer selected_row, GString **body, char **title)
    {
    PurpleBlistNode *node = selected_row;
    - if (!node || !FINCH_IS_GROUPING_NODE(node))
    + if (!FINCH_IS_GROUPING_NODE(node)) {
    return default_manager->create_tooltip(selected_row, body, title);
    + }
    if (body)
    *body = g_string_new(_("Nested Subgroup")); /* Perhaps list the child groups/subgroups? */
    return TRUE;
    @@ -312,7 +316,7 @@
    return TRUE;
    len = strlen(purple_group_get_name(PURPLE_GROUP(node)));
    - group = purple_blist_get_root();
    + group = purple_blist_get_default_root();
    for (; group; group = purple_blist_node_get_sibling_next(group)) {
    if (group == node)
    continue;
    @@ -360,7 +364,7 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - finch_grouping_node_register_type(plugin);
    + finch_grouping_node_register_type(G_TYPE_MODULE(plugin));
    default_manager = finch_blist_manager_find("default");
    --- a/finch/plugins/lastlog.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/plugins/lastlog.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file lastlog.c Lastlog plugin for purple-text.
    - *
    * Copyright (C) 2006 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
    *
    * This program is free software; you can redistribute it and/or modify
    @@ -18,7 +16,6 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -
    #define PLUGIN_STATIC_NAME GntLastlog
    #include "internal.h"
    --- a/finch/plugins/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/finch/plugins/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -1,5 +1,5 @@
    if PLUGINS
    - if with_x
    + if x11.found()
    gntclipboard = library('gntclipboard', 'gntclipboard.c',
    dependencies : [x11, libpurple_dep, libfinch_dep, glib],
    name_prefix : '',
    --- a/libpurple/account.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/account.c Tue Oct 08 21:48:28 2019 -0500
    @@ -33,6 +33,16 @@
    #include "signals.h"
    #include "util.h"
    +/**
    + * PurpleAccount:
    + *
    + * Structure representing an account.
    + */
    +struct _PurpleAccount
    +{
    + GObject gparent;
    +};
    +
    typedef struct
    {
    char *username; /* The username. */
    @@ -1539,7 +1549,6 @@
    void
    purple_account_set_username(PurpleAccount *account, const char *username)
    {
    - PurpleBlistUiOps *blist_ops;
    PurpleAccountPrivate *priv;
    g_return_if_fail(PURPLE_IS_ACCOUNT(account));
    @@ -1555,9 +1564,7 @@
    /* if the name changes, we should re-write the buddy list
    * to disk with the new name */
    - blist_ops = purple_blist_get_ui_ops();
    - if (blist_ops != NULL && blist_ops->save_account != NULL)
    - blist_ops->save_account(account);
    + purple_blist_save_account(purple_blist_get_default(), account);
    }
    void
    @@ -1782,11 +1789,8 @@
    priv = purple_account_get_instance_private(account);
    /* Out with the old... */
    - if (priv->status_types != NULL)
    - {
    - g_list_foreach(priv->status_types, (GFunc)purple_status_type_destroy, NULL);
    - g_list_free(priv->status_types);
    - }
    + g_list_free_full(priv->status_types,
    + (GDestroyNotify)purple_status_type_destroy);
    /* In with the new... */
    priv->status_types = status_types;
    @@ -1856,7 +1860,7 @@
    gc = purple_account_get_connection(account);
    protocol = purple_connection_get_protocol(gc);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, set_public_alias))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, set_public_alias))
    purple_protocol_server_iface_set_public_alias(protocol, gc, alias, success_cb, failure_cb);
    else if (failure_cb) {
    struct public_alias_closure *closure =
    @@ -1881,7 +1885,7 @@
    gc = purple_account_get_connection(account);
    protocol = purple_connection_get_protocol(gc);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_public_alias))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_public_alias))
    purple_protocol_server_iface_get_public_alias(protocol, gc, success_cb, failure_cb);
    else if (failure_cb) {
    struct public_alias_closure *closure =
    @@ -2069,22 +2073,6 @@
    purple_accounts_schedule_save();
    }
    -void
    -purple_account_set_ui_data(PurpleAccount *account, gpointer ui_data)
    -{
    - g_return_if_fail(PURPLE_IS_ACCOUNT(account));
    -
    - account->ui_data = ui_data;
    -}
    -
    -gpointer
    -purple_account_get_ui_data(PurpleAccount *account)
    -{
    - g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
    -
    - return account->ui_data;
    -}
    -
    gboolean
    purple_account_is_connected(PurpleAccount *account)
    {
    @@ -2306,7 +2294,6 @@
    GSList *l;
    char *name;
    PurpleBuddy *buddy;
    - PurpleBlistUiOps *blist_ops;
    PurpleAccountPrivate *priv;
    PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
    @@ -2337,9 +2324,7 @@
    if (ui_ops != NULL && ui_ops->permit_added != NULL)
    ui_ops->permit_added(account, who);
    - blist_ops = purple_blist_get_ui_ops();
    - if (blist_ops != NULL && blist_ops->save_account != NULL)
    - blist_ops->save_account(account);
    + purple_blist_save_account(purple_blist_get_default(), account);
    /* This lets the UI know a buddy has had its privacy setting changed */
    buddy = purple_blist_find_buddy(account, name);
    @@ -2358,7 +2343,6 @@
    const char *name;
    PurpleBuddy *buddy;
    char *del;
    - PurpleBlistUiOps *blist_ops;
    PurpleAccountPrivate *priv;
    PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
    @@ -2390,9 +2374,7 @@
    if (ui_ops != NULL && ui_ops->permit_removed != NULL)
    ui_ops->permit_removed(account, who);
    - blist_ops = purple_blist_get_ui_ops();
    - if (blist_ops != NULL && blist_ops->save_account != NULL)
    - blist_ops->save_account(account);
    + purple_blist_save_account(purple_blist_get_default(), account);
    buddy = purple_blist_find_buddy(account, name);
    if (buddy != NULL) {
    @@ -2410,7 +2392,6 @@
    GSList *l;
    char *name;
    PurpleBuddy *buddy;
    - PurpleBlistUiOps *blist_ops;
    PurpleAccountPrivate *priv;
    PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
    @@ -2441,9 +2422,7 @@
    if (ui_ops != NULL && ui_ops->deny_added != NULL)
    ui_ops->deny_added(account, who);
    - blist_ops = purple_blist_get_ui_ops();
    - if (blist_ops != NULL && blist_ops->save_account != NULL)
    - blist_ops->save_account(account);
    + purple_blist_save_account(purple_blist_get_default(), account);
    buddy = purple_blist_find_buddy(account, name);
    if (buddy != NULL) {
    @@ -2461,7 +2440,6 @@
    const char *normalized;
    char *name;
    PurpleBuddy *buddy;
    - PurpleBlistUiOps *blist_ops;
    PurpleAccountPrivate *priv;
    PurpleAccountUiOps *ui_ops = purple_accounts_get_ui_ops();
    @@ -2499,9 +2477,7 @@
    g_free(name);
    - blist_ops = purple_blist_get_ui_ops();
    - if (blist_ops != NULL && blist_ops->save_account != NULL)
    - blist_ops->save_account(account);
    + purple_blist_save_account(purple_blist_get_default(), account);
    return TRUE;
    }
    @@ -2601,20 +2577,22 @@
    GSList *
    purple_account_privacy_get_permitted(PurpleAccount *account)
    {
    - PurpleAccountPrivate *priv = purple_account_get_instance_private(account);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleAccountPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
    +
    + priv = purple_account_get_instance_private(account);
    return priv->permit;
    }
    GSList *
    purple_account_privacy_get_denied(PurpleAccount *account)
    {
    - PurpleAccountPrivate *priv = purple_account_get_instance_private(account);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleAccountPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_ACCOUNT(account), NULL);
    +
    + priv = purple_account_get_instance_private(account);
    return priv->deny;
    }
    @@ -2975,9 +2953,9 @@
    groups = g_list_append(groups, purple_buddy_get_group(buddy));
    }
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddies))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, add_buddies))
    purple_protocol_server_iface_add_buddies(protocol, gc, buddies, groups, message);
    - else if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddy)) {
    + else if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, add_buddy)) {
    GList *curb = buddies, *curg = groups;
    while ((curb != NULL) && (curg != NULL)) {
    @@ -3015,7 +2993,7 @@
    protocol = purple_connection_get_protocol(gc);
    if (protocol) {
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, remove_buddies))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, remove_buddies))
    purple_protocol_server_iface_remove_buddies(protocol, gc, buddies, groups);
    else {
    GList *curb = buddies;
    --- a/libpurple/account.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/account.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_ACCOUNT_H_
    -#define _PURPLE_ACCOUNT_H_
    +#ifndef PURPLE_ACCOUNT_H
    +#define PURPLE_ACCOUNT_H
    /**
    * SECTION:account
    * @section_id: libpurple-account
    @@ -32,14 +32,8 @@
    #include <glib-object.h>
    #define PURPLE_TYPE_ACCOUNT (purple_account_get_type())
    -#define PURPLE_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_ACCOUNT, PurpleAccount))
    -#define PURPLE_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_ACCOUNT, PurpleAccountClass))
    -#define PURPLE_IS_ACCOUNT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_ACCOUNT))
    -#define PURPLE_IS_ACCOUNT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_ACCOUNT))
    -#define PURPLE_ACCOUNT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_ACCOUNT, PurpleAccountClass))
    typedef struct _PurpleAccount PurpleAccount;
    -typedef struct _PurpleAccountClass PurpleAccountClass;
    typedef gboolean (*PurpleFilterAccountFunc)(PurpleAccount *account);
    typedef void (*PurpleAccountRequestAuthorizationCb)(const char *response, void *data);
    @@ -96,36 +90,6 @@
    PURPLE_ACCOUNT_PRIVACY_ALLOW_BUDDYLIST
    } PurpleAccountPrivacyType;
    -/**
    - * PurpleAccount:
    - * @ui_data: The UI data associated with this account. This is a convenience
    - * field provided to the UIs -- it is not used by the libpurple core.
    - *
    - * Structure representing an account.
    - */
    -struct _PurpleAccount
    -{
    - GObject gparent;
    -
    - /*< public >*/
    - gpointer ui_data;
    -};
    -
    -/**
    - * PurpleAccountClass:
    - *
    - * The base class for all #PurpleAccount's.
    - */
    -struct _PurpleAccountClass {
    - GObjectClass parent_class;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    G_BEGIN_DECLS
    /**************************************************************************/
    @@ -137,7 +101,7 @@
    *
    * Returns: The #GType for the Account object.
    */
    -GType purple_account_get_type(void);
    +G_DECLARE_FINAL_TYPE(PurpleAccount, purple_account, PURPLE, ACCOUNT, GObject)
    /**
    * purple_account_new:
    @@ -632,27 +596,6 @@
    const char *name, gboolean value);
    /**
    - * purple_account_set_ui_data:
    - * @account: The account.
    - * @ui_data: A pointer to associate with this object.
    - *
    - * Set the UI data associated with this account.
    - */
    -void purple_account_set_ui_data(PurpleAccount *account, gpointer ui_data);
    -
    -/**
    - * purple_account_get_ui_data:
    - * @account: The account.
    - *
    - * Returns the UI data associated with this account.
    - *
    - * Returns: The UI data associated with this account. This is a
    - * convenience field provided to the UIs--it is not
    - * used by the libpurple core.
    - */
    -gpointer purple_account_get_ui_data(PurpleAccount *account);
    -
    -/**
    * purple_account_is_connected:
    * @account: The account.
    *
    @@ -1265,4 +1208,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_ACCOUNT_H_ */
    +#endif /* PURPLE_ACCOUNT_H */
    --- a/libpurple/accountopt.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/accountopt.c Tue Oct 08 21:48:28 2019 -0500
    @@ -185,11 +185,8 @@
    }
    else if (option->type == PURPLE_PREF_STRING_LIST)
    {
    - if (option->default_value.list != NULL)
    - {
    - g_list_foreach(option->default_value.list, purple_account_option_list_free, NULL);
    - g_list_free(option->default_value.list);
    - }
    + g_list_free_full(option->default_value.list,
    + (GDestroyNotify)purple_account_option_list_free);
    }
    g_free(option);
    @@ -250,11 +247,8 @@
    g_return_if_fail(option != NULL);
    g_return_if_fail(option->type == PURPLE_PREF_STRING_LIST);
    - if (option->default_value.list != NULL)
    - {
    - g_list_foreach(option->default_value.list, purple_account_option_list_free, NULL);
    - g_list_free(option->default_value.list);
    - }
    + g_list_free_full(option->default_value.list,
    + (GDestroyNotify)purple_account_option_list_free);
    option->default_value.list = values;
    }
    --- a/libpurple/accountopt.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/accountopt.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_ACCOUNTOPT_H_
    -#define _PURPLE_ACCOUNTOPT_H_
    +#ifndef PURPLE_ACCOUNTOPT_H
    +#define PURPLE_ACCOUNTOPT_H
    /**
    * SECTION:accountopt
    * @section_id: libpurple-accountopt
    @@ -440,4 +440,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_ACCOUNTOPT_H_ */
    +#endif /* PURPLE_ACCOUNTOPT_H */
    --- a/libpurple/accounts.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/accounts.c Tue Oct 08 21:48:28 2019 -0500
    @@ -621,10 +621,8 @@
    purple_accounts_remove(account);
    /* Remove this account's buddies */
    - for (gnode = purple_blist_get_root();
    - gnode != NULL;
    - gnode = purple_blist_node_get_sibling_next(gnode))
    - {
    + for (gnode = purple_blist_get_default_root(); gnode != NULL;
    + gnode = purple_blist_node_get_sibling_next(gnode)) {
    if (!PURPLE_IS_GROUP(gnode))
    continue;
    --- a/libpurple/accounts.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/accounts.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_ACCOUNTS_H_
    -#define _PURPLE_ACCOUNTS_H_
    +#ifndef PURPLE_ACCOUNTS_H
    +#define PURPLE_ACCOUNTS_H
    /**
    * SECTION:accounts
    * @section_id: libpurple-accounts
    @@ -167,7 +167,7 @@
    *
    * Finds an account with the specified name and protocol id.
    *
    - * Returns: The account, if found, or %FALSE otherwise.
    + * Returns: (transfer none): The account, if found, or %NULL otherwise.
    */
    PurpleAccount *purple_accounts_find(const char *name, const char *protocol);
    @@ -250,4 +250,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_ACCOUNTS_H_ */
    +#endif /* PURPLE_ACCOUNTS_H */
    --- a/libpurple/action.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/action.c Tue Oct 08 21:48:28 2019 -0500
    @@ -24,7 +24,6 @@
    GCallback callback;
    gpointer data;
    GList *children;
    - gchar *stock_icon;
    };
    /******************************************************************************
    @@ -40,7 +39,6 @@
    act->callback = callback;
    act->data = data;
    act->children = children;
    - act->stock_icon = NULL;
    return act;
    }
    @@ -51,7 +49,6 @@
    purple_action_menu_set_children(act, NULL);
    - g_free(act->stock_icon);
    g_free(act->label);
    g_free(act);
    }
    @@ -116,11 +113,6 @@
    act->children = children;
    }
    -const gchar *
    -purple_action_menu_get_stock_icon(PurpleActionMenu *act) {
    - return act->stock_icon;
    -}
    -
    /******************************************************************************
    * Protocol Action API
    *****************************************************************************/
    --- a/libpurple/action.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/action.h Tue Oct 08 21:48:28 2019 -0500
    @@ -25,9 +25,7 @@
    #include <glib.h>
    #include <glib-object.h>
    -
    -#define PURPLE_TYPE_PROTOCOL_ACTION (purple_protocol_action_get_type())
    -
    +#define PURPLE_TYPE_PROTOCOL_ACTION (purple_protocol_action_get_type())
    typedef struct _PurpleProtocolAction PurpleProtocolAction;
    typedef void (*PurpleProtocolActionCallback)(PurpleProtocolAction *action);
    @@ -45,6 +43,10 @@
    /**
    * PurpleProtocolAction:
    + * @label: A translated string to be shown in a user interface.
    + * @callback: The function to call when the user wants to perform this action.
    + * @connection: The connection that this action should be performed against.
    + * @user_data: User data to pass to @callback.
    *
    * Represents an action that the protocol can perform. This shows up in the
    * Accounts menu, under a submenu with the name of the account.
    @@ -161,16 +163,6 @@
    */
    void purple_action_menu_set_children(PurpleActionMenu *act, GList *children);
    -/**
    - * purple_action_menu_get_stock_icon:
    - * @act: The menu action.
    - *
    - * Gets the stock icon of the PurpleActionMenu.
    - *
    - * Returns: The stock icon identifier.
    - */
    -const gchar *purple_action_menu_get_stock_icon(PurpleActionMenu *act);
    -
    /******************************************************************************
    * Protocol Action API
    *****************************************************************************/
    --- a/libpurple/attention.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/attention.h Tue Oct 08 21:48:28 2019 -0500
    @@ -47,37 +47,12 @@
    */
    typedef struct _PurpleAttentionType PurpleAttentionType;
    -#define PURPLE_TYPE_PROTOCOL_ATTENTION (purple_protocol_attention_get_type())
    -#define PURPLE_PROTOCOL_ATTENTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PROTOCOL_ATTENTION, PurpleProtocolAttention))
    -#define PURPLE_IS_PROTOCOL_ATTENTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_ATTENTION))
    -#define PURPLE_PROTOCOL_ATTENTION_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_ATTENTION, PurpleProtocolAttentionInterface))
    -
    -typedef struct _PurpleProtocolAttention PurpleProtocolAttention;
    -typedef struct _PurpleProtocolAttentionInterface PurpleProtocolAttentionInterface;
    -
    #include "account.h"
    #include "connection.h"
    -/**
    - * PurpleProtocolAttentionInterface:
    - *
    - * The protocol attention interface.
    - *
    - * This interface provides attention API for sending and receiving
    - * zaps/nudges/buzzes etc.
    - */
    -struct _PurpleProtocolAttentionInterface
    -{
    - /*< private >*/
    - GTypeInterface parent;
    +G_BEGIN_DECLS
    - /*< public >*/
    - gboolean (*send)(PurpleProtocolAttention *attn, PurpleConnection *gc, const gchar *username, guint type);
    -
    - GList *(*get_types)(PurpleProtocolAttention *attn, PurpleAccount *acct);
    -};
    -
    -G_BEGIN_DECLS
    +#define PURPLE_TYPE_PROTOCOL_ATTENTION (purple_protocol_attention_get_type())
    /******************************************************************************
    * AttentionType API
    @@ -223,7 +198,27 @@
    *
    * Returns: The #GType for the protocol attention interface.
    */
    -GType purple_protocol_attention_get_type(void);
    +G_DECLARE_INTERFACE(PurpleProtocolAttention, purple_protocol_attention, PURPLE,
    + PROTOCOL_ATTENTION, GObject)
    +
    +/**
    + * PurpleProtocolAttentionInterface:
    + *
    + * The protocol attention interface.
    + *
    + * This interface provides attention API for sending and receiving
    + * zaps/nudges/buzzes etc.
    + */
    +struct _PurpleProtocolAttentionInterface {
    + /*< private >*/
    + GTypeInterface parent;
    +
    + /*< public >*/
    + gboolean (*send)(PurpleProtocolAttention *attn, PurpleConnection *gc,
    + const gchar *username, guint type);
    +
    + GList *(*get_types)(PurpleProtocolAttention *attn, PurpleAccount *acct);
    +};
    /**
    * purple_protocol_attention_get_types:
    --- a/libpurple/blistnode.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/blistnode.c Tue Oct 08 21:48:28 2019 -0500
    @@ -114,29 +114,27 @@
    void purple_blist_node_remove_setting(PurpleBlistNode *node, const char *key)
    {
    - PurpleBlistUiOps *ops;
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    + PurpleBlistNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BLIST_NODE(node));
    + g_return_if_fail(key != NULL);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_if_fail(priv->settings != NULL);
    - g_return_if_fail(key != NULL);
    g_hash_table_remove(priv->settings, key);
    - ops = purple_blist_get_ui_ops();
    - if (ops && ops->save_node)
    - ops->save_node(node);
    + purple_blist_save_node(purple_blist_get_default(), node);
    }
    void
    purple_blist_node_set_transient(PurpleBlistNode *node, gboolean transient)
    {
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    + PurpleBlistNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BLIST_NODE(node));
    + priv = purple_blist_node_get_instance_private(node);
    priv->transient = transient;
    g_object_notify_by_pspec(G_OBJECT(node),
    @@ -146,34 +144,35 @@
    gboolean
    purple_blist_node_is_transient(PurpleBlistNode *node)
    {
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    + PurpleBlistNodePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_BLIST_NODE(node), 0);
    + priv = purple_blist_node_get_instance_private(node);
    return priv->transient;
    }
    GHashTable *
    purple_blist_node_get_settings(PurpleBlistNode *node)
    {
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    + PurpleBlistNodePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BLIST_NODE(node), NULL);
    + priv = purple_blist_node_get_instance_private(node);
    return priv->settings;
    }
    gboolean
    purple_blist_node_has_setting(PurpleBlistNode* node, const char *key)
    {
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    + PurpleBlistNodePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_BLIST_NODE(node), FALSE);
    + g_return_val_if_fail(key != NULL, FALSE);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_val_if_fail(priv->settings != NULL, FALSE);
    - g_return_val_if_fail(key != NULL, FALSE);
    /* Boxed type, so it won't ever be NULL, so no need for _extended */
    return (g_hash_table_lookup(priv->settings, key) != NULL);
    @@ -182,35 +181,34 @@
    void
    purple_blist_node_set_bool(PurpleBlistNode* node, const char *key, gboolean data)
    {
    + PurpleBlistNodePrivate *priv = NULL;
    GValue *value;
    - PurpleBlistUiOps *ops;
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BLIST_NODE(node));
    + g_return_if_fail(key != NULL);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_if_fail(priv->settings != NULL);
    - g_return_if_fail(key != NULL);
    value = purple_value_new(G_TYPE_BOOLEAN);
    g_value_set_boolean(value, data);
    g_hash_table_replace(priv->settings, g_strdup(key), value);
    - ops = purple_blist_get_ui_ops();
    - if (ops && ops->save_node)
    - ops->save_node(node);
    + purple_blist_save_node(purple_blist_get_default(), node);
    }
    gboolean
    purple_blist_node_get_bool(PurpleBlistNode* node, const char *key)
    {
    + PurpleBlistNodePrivate *priv = NULL;
    GValue *value;
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_BLIST_NODE(node), FALSE);
    + g_return_val_if_fail(key != NULL, FALSE);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_val_if_fail(priv->settings != NULL, FALSE);
    - g_return_val_if_fail(key != NULL, FALSE);
    value = g_hash_table_lookup(priv->settings, key);
    @@ -225,35 +223,34 @@
    void
    purple_blist_node_set_int(PurpleBlistNode* node, const char *key, int data)
    {
    + PurpleBlistNodePrivate *priv = NULL;
    GValue *value;
    - PurpleBlistUiOps *ops;
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BLIST_NODE(node));
    + g_return_if_fail(key != NULL);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_if_fail(priv->settings != NULL);
    - g_return_if_fail(key != NULL);
    value = purple_value_new(G_TYPE_INT);
    g_value_set_int(value, data);
    g_hash_table_replace(priv->settings, g_strdup(key), value);
    - ops = purple_blist_get_ui_ops();
    - if (ops && ops->save_node)
    - ops->save_node(node);
    + purple_blist_save_node(purple_blist_get_default(), node);
    }
    int
    purple_blist_node_get_int(PurpleBlistNode* node, const char *key)
    {
    + PurpleBlistNodePrivate *priv = NULL;
    GValue *value;
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_BLIST_NODE(node), 0);
    + g_return_val_if_fail(key != NULL, 0);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_val_if_fail(priv->settings != NULL, 0);
    - g_return_val_if_fail(key != NULL, 0);
    value = g_hash_table_lookup(priv->settings, key);
    @@ -268,35 +265,34 @@
    void
    purple_blist_node_set_string(PurpleBlistNode* node, const char *key, const char *data)
    {
    + PurpleBlistNodePrivate *priv = NULL;
    GValue *value;
    - PurpleBlistUiOps *ops;
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BLIST_NODE(node));
    + g_return_if_fail(key != NULL);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_if_fail(priv->settings != NULL);
    - g_return_if_fail(key != NULL);
    value = purple_value_new(G_TYPE_STRING);
    g_value_set_string(value, data);
    g_hash_table_replace(priv->settings, g_strdup(key), value);
    - ops = purple_blist_get_ui_ops();
    - if (ops && ops->save_node)
    - ops->save_node(node);
    + purple_blist_save_node(purple_blist_get_default(), node);
    }
    const char *
    purple_blist_node_get_string(PurpleBlistNode* node, const char *key)
    {
    + PurpleBlistNodePrivate *priv = NULL;
    GValue *value;
    - PurpleBlistNodePrivate *priv =
    - purple_blist_node_get_instance_private(node);
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BLIST_NODE(node), NULL);
    + g_return_val_if_fail(key != NULL, NULL);
    +
    + priv = purple_blist_node_get_instance_private(node);
    g_return_val_if_fail(priv->settings != NULL, NULL);
    - g_return_val_if_fail(key != NULL, NULL);
    value = g_hash_table_lookup(priv->settings, key);
    --- a/libpurple/blistnode.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/blistnode.h Tue Oct 08 21:48:28 2019 -0500
    @@ -91,7 +91,7 @@
    * @offline: Whether to include nodes for offline accounts
    *
    * Returns the next node of a given node. This function is to be used to iterate
    - * over the tree returned by purple_blist_get_buddy_list.
    + * over the tree returned by purple_blist_get_default.
    *
    * See purple_blist_node_get_parent(), purple_blist_node_get_first_child(),
    * purple_blist_node_get_sibling_next(), purple_blist_node_get_sibling_prev().
    @@ -122,7 +122,7 @@
    * See purple_blist_node_get_parent(), purple_blist_node_get_sibling_next(),
    * purple_blist_node_get_sibling_prev(), purple_blist_node_next().
    *
    - * Returns: The child node.
    + * Returns: (transfer none): The child node.
    */
    PurpleBlistNode *purple_blist_node_get_first_child(PurpleBlistNode *node);
    @@ -135,7 +135,7 @@
    * See purple_blist_node_get_parent(), purple_blist_node_get_first_child(),
    * purple_blist_node_get_sibling_prev(), purple_blist_node_next().
    *
    - * Returns: The sibling node.
    + * Returns: (transfer none): The sibling node.
    */
    PurpleBlistNode *purple_blist_node_get_sibling_next(PurpleBlistNode *node);
    @@ -148,7 +148,7 @@
    * See purple_blist_node_get_parent(), purple_blist_node_get_first_child(),
    * purple_blist_node_get_sibling_next(), purple_blist_node_next().
    *
    - * Returns: The sibling node.
    + * Returns: (transfer none): The sibling node.
    */
    PurpleBlistNode *purple_blist_node_get_sibling_prev(PurpleBlistNode *node);
    @@ -177,7 +177,7 @@
    *
    * Returns a node's settings
    *
    - * Returns: The hash table with the node's settings
    + * Returns: (transfer none): The hash table with the node's settings.
    */
    GHashTable *purple_blist_node_get_settings(PurpleBlistNode *node);
    @@ -289,8 +289,9 @@
    * purple_blist_node_get_extended_menu:
    * @n: The blist node for which to obtain the extended menu items.
    *
    - * Returns: (element-type PurpleActionMenu): The extended menu items for a buddy
    - * list node, as harvested by the blist-node-extended-menu signal.
    + * Returns: (element-type PurpleActionMenu) (transfer full): The extended menu
    + * items for a buddy list node, as harvested by the
    + * blist-node-extended-menu signal.
    */
    GList *purple_blist_node_get_extended_menu(PurpleBlistNode *n);
    --- a/libpurple/buddy.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/buddy.c Tue Oct 08 21:48:28 2019 -0500
    @@ -67,10 +67,11 @@
    void
    purple_buddy_set_icon(PurpleBuddy *buddy, PurpleBuddyIcon *icon)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    +
    + priv = purple_buddy_get_instance_private(buddy);
    if (priv->icon != icon)
    {
    @@ -83,37 +84,40 @@
    purple_signal_emit(purple_blist_get_handle(), "buddy-icon-changed", buddy);
    - if (ops && ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    }
    PurpleBuddyIcon *
    purple_buddy_get_icon(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    + priv = purple_buddy_get_instance_private(buddy);
    return priv->icon;
    }
    PurpleAccount *
    purple_buddy_get_account(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    + priv = purple_buddy_get_instance_private(buddy);
    return priv->account;
    }
    void
    purple_buddy_set_name(PurpleBuddy *buddy, const char *name)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    +
    + priv = purple_buddy_get_instance_private(buddy);
    purple_blist_update_buddies_cache(buddy, name);
    @@ -122,49 +126,52 @@
    g_object_notify_by_pspec(G_OBJECT(buddy), properties[PROP_NAME]);
    - if (ops) {
    - if (ops->save_node)
    - ops->save_node(PURPLE_BLIST_NODE(buddy));
    - if (ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy));
    - }
    + purple_blist_save_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    }
    const char *
    purple_buddy_get_name(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    + priv = purple_buddy_get_instance_private(buddy);
    return priv->name;
    }
    gpointer
    purple_buddy_get_protocol_data(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    + priv = purple_buddy_get_instance_private(buddy);
    return priv->proto_data;
    }
    void
    purple_buddy_set_protocol_data(PurpleBuddy *buddy, gpointer data)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    + priv = purple_buddy_get_instance_private(buddy);
    priv->proto_data = data;
    }
    const char *purple_buddy_get_alias_only(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    +
    + priv = purple_buddy_get_instance_private(buddy);
    if ((priv->local_alias != NULL) && (*priv->local_alias != '\0')) {
    return priv->local_alias;
    @@ -179,10 +186,12 @@
    const char *purple_buddy_get_contact_alias(PurpleBuddy *buddy)
    {
    + PurpleBuddyPrivate *priv = NULL;
    PurpleContact *c;
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    +
    + priv = purple_buddy_get_instance_private(buddy);
    /* Search for an alias for the buddy. In order of precedence: */
    /* The local buddy alias */
    @@ -204,9 +213,11 @@
    const char *purple_buddy_get_alias(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    +
    + priv = purple_buddy_get_instance_private(buddy);
    /* Search for an alias for the buddy. In order of precedence: */
    /* The buddy alias */
    @@ -224,13 +235,14 @@
    void
    purple_buddy_set_local_alias(PurpleBuddy *buddy, const char *alias)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyPrivate *priv = NULL;
    PurpleIMConversation *im;
    char *old_alias;
    char *new_alias = NULL;
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    +
    + priv = purple_buddy_get_instance_private(buddy);
    if ((alias != NULL) && (*alias != '\0'))
    new_alias = purple_utf8_strip_unprintables(alias);
    @@ -252,11 +264,10 @@
    g_object_notify_by_pspec(G_OBJECT(buddy),
    properties[PROP_LOCAL_ALIAS]);
    - if (ops && ops->save_node)
    - ops->save_node(PURPLE_BLIST_NODE(buddy));
    -
    - if (ops && ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy));
    + purple_blist_save_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    im = purple_conversations_find_im_with_account(priv->name,
    priv->account);
    @@ -270,23 +281,25 @@
    const char *purple_buddy_get_local_alias(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    + priv = purple_buddy_get_instance_private(buddy);
    return priv->local_alias;
    }
    void
    purple_buddy_set_server_alias(PurpleBuddy *buddy, const char *alias)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyPrivate *priv = NULL;
    PurpleIMConversation *im;
    char *old_alias;
    char *new_alias = NULL;
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    +
    + priv = purple_buddy_get_instance_private(buddy);
    if ((alias != NULL) && (*alias != '\0') && g_utf8_validate(alias, -1, NULL))
    new_alias = purple_utf8_strip_unprintables(alias);
    @@ -308,12 +321,10 @@
    g_object_notify_by_pspec(G_OBJECT(buddy),
    properties[PROP_SERVER_ALIAS]);
    - if (ops) {
    - if (ops->save_node)
    - ops->save_node(PURPLE_BLIST_NODE(buddy));
    - if (ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy));
    - }
    + purple_blist_save_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    im = purple_conversations_find_im_with_account(priv->name,
    priv->account);
    @@ -327,9 +338,11 @@
    const char *purple_buddy_get_server_alias(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    +
    + priv = purple_buddy_get_instance_private(buddy);
    if ((priv->server_alias) && (*priv->server_alias))
    return priv->server_alias;
    @@ -346,24 +359,26 @@
    PurplePresence *purple_buddy_get_presence(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), NULL);
    + priv = purple_buddy_get_instance_private(buddy);
    return priv->presence;
    }
    void
    purple_buddy_update_status(PurpleBuddy *buddy, PurpleStatus *old_status)
    {
    + PurpleBuddyPrivate *priv = NULL;
    PurpleStatus *status;
    PurpleBlistNode *cnode;
    PurpleContact *contact;
    PurpleCountingNode *contact_counter, *group_counter;
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    +
    + priv = purple_buddy_get_instance_private(buddy);
    status = purple_presence_get_active_status(priv->presence);
    @@ -415,25 +430,27 @@
    */
    purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
    - if (ops && ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    }
    PurpleMediaCaps purple_buddy_get_media_caps(PurpleBuddy *buddy)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_BUDDY(buddy), 0);
    + priv = purple_buddy_get_instance_private(buddy);
    return priv->media_caps;
    }
    void purple_buddy_set_media_caps(PurpleBuddy *buddy, PurpleMediaCaps media_caps)
    {
    - PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    + PurpleBuddyPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    + priv = purple_buddy_get_instance_private(buddy);
    priv->media_caps = media_caps;
    g_object_notify_by_pspec(G_OBJECT(buddy),
    @@ -536,15 +553,14 @@
    purple_buddy_constructed(GObject *object) {
    PurpleBuddy *buddy = PURPLE_BUDDY(object);
    PurpleBuddyPrivate *priv = purple_buddy_get_instance_private(buddy);
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    G_OBJECT_CLASS(purple_buddy_parent_class)->constructed(object);
    priv->presence = PURPLE_PRESENCE(purple_buddy_presence_new(buddy));
    purple_presence_set_status_active(priv->presence, "offline", TRUE);
    - if (ops && ops->new_node)
    - ops->new_node((PurpleBlistNode *)buddy);
    + purple_blist_new_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    priv->is_constructed = TRUE;
    }
    --- a/libpurple/buddy.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/buddy.h Tue Oct 08 21:48:28 2019 -0500
    @@ -141,7 +141,7 @@
    *
    * Returns a buddy's account.
    *
    - * Returns: The account
    + * Returns: (transfer none): The account.
    */
    PurpleAccount *purple_buddy_get_account(PurpleBuddy *buddy);
    @@ -197,7 +197,7 @@
    *
    * Returns a buddy's contact.
    *
    - * Returns: The buddy's contact.
    + * Returns: (transfer none): The buddy's contact.
    */
    PurpleContact *purple_buddy_get_contact(PurpleBuddy *buddy);
    @@ -207,7 +207,7 @@
    *
    * Returns a buddy's presence.
    *
    - * Returns: The buddy's presence.
    + * Returns: (transfer none): The buddy's presence.
    */
    PurplePresence *purple_buddy_get_presence(PurpleBuddy *buddy);
    @@ -321,7 +321,7 @@
    *
    * Returns the group of which the buddy is a member.
    *
    - * Returns: The group or NULL if the buddy is not in a group
    + * Returns: (transfer none): The group or %NULL if the buddy is not in a group.
    */
    PurpleGroup *purple_buddy_get_group(PurpleBuddy *buddy);
    --- a/libpurple/buddyicon.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/buddyicon.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#define _PURPLE_BUDDYICON_C_
    #include "internal.h"
    #include "buddyicon.h"
    @@ -636,6 +635,7 @@
    const char *protocol_icon_file;
    const char *dirname;
    gboolean caching;
    + gchar *path;
    guchar *data;
    size_t len;
    @@ -655,23 +655,20 @@
    * functions. */
    purple_buddy_icons_set_caching(FALSE);
    - if (protocol_icon_file != NULL)
    - {
    - char *path = g_build_filename(dirname, protocol_icon_file, NULL);
    - if (read_icon_file(path, &data, &len))
    - {
    - const char *checksum;
    + path = g_build_filename(dirname, protocol_icon_file, NULL);
    + if (read_icon_file(path, &data, &len)) {
    + const char *checksum;
    - icon = purple_buddy_icon_create(account, username);
    - icon->img = NULL;
    - checksum = purple_blist_node_get_string((PurpleBlistNode*)b, "icon_checksum");
    - purple_buddy_icon_set_data(icon, data, len, checksum);
    - }
    - else
    - delete_buddy_icon_settings((PurpleBlistNode*)b, "buddy_icon");
    + icon = purple_buddy_icon_create(account, username);
    + icon->img = NULL;
    + checksum = purple_blist_node_get_string((PurpleBlistNode *)b,
    + "icon_checksum");
    + purple_buddy_icon_set_data(icon, data, len, checksum);
    + } else {
    + delete_buddy_icon_settings((PurpleBlistNode *)b, "buddy_icon");
    + }
    - g_free(path);
    - }
    + g_free(path);
    purple_buddy_icons_set_caching(caching);
    }
    @@ -848,7 +845,6 @@
    char *old_icon;
    PurpleImage *old_img;
    PurpleImage *img = NULL;
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    g_return_val_if_fail(node != NULL, NULL);
    @@ -903,8 +899,8 @@
    /* Is this call necessary anymore? Can the buddies
    * themselves need updating when the custom buddy
    * icon changes? */
    - if (ops && ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    }
    } else if (PURPLE_IS_CHAT(node)) {
    PurpleChatConversation *chat = NULL;
    @@ -915,8 +911,7 @@
    }
    }
    - if (ops && ops->update)
    - ops->update(purple_blist_get_buddy_list(), node);
    + purple_blist_update_node(purple_blist_get_default(), node);
    if (old_img) {
    g_object_unref(old_img);
    @@ -995,7 +990,7 @@
    void
    _purple_buddy_icons_blist_loaded_cb()
    {
    - PurpleBlistNode *node = purple_blist_get_root();
    + PurpleBlistNode *node = purple_blist_get_default_root();
    const char *dirname = purple_buddy_icons_get_cache_dir();
    while (node != NULL)
    --- a/libpurple/buddyicon.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/buddyicon.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_BUDDYICON_H_
    -#define _PURPLE_BUDDYICON_H_
    +#ifndef PURPLE_BUDDYICON_H
    +#define PURPLE_BUDDYICON_H
    /**
    * SECTION:buddyicon
    * @section_id: libpurple-buddyicon
    @@ -170,7 +170,7 @@
    *
    * Returns the buddy icon's account.
    *
    - * Returns: The account.
    + * Returns: (transfer none): The account.
    */
    PurpleAccount *purple_buddy_icon_get_account(const PurpleBuddyIcon *icon);
    @@ -290,14 +290,11 @@
    *
    * Returns the buddy icon image for an account.
    *
    - * The caller owns a reference to the image, and must dereference
    - * the image with g_object_unref() for it to be freed.
    - *
    * This function deals with loading the icon from the cache, if
    * needed, so it should be called in any case where you want the
    * appropriate icon.
    *
    - * Returns: The account's buddy icon image.
    + * Returns: (transfer full): The account's buddy icon image.
    */
    PurpleImage *
    purple_buddy_icons_find_account_icon(PurpleAccount *account);
    @@ -314,9 +311,7 @@
    * This function will deal with saving a record of the icon,
    * caching the data, etc.
    *
    - * Returns: The icon that was set. The caller does NOT own
    - * a reference to this, and must call g_object_ref()
    - * if it wants one.
    + * Returns: (transfer none): The icon that was set.
    */
    PurpleImage *
    purple_buddy_icons_set_account_icon(PurpleAccount *account,
    @@ -353,14 +348,11 @@
    *
    * Returns the custom buddy icon image for a blist node.
    *
    - * The caller owns a reference to the image, and must dereference
    - * the image with g_object_unref() for it to be freed.
    - *
    * This function deals with loading the icon from the cache, if
    * needed, so it should be called in any case where you want the
    * appropriate icon.
    *
    - * Returns: The custom buddy icon.
    + * Returns: (transfer full): The custom buddy icon.
    */
    PurpleImage *
    purple_buddy_icons_node_find_custom_icon(PurpleBlistNode *node);
    @@ -377,8 +369,7 @@
    * This function will deal with saving a record of the icon, caching the data,
    * etc.
    *
    - * Returns: The icon that was set. The caller does NOT own a reference to this,
    - * and must call g_object_ref() if it wants one.
    + * Returns: (transfer none): The icon that was set.
    */
    PurpleImage *
    purple_buddy_icons_node_set_custom_icon(PurpleBlistNode *node,
    @@ -395,8 +386,7 @@
    * Convenience wrapper around purple_buddy_icons_node_set_custom_icon.
    * See purple_buddy_icons_node_set_custom_icon().
    *
    - * Returns: The icon that was set. The caller does NOT own a reference to this,
    - * and must call g_object_ref() if it wants one.
    + * Returns: (transfer none): The icon that was set.
    */
    PurpleImage *
    purple_buddy_icons_node_set_custom_icon_from_file(PurpleBlistNode *node,
    @@ -506,4 +496,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_BUDDYICON_H_ */
    +#endif /* PURPLE_BUDDYICON_H */
    --- a/libpurple/buddylist.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/buddylist.c Tue Oct 08 21:48:28 2019 -0500
    @@ -35,11 +35,11 @@
    /* Private data for a buddy list. */
    typedef struct {
    + PurpleBlistNode *root;
    GHashTable *buddies; /* Every buddy in this list */
    } PurpleBuddyListPrivate;
    -static PurpleBlistUiOps *blist_ui_ops = NULL;
    -
    +static GType buddy_list_type = G_TYPE_INVALID;
    static PurpleBuddyList *purplebuddylist = NULL;
    G_DEFINE_TYPE_WITH_PRIVATE(PurpleBuddyList, purple_buddy_list, G_TYPE_OBJECT);
    @@ -375,8 +375,8 @@
    "localized-default-group", localized_default);
    }
    - for (gnode = purplebuddylist->root; gnode != NULL; gnode = gnode->next)
    - {
    + for (gnode = purple_blist_get_default_root(); gnode != NULL;
    + gnode = gnode->next) {
    if (purple_blist_node_is_transient(gnode))
    continue;
    if (PURPLE_IS_GROUP(gnode))
    @@ -426,17 +426,17 @@
    }
    static void
    -_purple_blist_schedule_save()
    +purple_blist_real_schedule_save(void)
    {
    if (save_timer == 0)
    save_timer = g_timeout_add_seconds(5, save_cb, NULL);
    }
    static void
    -purple_blist_save_account(PurpleAccount *account)
    +purple_blist_real_save_account(PurpleBuddyList *list, PurpleAccount *account)
    {
    #if 1
    - _purple_blist_schedule_save();
    + purple_blist_real_schedule_save();
    #else
    if (account != NULL) {
    /* Save the buddies and privacy data for this account */
    @@ -447,18 +447,23 @@
    }
    static void
    -purple_blist_save_node(PurpleBlistNode *node)
    +purple_blist_real_save_node(PurpleBuddyList *list, PurpleBlistNode *node)
    {
    - _purple_blist_schedule_save();
    + purple_blist_real_schedule_save();
    }
    void purple_blist_schedule_save()
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    +
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    +
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    /* Save everything */
    - if (ops && ops->save_account)
    - ops->save_account(NULL);
    + if (klass && klass->save_account) {
    + klass->save_account(purplebuddylist, NULL);
    + }
    }
    /*********************************************************************
    @@ -608,8 +613,8 @@
    PurpleXmlNode *cnode;
    group = purple_group_new(name);
    - purple_blist_add_group(group,
    - purple_blist_get_last_sibling(purplebuddylist->root));
    + purple_blist_add_group(group, purple_blist_get_last_sibling(
    + purple_blist_get_default_root()));
    for (cnode = groupnode->child; cnode; cnode = cnode->next) {
    if (cnode->type != PURPLE_XMLNODE_TYPE_TAG)
    @@ -706,13 +711,18 @@
    *****************************************************************************/
    void
    +purple_blist_set_ui(GType type)
    +{
    + g_return_if_fail(g_type_is_a(type, PURPLE_TYPE_BUDDY_LIST) ||
    + type == G_TYPE_INVALID);
    + buddy_list_type = type;
    +}
    +
    +void
    purple_blist_boot(void)
    {
    - PurpleBlistUiOps *ui_ops;
    GList *account;
    - PurpleBuddyList *gbl = g_object_new(PURPLE_TYPE_BUDDY_LIST, NULL);
    -
    - ui_ops = purple_blist_get_ui_ops();
    + PurpleBuddyList *gbl = g_object_new(buddy_list_type, NULL);
    buddies_cache = g_hash_table_new_full(g_direct_hash, g_direct_equal,
    NULL, (GDestroyNotify)g_hash_table_destroy);
    @@ -726,24 +736,37 @@
    purple_blist_buddies_cache_add_account(account->data);
    }
    - if (ui_ops != NULL && ui_ops->new_list != NULL)
    - ui_ops->new_list(gbl);
    -
    purplebuddylist = gbl;
    load_blist();
    }
    PurpleBuddyList *
    -purple_blist_get_buddy_list()
    +purple_blist_get_default(void)
    {
    return purplebuddylist;
    }
    PurpleBlistNode *
    -purple_blist_get_root()
    +purple_blist_get_default_root(void)
    {
    - return purplebuddylist ? purplebuddylist->root : NULL;
    + if (purplebuddylist) {
    + PurpleBuddyListPrivate *priv =
    + purple_buddy_list_get_instance_private(purplebuddylist);
    + return priv->root;
    + }
    + return NULL;
    +}
    +
    +PurpleBlistNode *
    +purple_blist_get_root(PurpleBuddyList *list)
    +{
    + PurpleBuddyListPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_BUDDY_LIST(list), NULL);
    + priv = purple_buddy_list_get_instance_private(list);
    +
    + return priv->root;
    }
    static void
    @@ -767,32 +790,28 @@
    return buddies;
    }
    -void *
    -purple_blist_get_ui_data()
    -{
    - return purplebuddylist->ui_data;
    -}
    -
    -void
    -purple_blist_set_ui_data(void *ui_data)
    -{
    - purplebuddylist->ui_data = ui_data;
    -}
    -
    void purple_blist_show()
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    - if (ops && ops->show)
    - ops->show(purplebuddylist);
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    +
    + if (klass && klass->show) {
    + klass->show(purplebuddylist);
    + }
    }
    void purple_blist_set_visible(gboolean show)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    - if (ops && ops->set_visible)
    - ops->set_visible(purplebuddylist, show);
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    +
    + if (klass && klass->set_visible) {
    + klass->set_visible(purplebuddylist, show);
    + }
    }
    void purple_blist_update_buddies_cache(PurpleBuddy *buddy, const char *new_name)
    @@ -844,10 +863,12 @@
    void purple_blist_add_chat(PurpleChat *chat, PurpleGroup *group, PurpleBlistNode *node)
    {
    PurpleBlistNode *cnode = PURPLE_BLIST_NODE(chat);
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    PurpleCountingNode *group_counter;
    g_return_if_fail(PURPLE_IS_CHAT(chat));
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    if (node == NULL) {
    if (group == NULL)
    @@ -855,8 +876,10 @@
    /* Add group to blist if isn't already on it. Fixes #2752. */
    if (!purple_blist_find_group(purple_group_get_name(group))) {
    - purple_blist_add_group(group,
    - purple_blist_get_last_sibling(purplebuddylist->root));
    + purple_blist_add_group(
    + group,
    + purple_blist_get_last_sibling(
    + purple_blist_get_default_root()));
    }
    } else {
    group = PURPLE_GROUP(node->parent);
    @@ -883,12 +906,14 @@
    if (cnode->parent->child == cnode)
    cnode->parent->child = cnode->next;
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, cnode);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist, cnode);
    + }
    /* ops->remove() cleaned up the cnode's ui_data, so we need to
    * reinitialize it */
    - if (ops && ops->new_node)
    - ops->new_node(cnode);
    + if (klass && klass->new_node) {
    + klass->new_node(purplebuddylist, cnode);
    + }
    }
    if (node != NULL) {
    @@ -919,11 +944,14 @@
    }
    }
    - if (ops) {
    - if (ops->save_node)
    - ops->save_node(cnode);
    - if (ops->update)
    - ops->update(purplebuddylist, PURPLE_BLIST_NODE(cnode));
    + if (klass) {
    + if (klass->save_node) {
    + klass->save_node(purplebuddylist, cnode);
    + }
    + if (klass->update) {
    + klass->update(purplebuddylist,
    + PURPLE_BLIST_NODE(cnode));
    + }
    }
    purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
    @@ -932,19 +960,21 @@
    void purple_blist_add_buddy(PurpleBuddy *buddy, PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
    {
    + PurpleBuddyListClass *klass = NULL;
    + PurpleBuddyListPrivate *priv = NULL;
    PurpleBlistNode *cnode, *bnode;
    PurpleCountingNode *contact_counter, *group_counter;
    PurpleGroup *g;
    PurpleContact *c;
    PurpleAccount *account;
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    struct _purple_hbuddy *hb, *hb2;
    GHashTable *account_buddies;
    - PurpleBuddyListPrivate *priv =
    - purple_buddy_list_get_instance_private(purplebuddylist);
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + priv = purple_buddy_list_get_instance_private(purplebuddylist);
    bnode = PURPLE_BLIST_NODE(buddy);
    account = purple_buddy_get_account(buddy);
    @@ -966,8 +996,8 @@
    g = purple_blist_get_default_group();
    /* Add group to blist if isn't already on it. Fixes #2752. */
    if (!purple_blist_find_group(purple_group_get_name(g))) {
    - purple_blist_add_group(g,
    - purple_blist_get_last_sibling(purplebuddylist->root));
    + purple_blist_add_group(
    + g, purple_blist_get_last_sibling(priv->root));
    }
    c = purple_contact_new();
    purple_blist_add_contact(c, g,
    @@ -1005,8 +1035,9 @@
    if (bnode->parent->child == bnode)
    bnode->parent->child = bnode->next;
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, bnode);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist, bnode);
    + }
    if (bnode->parent->parent != (PurpleBlistNode*)g) {
    struct _purple_hbuddy hb;
    @@ -1025,8 +1056,9 @@
    } else {
    purple_contact_invalidate_priority_buddy((PurpleContact*)bnode->parent);
    - if (ops && ops->update)
    - ops->update(purplebuddylist, bnode->parent);
    + if (klass && klass->update) {
    + klass->update(purplebuddylist, bnode->parent);
    + }
    }
    }
    @@ -1079,11 +1111,15 @@
    purple_contact_invalidate_priority_buddy(purple_buddy_get_contact(buddy));
    - if (ops) {
    - if (ops->save_node)
    - ops->save_node((PurpleBlistNode*) buddy);
    - if (ops->update)
    - ops->update(purplebuddylist, PURPLE_BLIST_NODE(buddy));
    + if (klass) {
    + if (klass->save_node) {
    + klass->save_node(purplebuddylist,
    + (PurpleBlistNode *)buddy);
    + }
    + if (klass->update) {
    + klass->update(purplebuddylist,
    + PURPLE_BLIST_NODE(buddy));
    + }
    }
    /* Signal that the buddy has been added */
    @@ -1093,18 +1129,21 @@
    void purple_blist_add_contact(PurpleContact *contact, PurpleGroup *group, PurpleBlistNode *node)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    + PurpleBuddyListPrivate *priv = NULL;
    PurpleGroup *g;
    PurpleBlistNode *gnode, *cnode, *bnode;
    PurpleCountingNode *contact_counter, *group_counter;
    - PurpleBuddyListPrivate *priv =
    - purple_buddy_list_get_instance_private(purplebuddylist);
    g_return_if_fail(PURPLE_IS_CONTACT(contact));
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    if (PURPLE_BLIST_NODE(contact) == node)
    return;
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + priv = purple_buddy_list_get_instance_private(purplebuddylist);
    +
    if (node && (PURPLE_IS_CONTACT(node) ||
    PURPLE_IS_CHAT(node)))
    g = PURPLE_GROUP(node->parent);
    @@ -1190,11 +1229,13 @@
    purple_counting_node_change_current_size(group_counter, -1);
    purple_counting_node_change_total_size(group_counter, -1);
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, cnode);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist, cnode);
    + }
    - if (ops && ops->remove_node)
    - ops->remove_node(cnode);
    + if (klass && klass->remove_node) {
    + klass->remove_node(purplebuddylist, cnode);
    + }
    }
    if (node && (PURPLE_IS_CONTACT(node) ||
    @@ -1223,50 +1264,59 @@
    purple_counting_node_change_current_size(group_counter, +1);
    purple_counting_node_change_total_size(group_counter, +1);
    - if (ops && ops->save_node)
    - {
    - if (cnode->child)
    - ops->save_node(cnode);
    - for (bnode = cnode->child; bnode; bnode = bnode->next)
    - ops->save_node(bnode);
    + if (klass && klass->save_node) {
    + if (cnode->child) {
    + klass->save_node(purplebuddylist, cnode);
    + }
    + for (bnode = cnode->child; bnode; bnode = bnode->next) {
    + klass->save_node(purplebuddylist, bnode);
    + }
    }
    - if (ops && ops->update)
    - {
    - if (cnode->child)
    - ops->update(purplebuddylist, cnode);
    + if (klass && klass->update) {
    + if (cnode->child) {
    + klass->update(purplebuddylist, cnode);
    + }
    - for (bnode = cnode->child; bnode; bnode = bnode->next)
    - ops->update(purplebuddylist, bnode);
    + for (bnode = cnode->child; bnode; bnode = bnode->next) {
    + klass->update(purplebuddylist, bnode);
    + }
    }
    }
    void purple_blist_add_group(PurpleGroup *group, PurpleBlistNode *node)
    {
    - PurpleBlistUiOps *ops;
    + PurpleBuddyListClass *klass = NULL;
    + PurpleBuddyListPrivate *priv = NULL;
    PurpleBlistNode *gnode = (PurpleBlistNode*)group;
    gchar* key;
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    g_return_if_fail(PURPLE_IS_GROUP(group));
    - ops = purple_blist_get_ui_ops();
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + priv = purple_buddy_list_get_instance_private(purplebuddylist);
    /* if we're moving to overtop of ourselves, do nothing */
    if (gnode == node) {
    - if (!purplebuddylist->root)
    + if (!priv->root) {
    node = NULL;
    - else
    + } else {
    return;
    + }
    }
    if (purple_blist_find_group(purple_group_get_name(group))) {
    /* This is just being moved */
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, (PurpleBlistNode *)group);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist,
    + (PurpleBlistNode *)group);
    + }
    - if (gnode == purplebuddylist->root)
    - purplebuddylist->root = gnode->next;
    + if (gnode == priv->root) {
    + priv->root = gnode->next;
    + }
    if (gnode->prev)
    gnode->prev->next = gnode->next;
    if (gnode->next)
    @@ -1283,23 +1333,26 @@
    node->next->prev = gnode;
    node->next = gnode;
    } else {
    - if (purplebuddylist->root)
    - purplebuddylist->root->prev = gnode;
    - gnode->next = purplebuddylist->root;
    + if (priv->root) {
    + priv->root->prev = gnode;
    + }
    + gnode->next = priv->root;
    gnode->prev = NULL;
    - purplebuddylist->root = gnode;
    + priv->root = gnode;
    }
    - if (ops && ops->save_node) {
    - ops->save_node(gnode);
    - for (node = gnode->child; node; node = node->next)
    - ops->save_node(node);
    + if (klass && klass->save_node) {
    + klass->save_node(purplebuddylist, gnode);
    + for (node = gnode->child; node; node = node->next) {
    + klass->save_node(purplebuddylist, node);
    + }
    }
    - if (ops && ops->update) {
    - ops->update(purplebuddylist, gnode);
    - for (node = gnode->child; node; node = node->next)
    - ops->update(purplebuddylist, node);
    + if (klass && klass->update) {
    + klass->update(purplebuddylist, gnode);
    + for (node = gnode->child; node; node = node->next) {
    + klass->update(purplebuddylist, node);
    + }
    }
    purple_signal_emit(purple_blist_get_handle(), "blist-node-added",
    @@ -1308,12 +1361,14 @@
    void purple_blist_remove_contact(PurpleContact *contact)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    PurpleBlistNode *node, *gnode;
    PurpleGroup *group;
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    g_return_if_fail(PURPLE_IS_CONTACT(contact));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    node = (PurpleBlistNode *)contact;
    gnode = node->parent;
    group = PURPLE_GROUP(gnode);
    @@ -1344,11 +1399,13 @@
    purple_counting_node_change_total_size(PURPLE_COUNTING_NODE(group), -1);
    /* Update the UI */
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, node);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist, node);
    + }
    - if (ops && ops->remove_node)
    - ops->remove_node(node);
    + if (klass && klass->remove_node) {
    + klass->remove_node(purplebuddylist, node);
    + }
    purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
    PURPLE_BLIST_NODE(contact));
    @@ -1360,9 +1417,8 @@
    void purple_blist_remove_buddy(PurpleBuddy *buddy)
    {
    - PurpleBuddyListPrivate *priv =
    - purple_buddy_list_get_instance_private(purplebuddylist);
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    + PurpleBuddyListPrivate *priv = NULL;
    PurpleBlistNode *node, *cnode, *gnode;
    PurpleCountingNode *contact_counter, *group_counter;
    PurpleContact *contact;
    @@ -1371,8 +1427,11 @@
    GHashTable *account_buddies;
    PurpleAccount *account;
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    g_return_if_fail(PURPLE_IS_BUDDY(buddy));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + priv = purple_buddy_list_get_instance_private(purplebuddylist);
    account = purple_buddy_get_account(buddy);
    node = PURPLE_BLIST_NODE(buddy);
    cnode = node->parent;
    @@ -1409,8 +1468,9 @@
    if (cnode->child && purple_contact_get_priority_buddy(contact) == buddy) {
    purple_contact_invalidate_priority_buddy(contact);
    - if (ops && ops->update)
    - ops->update(purplebuddylist, cnode);
    + if (klass && klass->update) {
    + klass->update(purplebuddylist, cnode);
    + }
    }
    }
    @@ -1424,11 +1484,13 @@
    g_hash_table_remove(account_buddies, &hb);
    /* Update the UI */
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, node);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist, node);
    + }
    - if (ops && ops->remove_node)
    - ops->remove_node(node);
    + if (klass && klass->remove_node) {
    + klass->remove_node(purplebuddylist, node);
    + }
    /* Remove this buddy's pounces */
    purple_pounce_destroy_all_by_buddy(buddy);
    @@ -1446,13 +1508,15 @@
    void purple_blist_remove_chat(PurpleChat *chat)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    PurpleBlistNode *node, *gnode;
    PurpleGroup *group;
    PurpleCountingNode *group_counter;
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    g_return_if_fail(PURPLE_IS_CHAT(chat));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    node = (PurpleBlistNode *)chat;
    gnode = node->parent;
    group = (PurpleGroup *)gnode;
    @@ -1477,11 +1541,13 @@
    }
    /* Update the UI */
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, node);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist, node);
    + }
    - if (ops && ops->remove_node)
    - ops->remove_node(node);
    + if (klass && klass->remove_node) {
    + klass->remove_node(purplebuddylist, node);
    + }
    purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
    PURPLE_BLIST_NODE(chat));
    @@ -1492,16 +1558,20 @@
    void purple_blist_remove_group(PurpleGroup *group)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    + PurpleBuddyListPrivate *priv = NULL;
    PurpleBlistNode *node;
    GList *l;
    gchar* key;
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    g_return_if_fail(PURPLE_IS_GROUP(group));
    if (group == purple_blist_get_default_group())
    purple_debug_warning("buddylist", "cannot remove default group");
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + priv = purple_buddy_list_get_instance_private(purplebuddylist);
    node = (PurpleBlistNode *)group;
    /* Make sure the group is empty */
    @@ -1509,8 +1579,9 @@
    return;
    /* Remove the node from its parent */
    - if (purplebuddylist->root == node)
    - purplebuddylist->root = node->next;
    + if (priv->root == node) {
    + priv->root = node->next;
    + }
    if (node->prev)
    node->prev->next = node->next;
    if (node->next)
    @@ -1521,11 +1592,13 @@
    g_free(key);
    /* Update the UI */
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, node);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist, node);
    + }
    - if (ops && ops->remove_node)
    - ops->remove_node(node);
    + if (klass && klass->remove_node) {
    + klass->remove_node(purplebuddylist, node);
    + }
    purple_signal_emit(purple_blist_get_handle(), "blist-node-removed",
    PURPLE_BLIST_NODE(group));
    @@ -1558,7 +1631,7 @@
    hb.account = account;
    hb.name = (gchar *)purple_normalize(account, name);
    - for (group = purplebuddylist->root; group; group = group->next) {
    + for (group = priv->root; group; group = group->next) {
    if (!group->child)
    continue;
    @@ -1614,7 +1687,7 @@
    hb.name = (gchar *)purple_normalize(account, name);
    hb.account = account;
    - for (node = purplebuddylist->root; node != NULL; node = node->next) {
    + for (node = priv->root; node != NULL; node = node->next) {
    if (!node->child)
    continue;
    @@ -1687,11 +1760,12 @@
    protocol = purple_protocols_find(purple_account_get_protocol_id(account));
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, find_blist_chat))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, find_blist_chat))
    return purple_protocol_client_iface_find_blist_chat(protocol, account, name);
    normname = g_strdup(purple_normalize(account, name));
    - for (group = purplebuddylist->root; group != NULL; group = group->next) {
    + for (group = purple_blist_get_default_root(); group != NULL;
    + group = group->next) {
    for (node = group->child; node != NULL; node = node->next) {
    if (PURPLE_IS_CHAT(node)) {
    @@ -1706,8 +1780,7 @@
    pce = parts->data;
    chat_name = g_hash_table_lookup(purple_chat_get_components(chat),
    pce->identifier);
    - g_list_foreach(parts, (GFunc)g_free, NULL);
    - g_list_free(parts);
    + g_list_free_full(parts, g_free);
    if (purple_chat_get_account(chat) == account && chat_name != NULL &&
    purple_strequal(purple_normalize(account, chat_name), normname)) {
    @@ -1724,16 +1797,19 @@
    void purple_blist_add_account(PurpleAccount *account)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    PurpleBlistNode *gnode, *cnode, *bnode;
    PurpleCountingNode *contact_counter, *group_counter;
    g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    - if (!ops || !ops->update)
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + if (!klass || !klass->update) {
    return;
    + }
    - for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = gnode->next) {
    if (!PURPLE_IS_GROUP(gnode))
    continue;
    for (cnode = gnode->child; cnode; cnode = cnode->next) {
    @@ -1748,29 +1824,34 @@
    purple_counting_node_change_current_size(contact_counter, +1);
    if (purple_counting_node_get_current_size(contact_counter) == 1)
    purple_counting_node_change_current_size(group_counter, +1);
    - ops->update(purplebuddylist, bnode);
    - }
    - }
    - if (recompute ||
    - purple_blist_node_get_bool(cnode, "show_offline")) {
    - purple_contact_invalidate_priority_buddy((PurpleContact*)cnode);
    - ops->update(purplebuddylist, cnode);
    - }
    + klass->update(
    + purplebuddylist,
    + bnode);
    + }
    + }
    + if (recompute ||
    + purple_blist_node_get_bool(
    + cnode, "show_offline")) {
    + purple_contact_invalidate_priority_buddy(
    + (PurpleContact *)cnode);
    + klass->update(purplebuddylist,
    + cnode);
    + }
    } else if (PURPLE_IS_CHAT(cnode) &&
    purple_chat_get_account(PURPLE_CHAT(cnode)) == account) {
    group_counter = PURPLE_COUNTING_NODE(gnode);
    purple_counting_node_change_online_count(group_counter, +1);
    purple_counting_node_change_current_size(group_counter, +1);
    - ops->update(purplebuddylist, cnode);
    + klass->update(purplebuddylist, cnode);
    }
    }
    - ops->update(purplebuddylist, gnode);
    + klass->update(purplebuddylist, gnode);
    }
    }
    void purple_blist_remove_account(PurpleAccount *account)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleBuddyListClass *klass = NULL;
    PurpleBlistNode *gnode, *cnode, *bnode;
    PurpleCountingNode *contact_counter, *group_counter;
    PurpleBuddy *buddy;
    @@ -1780,8 +1861,10 @@
    GList *list = NULL, *iter = NULL;
    g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    - for (gnode = purplebuddylist->root; gnode; gnode = gnode->next) {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = gnode->next) {
    if (!PURPLE_IS_GROUP(gnode))
    continue;
    @@ -1825,16 +1908,20 @@
    else
    recompute = TRUE;
    - if (ops && ops->remove) {
    - ops->remove(purplebuddylist, bnode);
    + if (klass && klass->remove) {
    + klass->remove(
    + purplebuddylist,
    + bnode);
    }
    }
    }
    if (recompute) {
    purple_contact_invalidate_priority_buddy(contact);
    - if (ops && ops->update)
    - ops->update(purplebuddylist, cnode);
    + if (klass && klass->update) {
    + klass->update(purplebuddylist,
    + cnode);
    + }
    }
    } else if (PURPLE_IS_CHAT(cnode)) {
    chat = PURPLE_CHAT(cnode);
    @@ -1844,8 +1931,10 @@
    purple_counting_node_change_current_size(group_counter, -1);
    purple_counting_node_change_online_count(group_counter, -1);
    - if (ops && ops->remove)
    - ops->remove(purplebuddylist, cnode);
    + if (klass && klass->remove) {
    + klass->remove(purplebuddylist,
    + cnode);
    + }
    }
    }
    }
    @@ -1867,7 +1956,8 @@
    {
    PurpleBlistNode *group = NULL, *meta_contact = NULL, *contact = NULL;
    - for(group = purplebuddylist->root; group != NULL; group = group->next) {
    + for (group = purple_blist_get_default_root(); group != NULL;
    + group = group->next) {
    if(group_func != NULL) {
    group_func(group, data);
    }
    @@ -1897,71 +1987,104 @@
    purple_blist_request_add_buddy(PurpleAccount *account, const char *username,
    const char *group, const char *alias)
    {
    - PurpleBlistUiOps *ui_ops;
    + PurpleBuddyListClass *klass = NULL;
    +
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    - ui_ops = purple_blist_get_ui_ops();
    -
    - if (ui_ops != NULL && ui_ops->request_add_buddy != NULL)
    - ui_ops->request_add_buddy(account, username, group, alias);
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + if (klass != NULL && klass->request_add_buddy != NULL) {
    + klass->request_add_buddy(purplebuddylist, account, username,
    + group, alias);
    + }
    }
    void
    purple_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
    const char *alias, const char *name)
    {
    - PurpleBlistUiOps *ui_ops;
    + PurpleBuddyListClass *klass = NULL;
    +
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    - ui_ops = purple_blist_get_ui_ops();
    -
    - if (ui_ops != NULL && ui_ops->request_add_chat != NULL)
    - ui_ops->request_add_chat(account, group, alias, name);
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + if (klass != NULL && klass->request_add_chat != NULL) {
    + klass->request_add_chat(purplebuddylist, account, group, alias,
    + name);
    + }
    }
    void
    purple_blist_request_add_group(void)
    {
    - PurpleBlistUiOps *ui_ops;
    + PurpleBuddyListClass *klass = NULL;
    +
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(purplebuddylist));
    +
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(purplebuddylist);
    + if (klass != NULL && klass->request_add_group != NULL) {
    + klass->request_add_group(purplebuddylist);
    + }
    +}
    - ui_ops = purple_blist_get_ui_ops();
    +void
    +purple_blist_new_node(PurpleBuddyList *list, PurpleBlistNode *node)
    +{
    + PurpleBuddyListClass *klass = NULL;
    - if (ui_ops != NULL && ui_ops->request_add_group != NULL)
    - ui_ops->request_add_group();
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
    +
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
    + if (klass && klass->new_node) {
    + klass->new_node(list, node);
    + }
    }
    void
    -purple_blist_set_ui_ops(PurpleBlistUiOps *ops)
    +purple_blist_update_node(PurpleBuddyList *list, PurpleBlistNode *node)
    {
    - gboolean overrode = FALSE;
    - blist_ui_ops = ops;
    -
    - if (!ops)
    - return;
    + PurpleBuddyListClass *klass = NULL;
    - if (!ops->save_node) {
    - ops->save_node = purple_blist_save_node;
    - overrode = TRUE;
    - }
    - if (!ops->remove_node) {
    - ops->remove_node = purple_blist_save_node;
    - overrode = TRUE;
    - }
    - if (!ops->save_account) {
    - ops->save_account = purple_blist_save_account;
    - overrode = TRUE;
    - }
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
    - if (overrode && (ops->save_node != purple_blist_save_node ||
    - ops->remove_node != purple_blist_save_node ||
    - ops->save_account != purple_blist_save_account)) {
    - purple_debug_warning("buddylist", "Only some of the blist saving UI ops "
    - "were overridden. This probably is not what you want!\n");
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
    + if (klass && klass->update) {
    + klass->update(list, node);
    }
    }
    -PurpleBlistUiOps *
    -purple_blist_get_ui_ops(void)
    +void
    +purple_blist_save_node(PurpleBuddyList *list, PurpleBlistNode *node)
    +{
    + PurpleBuddyListClass *klass = NULL;
    +
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
    +
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
    + if (klass && klass->save_node) {
    + klass->save_node(list, node);
    + }
    +}
    +
    +void
    +purple_blist_save_account(PurpleBuddyList *list, PurpleAccount *account)
    {
    - return blist_ui_ops;
    + PurpleBuddyListClass *klass = NULL;
    +
    + /* XXX: There's a chicken and egg problem with the accounts api, where
    + * it'll call this function before purple_blist_init is called, this will
    + * cause the following g_return_if_fail to fail, and muck up the logs. We
    + * need to find a better fix for this, but this gets rid of it for now.
    + */
    + if(G_UNLIKELY(list == NULL && purplebuddylist == NULL)) {
    + return;
    + }
    +
    + g_return_if_fail(PURPLE_IS_BUDDY_LIST(list));
    +
    + klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
    + if (klass && klass->save_account) {
    + klass->save_account(list, account);
    + }
    }
    const gchar *
    @@ -1983,6 +2106,9 @@
    {
    void *handle = purple_blist_get_handle();
    + /* Set a default, which can't be done as a static initializer. */
    + buddy_list_type = PURPLE_TYPE_BUDDY_LIST;
    +
    purple_signal_register(handle, "buddy-status-changed",
    purple_marshal_VOID__POINTER_POINTER_POINTER,
    G_TYPE_NONE, 3, PURPLE_TYPE_BUDDY, PURPLE_TYPE_STATUS,
    @@ -2051,16 +2177,15 @@
    }
    static void
    -blist_node_destroy(PurpleBlistNode *node)
    +blist_node_destroy(PurpleBuddyListClass *klass, PurpleBuddyList *list,
    + PurpleBlistNode *node)
    {
    - PurpleBlistUiOps *ui_ops;
    PurpleBlistNode *child, *next_child;
    - ui_ops = purple_blist_get_ui_ops();
    child = node->child;
    while (child) {
    next_child = child->next;
    - blist_node_destroy(child);
    + blist_node_destroy(klass, list, child);
    child = next_child;
    }
    @@ -2069,8 +2194,9 @@
    node->child = NULL;
    node->next = NULL;
    node->prev = NULL;
    - if (ui_ops && ui_ops->remove)
    - ui_ops->remove(purplebuddylist, node);
    + if (klass && klass->remove) {
    + klass->remove(list, node);
    + }
    g_object_unref(node);
    }
    @@ -2078,9 +2204,6 @@
    void
    purple_blist_uninit(void)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    - PurpleBlistNode *node, *next_node;
    -
    /* This happens if we quit before purple_set_blist is called. */
    if (purplebuddylist == NULL)
    return;
    @@ -2093,25 +2216,13 @@
    purple_debug(PURPLE_DEBUG_INFO, "buddylist", "Destroying\n");
    - if (ops && ops->destroy)
    - ops->destroy(purplebuddylist);
    -
    - node = purple_blist_get_root();
    - while (node) {
    - next_node = node->next;
    - blist_node_destroy(node);
    - node = next_node;
    - }
    - purplebuddylist->root = NULL;
    -
    g_hash_table_destroy(buddies_cache);
    g_hash_table_destroy(groups_cache);
    buddies_cache = NULL;
    groups_cache = NULL;
    - g_object_unref(purplebuddylist);
    - purplebuddylist = NULL;
    + g_clear_object(&purplebuddylist);
    g_free(localized_default_group_name);
    localized_default_group_name = NULL;
    @@ -2121,36 +2232,6 @@
    }
    /**************************************************************************
    - * GBoxed code
    - **************************************************************************/
    -static PurpleBlistUiOps *
    -purple_blist_ui_ops_copy(PurpleBlistUiOps *ops)
    -{
    - PurpleBlistUiOps *ops_new;
    -
    - g_return_val_if_fail(ops != NULL, NULL);
    -
    - ops_new = g_new(PurpleBlistUiOps, 1);
    - *ops_new = *ops;
    -
    - return ops_new;
    -}
    -
    -GType
    -purple_blist_ui_ops_get_type(void)
    -{
    - static GType type = 0;
    -
    - if (type == 0) {
    - type = g_boxed_type_register_static("PurpleBlistUiOps",
    - (GBoxedCopyFunc)purple_blist_ui_ops_copy,
    - (GBoxedFreeFunc)g_free);
    - }
    -
    - return type;
    -}
    -
    -/**************************************************************************
    * GObject code
    **************************************************************************/
    @@ -2171,10 +2252,22 @@
    static void
    purple_buddy_list_finalize(GObject *object)
    {
    - PurpleBuddyListPrivate *priv = purple_buddy_list_get_instance_private(
    - PURPLE_BUDDY_LIST(object));
    + PurpleBuddyList *list = PURPLE_BUDDY_LIST(object);
    + PurpleBuddyListClass *klass = PURPLE_BUDDY_LIST_GET_CLASS(list);
    + PurpleBuddyListPrivate *priv =
    + purple_buddy_list_get_instance_private(list);
    + PurpleBlistNode *node, *next_node;
    +
    g_hash_table_destroy(priv->buddies);
    + node = priv->root;
    + while (node) {
    + next_node = node->next;
    + blist_node_destroy(klass, list, node);
    + node = next_node;
    + }
    + priv->root = NULL;
    +
    G_OBJECT_CLASS(purple_buddy_list_parent_class)->finalize(object);
    }
    @@ -2184,5 +2277,8 @@
    GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    obj_class->finalize = purple_buddy_list_finalize;
    +
    + klass->save_node = purple_blist_real_save_node;
    + klass->remove_node = purple_blist_real_save_node;
    + klass->save_account = purple_blist_real_save_account;
    }
    -
    --- a/libpurple/buddylist.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/buddylist.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_BUDDY_LIST_H_
    -#define _PURPLE_BUDDY_LIST_H_
    +#ifndef PURPLE_BUDDY_LIST_H
    +#define PURPLE_BUDDY_LIST_H
    +
    /**
    * SECTION:buddylist
    * @section_id: libpurple-buddylist
    @@ -33,19 +34,8 @@
    #include "buddy.h"
    -#define PURPLE_TYPE_BUDDY_LIST (purple_buddy_list_get_type())
    -#define PURPLE_BUDDY_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_BUDDY_LIST, PurpleBuddyList))
    -#define PURPLE_BUDDY_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_BUDDY_LIST, PurpleBuddyListClass))
    -#define PURPLE_IS_BUDDY_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_BUDDY_LIST))
    -#define PURPLE_IS_BUDDY_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_BUDDY_LIST))
    -#define PURPLE_BUDDY_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_BUDDY_LIST, PurpleBuddyListClass))
    -
    -typedef struct _PurpleBuddyList PurpleBuddyList;
    -typedef struct _PurpleBuddyListClass PurpleBuddyListClass;
    -
    -#define PURPLE_TYPE_BLIST_UI_OPS (purple_blist_ui_ops_get_type())
    -
    -typedef struct _PurpleBlistUiOps PurpleBlistUiOps;
    +#define PURPLE_TYPE_BUDDY_LIST (purple_buddy_list_get_type())
    +typedef struct _PurpleBuddyList PurpleBuddyList;
    #define PURPLE_BLIST_DEFAULT_GROUP_NAME _("Buddies")
    @@ -67,55 +57,32 @@
    /**************************************************************************/
    /**
    * PurpleBuddyList:
    - * @root: The first node in the buddy list
    - * @ui_data: The UI data associated with this buddy list. This is a convenience
    - * field provided to the UIs -- it is not used by the libpurple core.
    *
    * The Buddy List
    */
    -struct _PurpleBuddyList {
    - GObject gparent;
    -
    - /*< public >*/
    - PurpleBlistNode *root;
    - gpointer ui_data;
    -};
    -
    -struct _PurpleBuddyListClass {
    - GObjectClass gparent_class;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    /**
    - * PurpleBlistUiOps:
    - * @new_list: Sets UI-specific data on a buddy list.
    + * PurpleBuddyListClass:
    * @new_node: Sets UI-specific data on a node.
    * @show: The core will call this when it's finished doing its core
    - * stuff
    + * stuff.
    * @update: This will update a node in the buddy list.
    * @remove: This removes a node from the list
    - * @destroy: When the list is destroyed, this is called to destroy the UI.
    - * @set_visible: Hides or unhides the buddy list
    + * @set_visible: Hides or unhides the buddy list.
    * @save_node: This is called when a node has been modified and should be
    * saved.
    - * <sbr/>Implementation of this UI op is
    + * <sbr/>Implementation of this method is
    * <emphasis>OPTIONAL</emphasis>. If not implemented, it will be
    * set to a fallback function that saves data to
    * <filename>blist.xml</filename> like in previous libpurple
    * versions.
    * <sbr/>@node: The node which has been modified.
    * @remove_node: Called when a node is about to be removed from the buddy list.
    - * The UI op should update the relevant data structures to remove
    - * this node (for example, removing a buddy from the group this
    - * node is in).
    - * <sbr/>Implementation of this UI op is
    - * <emphasis>OPTIONAL</emphasis>. If not implemented,
    - * it will be set to a fallback function that saves data to
    + * The method should update the relevant data structures to
    + * remove this node (for example, removing a buddy from the
    + * group this node is in).
    + * <sbr/>Implementation of this method is
    + * <emphasis>OPTIONAL</emphasis>. If not implemented, it will be
    + * set to a fallback function that saves data to
    * <filename>blist.xml</filename> like in previous libpurple
    * versions.
    * <sbr/>@node: The node which has been modified.
    @@ -123,7 +90,7 @@
    * this, the callback must save the privacy and buddy list data
    * for an account. If the account is %NULL, save the data for all
    * accounts.
    - * <sbr/>Implementation of this UI op is
    + * <sbr/>Implementation of this method is
    * <emphasis>OPTIONAL</emphasis>. If not implemented, it will be
    * set to a fallback function that saves data to
    * <filename>blist.xml</filename> like in previous
    @@ -131,39 +98,40 @@
    * <sbr/>@account: The account whose data to save. If %NULL,
    * save all data for all accounts.
    *
    - * Buddy list UI operations.
    + * Buddy list operations.
    *
    - * Any UI representing a buddy list must assign a filled-out PurpleBlistUiOps
    - * structure to the buddy list core.
    + * Any UI representing a buddy list must derive a filled-out
    + * @PurpleBuddyListClass and set the GType using purple_blist_set_ui() before a
    + * buddy list is created.
    */
    -struct _PurpleBlistUiOps
    -{
    - void (*new_list)(PurpleBuddyList *list);
    - void (*new_node)(PurpleBlistNode *node);
    +struct _PurpleBuddyListClass {
    + /*< private >*/
    + GObjectClass gparent_class;
    +
    + /*< public >*/
    + void (*new_node)(PurpleBuddyList *list, PurpleBlistNode *node);
    void (*show)(PurpleBuddyList *list);
    void (*update)(PurpleBuddyList *list, PurpleBlistNode *node);
    void (*remove)(PurpleBuddyList *list, PurpleBlistNode *node);
    - void (*destroy)(PurpleBuddyList *list);
    void (*set_visible)(PurpleBuddyList *list, gboolean show);
    - void (*request_add_buddy)(PurpleAccount *account, const char *username,
    - const char *group, const char *alias);
    -
    - void (*request_add_chat)(PurpleAccount *account, PurpleGroup *group,
    - const char *alias, const char *name);
    + void (*request_add_buddy)(PurpleBuddyList *list, PurpleAccount *account,
    + const char *username, const char *group,
    + const char *alias);
    - void (*request_add_group)(void);
    + void (*request_add_chat)(PurpleBuddyList *list, PurpleAccount *account,
    + PurpleGroup *group, const char *alias,
    + const char *name);
    - void (*save_node)(PurpleBlistNode *node);
    - void (*remove_node)(PurpleBlistNode *node);
    + void (*request_add_group)(PurpleBuddyList *list);
    - void (*save_account)(PurpleAccount *account);
    + void (*save_node)(PurpleBuddyList *list, PurpleBlistNode *node);
    + void (*remove_node)(PurpleBuddyList *list, PurpleBlistNode *node);
    +
    + void (*save_account)(PurpleBuddyList *list, PurpleAccount *account);
    /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    + gpointer reserved[4];
    };
    G_BEGIN_DECLS
    @@ -177,25 +145,40 @@
    *
    * Returns: The #GType for the #PurpleBuddyList object.
    */
    -GType purple_buddy_list_get_type(void);
    +G_DECLARE_DERIVABLE_TYPE(PurpleBuddyList, purple_buddy_list, PURPLE, BUDDY_LIST,
    + GObject)
    /**
    - * purple_blist_get_buddy_list:
    + * purple_blist_get_default:
    *
    - * Returns the main buddy list.
    + * Returns the default buddy list.
    + *
    + * Returns: (transfer none): The default buddy list.
    *
    - * Returns: The main buddy list.
    + * Since: 3.0.0
    */
    -PurpleBuddyList *purple_blist_get_buddy_list(void);
    +PurpleBuddyList *purple_blist_get_default(void);
    +
    +/**
    + * purple_blist_get_default_root:
    + *
    + * Returns the root node of the default buddy list.
    + *
    + * Returns: (transfer none): The root node.
    + *
    + * Since: 3.0.0
    + */
    +PurpleBlistNode *purple_blist_get_default_root(void);
    /**
    * purple_blist_get_root:
    + * @list: The buddy list to query.
    *
    - * Returns the root node of the main buddy list.
    + * Returns the root node of the specified buddy list.
    *
    - * Returns: The root node.
    + * Returns: (transfer none): The root node.
    */
    -PurpleBlistNode *purple_blist_get_root(void);
    +PurpleBlistNode *purple_blist_get_root(PurpleBuddyList *list);
    /**
    * purple_blist_get_buddies:
    @@ -212,23 +195,6 @@
    GSList *purple_blist_get_buddies(void);
    /**
    - * purple_blist_get_ui_data:
    - *
    - * Returns the UI data for the list.
    - *
    - * Returns: The UI data for the list.
    - */
    -gpointer purple_blist_get_ui_data(void);
    -
    -/**
    - * purple_blist_set_ui_data:
    - * @ui_data: The UI data for the list.
    - *
    - * Sets the UI data for the list.
    - */
    -void purple_blist_set_ui_data(gpointer ui_data);
    -
    -/**
    * purple_blist_show:
    *
    * Shows the buddy list, creating a new one if necessary.
    @@ -367,7 +333,7 @@
    *
    * Finds the buddy struct given a name and an account
    *
    - * Returns: The buddy or NULL if the buddy does not exist
    + * Returns: (transfer none): The buddy or %NULL if the buddy does not exist.
    */
    PurpleBuddy *purple_blist_find_buddy(PurpleAccount *account, const char *name);
    @@ -379,7 +345,8 @@
    *
    * Finds the buddy struct given a name, an account, and a group
    *
    - * Returns: The buddy or NULL if the buddy does not exist in the group
    + * Returns: (transfer none): The buddy or %NULL if the buddy does not exist in
    + * the group.
    */
    PurpleBuddy *purple_blist_find_buddy_in_group(PurpleAccount *account, const char *name,
    PurpleGroup *group);
    @@ -402,7 +369,7 @@
    *
    * Finds a group by name
    *
    - * Returns: The group or NULL if the group does not exist
    + * Returns: (transfer none): The group or %NULL if the group does not exist.
    */
    PurpleGroup *purple_blist_find_group(const char *name);
    @@ -411,7 +378,7 @@
    *
    * Finds or creates default group.
    *
    - * Returns: The default group.
    + * Returns: (transfer none): The default group.
    */
    PurpleGroup *purple_blist_get_default_group(void);
    @@ -422,7 +389,7 @@
    *
    * Finds a chat by name.
    *
    - * Returns: The chat, or %NULL if the chat does not exist.
    + * Returns: (transfer none): The chat, or %NULL if the chat does not exist.
    */
    PurpleChat *purple_blist_find_chat(PurpleAccount *account, const char *name);
    @@ -508,39 +475,78 @@
    */
    void purple_blist_request_add_group(void);
    -/**************************************************************************/
    -/* UI Registration Functions */
    -/**************************************************************************/
    +/**
    + * purple_blist_new_node:
    + * @list: The list that contains the node.
    + * @node: The node to initialize.
    + *
    + * Sets UI-specific data on a node.
    + *
    + * This should usually only be run when initializing a @PurpleBlistNode
    + * instance.
    + *
    + * Since: 3.0.0
    + */
    +void purple_blist_new_node(PurpleBuddyList *list, PurpleBlistNode *node);
    /**
    - * purple_blist_ui_ops_get_type:
    + * purple_blist_update_node:
    + * @list: The buddy list to modify.
    + * @node: The node to update.
    *
    - * Returns: The #GType for the #PurpleBlistUiOps boxed structure.
    + * Update a node in the buddy list in the UI.
    + *
    + * Since: 3.0.0
    */
    -GType purple_blist_ui_ops_get_type(void);
    +void purple_blist_update_node(PurpleBuddyList *list, PurpleBlistNode *node);
    /**
    - * purple_blist_set_ui_ops:
    - * @ops: The ops struct.
    + * purple_blist_save_node:
    + * @list: The list that contains the node.
    + * @node: The node which has been modified.
    + *
    + * This is called when a node has been modified and should be saved by the UI.
    *
    - * Sets the UI operations structure to be used for the buddy list.
    + * If the UI does not implement a more specific method, it will be set to save
    + * data to <filename>blist.xml</filename> like in previous libpurple versions.
    + *
    + * Since: 3.0.0
    */
    -void purple_blist_set_ui_ops(PurpleBlistUiOps *ops);
    +void purple_blist_save_node(PurpleBuddyList *list, PurpleBlistNode *node);
    /**
    - * purple_blist_get_ui_ops:
    + * purple_blist_save_account:
    + * @list: The list that contains the account.
    + * @account: The account whose data to save. If %NULL, save all data for all
    + * accounts.
    *
    - * Returns the UI operations structure to be used for the buddy list.
    + * Save all the data for an account.
    *
    - * Returns: The UI operations structure.
    + * If the UI does not set a more specific method, it will be set to save data
    + * to <filename>blist.xml</filename> like in previous libpurple versions.
    + *
    + * Since: 3.0.0
    */
    -PurpleBlistUiOps *purple_blist_get_ui_ops(void);
    +void purple_blist_save_account(PurpleBuddyList *list, PurpleAccount *account);
    /**************************************************************************/
    /* Buddy List Subsystem */
    /**************************************************************************/
    /**
    + * purple_blist_set_ui:
    + * @type: The @GType of a derived UI implementation of @PurpleBuddyList.
    + *
    + * Set the UI implementation of the buddy list.
    + *
    + * This must be called before the buddy list is created or you will get the
    + * default libpurple implementation.
    + *
    + * Since: 3.0.0
    + */
    +void purple_blist_set_ui(GType type);
    +
    +/**
    * purple_blist_get_handle:
    *
    * Returns the handle for the buddy list subsystem.
    @@ -574,4 +580,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_BUDDY_LIST_H_ */
    +#endif /* PURPLE_BUDDY_LIST_H */
    --- a/libpurple/chat.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/chat.c Tue Oct 08 21:48:28 2019 -0500
    @@ -60,9 +60,11 @@
    const char *purple_chat_get_name(PurpleChat *chat)
    {
    - PurpleChatPrivate *priv = purple_chat_get_instance_private(chat);
    + PurpleChatPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT(chat), NULL);
    +
    + priv = purple_chat_get_instance_private(chat);
    if ((priv->alias != NULL) && (*priv->alias != '\0'))
    return priv->alias;
    @@ -72,21 +74,22 @@
    const char *purple_chat_get_name_only(PurpleChat *chat)
    {
    + PurpleChatPrivate *priv = NULL;
    char *ret = NULL;
    PurpleProtocol *protocol = NULL;
    - PurpleChatPrivate *priv = purple_chat_get_instance_private(chat);
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT(chat), NULL);
    +
    + priv = purple_chat_get_instance_private(chat);
    protocol = purple_protocols_find(purple_account_get_protocol_id(priv->account));
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info)) {
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info)) {
    PurpleProtocolChatEntry *pce;
    GList *parts = purple_protocol_chat_iface_info(protocol, purple_account_get_connection(priv->account));
    pce = parts->data;
    ret = g_hash_table_lookup(priv->components, pce->identifier);
    - g_list_foreach(parts, (GFunc)g_free, NULL);
    - g_list_free(parts);
    + g_list_free_full(parts, g_free);
    }
    return ret;
    @@ -95,12 +98,13 @@
    void
    purple_chat_set_alias(PurpleChat *chat, const char *alias)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleChatPrivate *priv = NULL;
    char *old_alias;
    char *new_alias = NULL;
    - PurpleChatPrivate *priv = purple_chat_get_instance_private(chat);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT(chat));
    +
    + priv = purple_chat_get_instance_private(chat);
    if ((alias != NULL) && (*alias != '\0'))
    new_alias = purple_utf8_strip_unprintables(alias);
    @@ -121,12 +125,10 @@
    g_object_notify_by_pspec(G_OBJECT(chat), properties[PROP_ALIAS]);
    - if (ops) {
    - if (ops->save_node)
    - ops->save_node(PURPLE_BLIST_NODE(chat));
    - if (ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(chat));
    - }
    + purple_blist_save_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(chat));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(chat));
    purple_signal_emit(purple_blist_get_handle(), "blist-node-aliased",
    chat, old_alias);
    @@ -144,20 +146,22 @@
    PurpleAccount *
    purple_chat_get_account(PurpleChat *chat)
    {
    - PurpleChatPrivate *priv = purple_chat_get_instance_private(chat);
    + PurpleChatPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT(chat), NULL);
    + priv = purple_chat_get_instance_private(chat);
    return priv->account;
    }
    GHashTable *
    purple_chat_get_components(PurpleChat *chat)
    {
    - PurpleChatPrivate *priv = purple_chat_get_instance_private(chat);
    + PurpleChatPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT(chat), NULL);
    + priv = purple_chat_get_instance_private(chat);
    return priv->components;
    }
    @@ -226,12 +230,11 @@
    {
    PurpleChat *chat = PURPLE_CHAT(object);
    PurpleChatPrivate *priv = purple_chat_get_instance_private(chat);
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    G_OBJECT_CLASS(purple_chat_parent_class)->constructed(object);
    - if (ops != NULL && ops->new_node != NULL)
    - ops->new_node(PURPLE_BLIST_NODE(chat));
    + purple_blist_new_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(chat));
    priv->is_constructed = TRUE;
    }
    --- a/libpurple/chat.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/chat.h Tue Oct 08 21:48:28 2019 -0500
    @@ -119,7 +119,8 @@
    *
    * Returns the group of which the chat is a member.
    *
    - * Returns: The parent group, or %NULL if the chat is not in a group.
    + * Returns: (transfer none): The parent group, or %NULL if the chat is not in a
    + * group.
    */
    PurpleGroup *purple_chat_get_group(PurpleChat *chat);
    @@ -129,7 +130,7 @@
    *
    * Returns the account the chat belongs to.
    *
    - * Returns: The account the chat belongs to.
    + * Returns: (transfer none): The account the chat belongs to.
    */
    PurpleAccount *purple_chat_get_account(PurpleChat *chat);
    --- a/libpurple/circularbuffer.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/circularbuffer.h Tue Oct 08 21:48:28 2019 -0500
    @@ -29,20 +29,12 @@
    #include <glib.h>
    #include <glib-object.h>
    -#define PURPLE_TYPE_CIRCULAR_BUFFER (purple_circular_buffer_get_type())
    -#define PURPLE_CIRCULAR_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_CIRCULAR_BUFFER, PurpleCircularBuffer))
    -#define PURPLE_CIRCULAR_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_CIRCULAR_BUFFER, PurpleCircularBufferClass))
    -#define PURPLE_IS_CIRCULAR_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_CIRCULAR_BUFFER))
    -#define PURPLE_IS_CIRCULAR_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_CIRCULAR_BUFFER))
    -#define PURPLE_CIRCULAR_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_CIRCULAR_BUFFER, PurpleCircularBufferClass))
    +G_BEGIN_DECLS
    -typedef struct _PurpleCircularBuffer PurpleCircularBuffer;
    -typedef struct _PurpleCircularBufferClass PurpleCircularBufferClass;
    +#define PURPLE_TYPE_CIRCULAR_BUFFER (purple_circular_buffer_get_type())
    -struct _PurpleCircularBuffer {
    - /*< private >*/
    - GObject parent;
    -};
    +G_DECLARE_DERIVABLE_TYPE(PurpleCircularBuffer, purple_circular_buffer, PURPLE,
    + CIRCULAR_BUFFER, GObject)
    struct _PurpleCircularBufferClass {
    /*< private >*/
    @@ -59,10 +51,6 @@
    void (*purple_reserved4)(void);
    };
    -G_BEGIN_DECLS
    -
    -GType purple_circular_buffer_get_type(void);
    -
    /**
    * purple_circular_buffer_new:
    * @growsize: The amount that the buffer should grow the first time data
    --- a/libpurple/cmds.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/cmds.h Tue Oct 08 21:48:28 2019 -0500
    @@ -16,8 +16,9 @@
    *
    */
    -#ifndef _PURPLE_CMDS_H_
    -#define _PURPLE_CMDS_H_
    +#ifndef PURPLE_CMDS_H
    +#define PURPLE_CMDS_H
    +
    /**
    * SECTION:cmds
    * @section_id: libpurple-cmds
    @@ -28,12 +29,20 @@
    #include "conversation.h"
    -/**************************************************************************/
    -/* Structures */
    -/**************************************************************************/
    +/******************************************************************************
    + * Structures
    + *****************************************************************************/
    /**
    * PurpleCmdStatus:
    + * @PURPLE_CMD_STATUS_OK: The command executed successfully.
    + * @PURPLE_CMD_STATUS_FAILED: The command failed to execute.
    + * @PURPLE_CMD_STATUS_NOT_FOUND: The command was not found.
    + * @PURPLE_CMD_STATUS_WRONG_ARGS: The wrong number of arguments were passed.
    + * @PURPLE_CMD_STATUS_WRONG_PROTOCOL: The command was run with the wrong
    + * protocol.
    + * @PURPLE_CMD_STATUS_WRONG_TYPE: The Command was ran against the wrong type of
    + * conversation.
    *
    * The possible results of running a command with purple_cmd_do_command().
    */
    @@ -48,9 +57,9 @@
    /**
    * PurpleCmdRet:
    - * @PURPLE_CMD_RET_OK: Everything's okay; Don't look for another command
    - * to call.
    - * @PURPLE_CMD_RET_FAILED: The command failed, but stop looking.
    + * @PURPLE_CMD_RET_OK: Everything's okay; Don't look for another command to
    + * call.
    + * @PURPLE_CMD_RET_FAILED: The command failed, but stop looking.
    * @PURPLE_CMD_RET_CONTINUE: Continue, looking for other commands with the same
    * name to call.
    *
    @@ -71,10 +80,14 @@
    /**
    * PurpleCmdFunc:
    + * @conversation: The #PurpleConversation where the command is being run.
    + * @cmd: The name of the command.
    + * @args: The arguments to the command.
    + * @error: (out): A return address for a #GError.
    + * @data: User data to pass to the function.
    *
    * A function implementing a command, as passed to purple_cmd_register().
    */
    -/* TODO document the arguments to these functions. */
    typedef PurpleCmdRet (*PurpleCmdFunc)(PurpleConversation *conversation, const gchar *cmd,
    gchar **args, gchar **error, void *data);
    /**
    @@ -86,6 +99,21 @@
    */
    typedef guint PurpleCmdId;
    +/**
    + * PurpleCmdPriority:
    + * @PURPLE_CMD_P_VERY_LOW: Lowest priority.
    + * @PURPLE_CMD_P_LOW: Low priority.
    + * @PURPLE_CMD_P_DEFAULT: Default priority.
    + * @PURPLE_CMD_P_PROTOCOL: Priority for protocol plugins.
    + * @PURPLE_CMD_P_PLUGIN: Priority for plugins.
    + * @PURPLE_CMD_P_ALIAS: Priority for aliasing commands.
    + * @PURPLE_CMD_P_HIGH: High priority.
    + * @PURPLE_CMD_P_VERY_HIGH: Highest priority.
    + *
    + * Commands are registered from multiple locations which leads to name
    + * collisions. PurpleCmdPriority is used to determine which command will be
    + * run.
    + */
    typedef enum {
    PURPLE_CMD_P_VERY_LOW = -1000,
    PURPLE_CMD_P_LOW = 0,
    @@ -338,4 +366,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_CMDS_H_ */
    +#endif /* PURPLE_CMDS_H */
    --- a/libpurple/connection.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/connection.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#define _PURPLE_CONNECTION_C_
    #include "internal.h"
    #include "glibcompat.h"
    @@ -133,9 +132,7 @@
    {
    PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    - g_return_if_fail(priv != NULL);
    -
    - if (!PURPLE_PROTOCOL_IMPLEMENTS(priv->protocol, SERVER_IFACE, keepalive))
    + if (!PURPLE_PROTOCOL_IMPLEMENTS(priv->protocol, SERVER, keepalive))
    return;
    if (on && !priv->keepalive)
    @@ -165,10 +162,12 @@
    void
    purple_connection_set_state(PurpleConnection *gc, PurpleConnectionState state)
    {
    + PurpleConnectionPrivate *priv = NULL;
    PurpleConnectionUiOps *ops;
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    +
    + priv = purple_connection_get_instance_private(gc);
    if (priv->state == state)
    return;
    @@ -256,10 +255,11 @@
    void
    purple_connection_set_flags(PurpleConnection *gc, PurpleConnectionFlags flags)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    + priv = purple_connection_get_instance_private(gc);
    priv->flags = flags;
    if (!priv->is_finalizing)
    @@ -269,10 +269,11 @@
    void
    purple_connection_set_display_name(PurpleConnection *gc, const char *name)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    + priv = purple_connection_get_instance_private(gc);
    g_free(priv->display_name);
    priv->display_name = g_strdup(name);
    @@ -282,130 +283,143 @@
    void
    purple_connection_set_protocol_data(PurpleConnection *gc, void *proto_data)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    + priv = purple_connection_get_instance_private(gc);
    priv->proto_data = proto_data;
    }
    PurpleConnectionState
    purple_connection_get_state(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, PURPLE_CONNECTION_DISCONNECTED);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), PURPLE_CONNECTION_DISCONNECTED);
    + priv = purple_connection_get_instance_private(gc);
    return priv->state;
    }
    PurpleConnectionFlags
    purple_connection_get_flags(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), 0);
    + priv = purple_connection_get_instance_private(gc);
    return priv->flags;
    }
    gboolean
    purple_connection_is_disconnecting(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, TRUE);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), TRUE);
    + priv = purple_connection_get_instance_private(gc);
    return priv->is_finalizing;
    }
    PurpleAccount *
    purple_connection_get_account(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
    + priv = purple_connection_get_instance_private(gc);
    return priv->account;
    }
    PurpleProtocol *
    purple_connection_get_protocol(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
    + priv = purple_connection_get_instance_private(gc);
    return priv->protocol;
    }
    const char *
    purple_connection_get_password(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
    + priv = purple_connection_get_instance_private(gc);
    return priv->password;
    }
    GSList *
    purple_connection_get_active_chats(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
    + priv = purple_connection_get_instance_private(gc);
    return priv->active_chats;
    }
    const char *
    purple_connection_get_display_name(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
    + priv = purple_connection_get_instance_private(gc);
    return priv->display_name;
    }
    void *
    purple_connection_get_protocol_data(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
    + priv = purple_connection_get_instance_private(gc);
    return priv->proto_data;
    }
    void
    _purple_connection_add_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    + priv = purple_connection_get_instance_private(gc);
    priv->active_chats = g_slist_append(priv->active_chats, chat);
    }
    void
    _purple_connection_remove_active_chat(PurpleConnection *gc, PurpleChatConversation *chat)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    + priv = purple_connection_get_instance_private(gc);
    priv->active_chats = g_slist_remove(priv->active_chats, chat);
    }
    gboolean
    _purple_connection_wants_to_die(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), FALSE);
    + priv = purple_connection_get_instance_private(gc);
    return priv->wants_to_die;
    }
    @@ -445,14 +459,15 @@
    {
    PurpleAccount *account;
    PurpleConnection *gc;
    - PurpleConnectionPrivate *priv;
    account = data;
    gc = purple_account_get_connection(account);
    - priv = purple_connection_get_instance_private(gc);
    - if (priv != NULL)
    + if (PURPLE_IS_CONNECTION(gc)) {
    + PurpleConnectionPrivate *priv;
    + priv = purple_connection_get_instance_private(gc);
    priv->disconnect_timeout = 0;
    + }
    purple_account_disconnect(account);
    return FALSE;
    @@ -463,10 +478,12 @@
    PurpleConnectionError reason,
    const char *description)
    {
    + PurpleConnectionPrivate *priv = NULL;
    PurpleConnectionUiOps *ops;
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    + priv = purple_connection_get_instance_private(gc);
    +
    /* This sanity check relies on PURPLE_CONNECTION_ERROR_OTHER_ERROR
    * being the last member of the PurpleConnectionError enum in
    * connection.h; if other reasons are added after it, this check should
    @@ -510,10 +527,11 @@
    PurpleConnectionErrorInfo *
    purple_connection_get_error_info(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONNECTION(gc), NULL);
    + priv = purple_connection_get_instance_private(gc);
    return priv->error_info;
    }
    @@ -620,16 +638,28 @@
    void purple_connection_update_last_received(PurpleConnection *gc)
    {
    - PurpleConnectionPrivate *priv = purple_connection_get_instance_private(gc);
    + PurpleConnectionPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    + priv = purple_connection_get_instance_private(gc);
    /*
    * For safety, actually this function shouldn't be called when the
    * keepalive mechanism is inactive.
    */
    if (priv->keepalive) {
    - purple_timeout_reset(priv->keepalive, purple_protocol_server_iface_get_keepalive_interval(priv->protocol));
    + /* The #GTimeoutSource API doesn't expose a function to reset when a
    + * #GTimeoutSource will dispatch the next time, but because it works to
    + * directly call g_source_set_ready_time() on a #GTimeoutSource, and since
    + * it seems unlikely that the implementation will change, we just do that
    + * for now as a workaround for this API shortcoming.
    + */
    + gint64 seconds_from_now = purple_protocol_server_iface_get_keepalive_interval(priv->protocol);
    +
    + g_source_set_ready_time(
    + priv->keepalive,
    + g_get_monotonic_time() + (seconds_from_now * G_USEC_PER_SEC)
    + );
    }
    }
    @@ -950,7 +980,7 @@
    if (regist)
    {
    - if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, register_user))
    + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, register_user))
    return;
    }
    else
    @@ -965,7 +995,7 @@
    }
    }
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, connection_new))
    gc = purple_protocol_factory_iface_connection_new(protocol, account,
    password);
    else
    @@ -1034,7 +1064,7 @@
    return;
    }
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, connection_new))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, connection_new))
    gc = purple_protocol_factory_iface_connection_new(protocol, account,
    password);
    else
    --- a/libpurple/connection.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/connection.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_CONNECTION_H_
    -#define _PURPLE_CONNECTION_H_
    +#ifndef PURPLE_CONNECTION_H
    +#define PURPLE_CONNECTION_H
    /**
    * SECTION:connection
    * @section_id: libpurple-connection
    @@ -641,4 +641,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_CONNECTION_H_ */
    +#endif /* PURPLE_CONNECTION_H */
    --- a/libpurple/contact.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/contact.c Tue Oct 08 21:48:28 2019 -0500
    @@ -59,8 +59,6 @@
    PurpleContactPrivate *priv =
    purple_contact_get_instance_private(contact);
    - g_return_if_fail(priv != NULL);
    -
    priv->priority_buddy = NULL;
    for (bnode = PURPLE_BLIST_NODE(contact)->child;
    bnode != NULL;
    @@ -112,15 +110,14 @@
    void
    purple_contact_set_alias(PurpleContact *contact, const char *alias)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleContactPrivate *priv = NULL;
    PurpleIMConversation *im;
    PurpleBlistNode *bnode;
    char *old_alias;
    char *new_alias = NULL;
    - PurpleContactPrivate *priv =
    - purple_contact_get_instance_private(contact);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONTACT(contact));
    + priv = purple_contact_get_instance_private(contact);
    if ((alias != NULL) && (*alias != '\0'))
    new_alias = purple_utf8_strip_unprintables(alias);
    @@ -142,12 +139,10 @@
    g_object_notify_by_pspec(G_OBJECT(contact),
    properties[PROP_ALIAS]);
    - if (ops) {
    - if (ops->save_node)
    - ops->save_node(PURPLE_BLIST_NODE(contact));
    - if (ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(contact));
    - }
    + purple_blist_save_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(contact));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(contact));
    for(bnode = PURPLE_BLIST_NODE(contact)->child; bnode != NULL; bnode = bnode->next)
    {
    @@ -166,11 +161,11 @@
    const char *purple_contact_get_alias(PurpleContact* contact)
    {
    - PurpleContactPrivate *priv =
    - purple_contact_get_instance_private(contact);
    + PurpleContactPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL);
    + priv = purple_contact_get_instance_private(contact);
    if (priv->alias)
    return priv->alias;
    @@ -199,21 +194,21 @@
    void purple_contact_invalidate_priority_buddy(PurpleContact *contact)
    {
    - PurpleContactPrivate *priv =
    - purple_contact_get_instance_private(contact);
    + PurpleContactPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONTACT(contact));
    + priv = purple_contact_get_instance_private(contact);
    priv->priority_valid = FALSE;
    }
    PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact)
    {
    - PurpleContactPrivate *priv =
    - purple_contact_get_instance_private(contact);
    + PurpleContactPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONTACT(contact), NULL);
    + priv = purple_contact_get_instance_private(contact);
    if (!priv->priority_valid)
    purple_contact_compute_priority_buddy(contact);
    @@ -300,10 +295,8 @@
    static void
    purple_contact_init(PurpleContact *contact)
    {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    -
    - if (ops && ops->new_node)
    - ops->new_node(PURPLE_BLIST_NODE(contact));
    + purple_blist_new_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(contact));
    }
    /* GObject finalize function */
    --- a/libpurple/contact.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/contact.h Tue Oct 08 21:48:28 2019 -0500
    @@ -91,7 +91,7 @@
    *
    * Gets the PurpleGroup from a PurpleContact
    *
    - * Returns: The group
    + * Returns: (transfer none): The group.
    */
    PurpleGroup *purple_contact_get_group(const PurpleContact *contact);
    @@ -101,7 +101,7 @@
    *
    * Returns the highest priority buddy for a given contact.
    *
    - * Returns: The highest priority buddy
    + * Returns: (transfer none): The highest priority buddy.
    */
    PurpleBuddy *purple_contact_get_priority_buddy(PurpleContact *contact);
    --- a/libpurple/conversation.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/conversation.c Tue Oct 08 21:48:28 2019 -0500
    @@ -91,8 +91,6 @@
    const char *sent;
    int err = 0;
    - g_return_if_fail(priv != NULL);
    -
    if (*message == '\0')
    return;
    @@ -192,37 +190,6 @@
    g_free(displayed);
    }
    -static void
    -open_log(PurpleConversation *conv)
    -{
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    - GDateTime *dt;
    -
    - g_return_if_fail(priv != NULL);
    -
    - dt = g_date_time_new_now_local();
    - priv->logs = g_list_append(NULL, purple_log_new(PURPLE_IS_CHAT_CONVERSATION(conv) ? PURPLE_LOG_CHAT :
    - PURPLE_LOG_IM, priv->name, priv->account,
    - conv, dt));
    - g_date_time_unref(dt);
    -}
    -
    -/* Functions that deal with PurpleMessage history */
    -
    -static void
    -add_message_to_history(PurpleConversation *conv, PurpleMessage *msg)
    -{
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(msg != NULL);
    -
    - g_object_ref(msg);
    - priv->message_history = g_list_prepend(priv->message_history, msg);
    -}
    -
    /**************************************************************************
    * Conversation API
    **************************************************************************/
    @@ -240,11 +207,11 @@
    void
    purple_conversation_set_features(PurpleConversation *conv, PurpleConnectionFlags features)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    + priv = purple_conversation_get_instance_private(conv);
    priv->features = features;
    g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_FEATURES]);
    @@ -255,11 +222,11 @@
    PurpleConnectionFlags
    purple_conversation_get_features(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), 0);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->features;
    }
    @@ -294,10 +261,10 @@
    purple_conversation_set_ui_ops(PurpleConversation *conv,
    PurpleConversationUiOps *ops)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    + priv = purple_conversation_get_instance_private(conv);
    if (priv->ui_ops == ops)
    return;
    @@ -311,21 +278,21 @@
    PurpleConversationUiOps *
    purple_conversation_get_ui_ops(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->ui_ops;
    }
    void
    purple_conversation_set_account(PurpleConversation *conv, PurpleAccount *account)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    + priv = purple_conversation_get_instance_private(conv);
    if (account == purple_conversation_get_account(conv))
    return;
    @@ -341,11 +308,11 @@
    PurpleAccount *
    purple_conversation_get_account(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->account;
    }
    @@ -367,12 +334,12 @@
    void
    purple_conversation_set_title(PurpleConversation *conv, const char *title)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    g_return_if_fail(title != NULL);
    + priv = purple_conversation_get_instance_private(conv);
    g_free(priv->title);
    priv->title = g_strdup(title);
    @@ -385,11 +352,11 @@
    const char *
    purple_conversation_get_title(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->title;
    }
    @@ -423,10 +390,10 @@
    void
    purple_conversation_set_name(PurpleConversation *conv, const char *name)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    + priv = purple_conversation_get_instance_private(conv);
    _purple_conversations_update_cache(conv, name, NULL);
    @@ -442,11 +409,11 @@
    const char *
    purple_conversation_get_name(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->name;
    }
    @@ -454,10 +421,10 @@
    purple_conversation_set_e2ee_state(PurpleConversation *conv,
    PurpleE2eeState *state)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    + priv = purple_conversation_get_instance_private(conv);
    if (state != NULL && purple_e2ee_state_get_provider(state) !=
    purple_e2ee_provider_get_main())
    @@ -482,11 +449,11 @@
    PurpleE2eeState *
    purple_conversation_get_e2ee_state(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    PurpleE2eeProvider *provider;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
    + priv = purple_conversation_get_instance_private(conv);
    if (priv->e2ee_state == NULL)
    return NULL;
    @@ -507,16 +474,28 @@
    void
    purple_conversation_set_logging(PurpleConversation *conv, gboolean log)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    + priv = purple_conversation_get_instance_private(conv);
    if (priv->logging != log)
    {
    priv->logging = log;
    - if (log && priv->logs == NULL)
    - open_log(conv);
    + if (log && priv->logs == NULL) {
    + GDateTime *dt;
    + PurpleLog *log;
    +
    + dt = g_date_time_new_now_local();
    + log = purple_log_new(PURPLE_IS_CHAT_CONVERSATION(conv)
    + ? PURPLE_LOG_CHAT
    + : PURPLE_LOG_IM,
    + priv->name, priv->account, conv,
    + dt);
    + g_date_time_unref(dt);
    +
    + priv->logs = g_list_append(NULL, log);
    + }
    g_object_notify_by_pspec(G_OBJECT(conv), properties[PROP_LOGGING]);
    @@ -527,43 +506,42 @@
    gboolean
    purple_conversation_is_logging(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), FALSE);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->logging;
    }
    void
    purple_conversation_close_logs(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    - g_list_foreach(priv->logs, (GFunc)purple_log_free, NULL);
    - g_list_free(priv->logs);
    + priv = purple_conversation_get_instance_private(conv);
    + g_list_free_full(priv->logs, (GDestroyNotify)purple_log_free);
    priv->logs = NULL;
    }
    void
    _purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *pmsg)
    {
    + PurpleConversationPrivate *priv = NULL;
    PurpleProtocol *protocol = NULL;
    PurpleConnection *gc = NULL;
    PurpleAccount *account;
    PurpleConversationUiOps *ops;
    PurpleBuddy *b;
    int plugin_return;
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    /* int logging_font_options = 0; */
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    g_return_if_fail(pmsg != NULL);
    + priv = purple_conversation_get_instance_private(conv);
    ops = purple_conversation_get_ui_ops(conv);
    account = purple_conversation_get_account(conv);
    @@ -655,7 +633,8 @@
    ops->write_conv(conv, pmsg);
    }
    - add_message_to_history(conv, pmsg);
    + g_object_ref(pmsg);
    + priv->message_history = g_list_prepend(priv->message_history, pmsg);
    purple_signal_emit(purple_conversations_get_handle(),
    (PURPLE_IS_IM_CONVERSATION(conv) ? "wrote-im-msg" : "wrote-chat-msg"),
    @@ -753,20 +732,26 @@
    char *message = data[1];
    g_free(data);
    +
    + if (!PURPLE_IS_CONVERSATION(conv)) {
    + /* Maybe it was closed before this callback was called. */
    + return;
    + }
    +
    common_send(conv, message, 0);
    }
    void
    purple_conversation_send_confirm(PurpleConversation *conv, const char *message)
    {
    + PurpleConversationPrivate *priv = NULL;
    char *text;
    gpointer *data;
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    g_return_if_fail(message != NULL);
    + priv = purple_conversation_get_instance_private(conv);
    if (priv->ui_ops != NULL && priv->ui_ops->send_confirm != NULL)
    {
    priv->ui_ops->send_confirm(conv, message);
    @@ -799,12 +784,12 @@
    void purple_conversation_clear_message_history(PurpleConversation *conv)
    {
    + PurpleConversationPrivate *priv = NULL;
    GList *list;
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CONVERSATION(conv));
    + priv = purple_conversation_get_instance_private(conv);
    list = priv->message_history;
    g_list_free_full(list, g_object_unref);
    priv->message_history = NULL;
    @@ -815,11 +800,11 @@
    GList *purple_conversation_get_message_history(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->message_history;
    }
    @@ -910,11 +895,11 @@
    PurpleSmileyList *
    purple_conversation_get_remote_smileys(PurpleConversation *conv)
    {
    - PurpleConversationPrivate *priv =
    - purple_conversation_get_instance_private(conv);
    + PurpleConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conv), NULL);
    + priv = purple_conversation_get_instance_private(conv);
    return priv->remote_smileys;
    }
    --- a/libpurple/conversation.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/conversation.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_CONVERSATION_H_
    -#define _PURPLE_CONVERSATION_H_
    +#ifndef PURPLE_CONVERSATION_H
    +#define PURPLE_CONVERSATION_H
    /**
    * SECTION:conversation
    * @section_id: libpurple-conversation
    @@ -619,10 +619,9 @@
    *
    * Retrieves the extended menu items for the conversation.
    *
    - * Returns: (element-type PurpleActionMenu):
    - * A list of PurpleActionMenu items, harvested by the
    - * chat-extended-menu signal. The list and the menuaction
    - * items should be freed by the caller.
    + * Returns: (element-type PurpleActionMenu) (transfer full): The extended menu
    + * items for a conversation, as harvested by the chat-extended-menu
    + * signal.
    */
    GList * purple_conversation_get_extended_menu(PurpleConversation *conv);
    @@ -647,7 +646,7 @@
    *
    * Gets the maximum message size in bytes for the conversation.
    *
    - * See #PurpleProtocolClientIface's <literal>get_max_message_size</literal>.
    + * See #PurpleProtocolClientInterface's <literal>get_max_message_size</literal>.
    *
    * Returns: Maximum message size, 0 if unspecified, -1 for infinite.
    */
    @@ -708,4 +707,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_CONVERSATION_H_ */
    +#endif /* PURPLE_CONVERSATION_H */
    --- a/libpurple/conversations.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/conversations.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_CONVERSATIONS_H_
    -#define _PURPLE_CONVERSATIONS_H_
    +#ifndef PURPLE_CONVERSATIONS_H
    +#define PURPLE_CONVERSATIONS_H
    /**
    * SECTION:conversations
    * @section_id: libpurple-conversations
    @@ -90,7 +90,7 @@
    *
    * Finds a conversation of any type with the specified name and Purple account.
    *
    - * Returns: The conversation if found, or %NULL otherwise.
    + * Returns: (transfer none): The conversation if found, or %NULL otherwise.
    */
    PurpleConversation *purple_conversations_find_with_account(const char *name,
    PurpleAccount *account);
    @@ -102,7 +102,7 @@
    *
    * Finds an IM with the specified name and Purple account.
    *
    - * Returns: The conversation if found, or %NULL otherwise.
    + * Returns: (transfer none): The conversation if found, or %NULL otherwise.
    */
    PurpleIMConversation *purple_conversations_find_im_with_account(const char *name,
    PurpleAccount *account);
    @@ -114,7 +114,7 @@
    *
    * Finds a chat with the specified name and Purple account.
    *
    - * Returns: The conversation if found, or %NULL otherwise.
    + * Returns: (transfer none): The conversation if found, or %NULL otherwise.
    */
    PurpleChatConversation *purple_conversations_find_chat_with_account(const char *name,
    PurpleAccount *account);
    @@ -126,7 +126,7 @@
    *
    * Finds a chat with the specified chat ID.
    *
    - * Returns: The chat conversation.
    + * Returns: (transfer none): The chat conversation.
    */
    PurpleChatConversation *purple_conversations_find_chat(const PurpleConnection *gc, int id);
    @@ -172,4 +172,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_CONVERSATIONS_H_ */
    +#endif /* PURPLE_CONVERSATIONS_H */
    --- a/libpurple/conversationtypes.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/conversationtypes.c Tue Oct 08 21:48:28 2019 -0500
    @@ -190,11 +190,11 @@
    void
    purple_im_conversation_set_icon(PurpleIMConversation *im, PurpleBuddyIcon *icon)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
    + priv = purple_im_conversation_get_instance_private(im);
    if (priv->icon != icon)
    {
    purple_buddy_icon_unref(priv->icon);
    @@ -211,24 +211,24 @@
    PurpleBuddyIcon *
    purple_im_conversation_get_icon(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), NULL);
    + priv = purple_im_conversation_get_instance_private(im);
    return priv->icon;
    }
    void
    purple_im_conversation_set_typing_state(PurpleIMConversation *im, PurpleIMTypingState state)
    {
    + PurpleIMConversationPrivate *priv = NULL;
    PurpleAccount *account;
    const char *name;
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
    + priv = purple_im_conversation_get_instance_private(im);
    name = purple_conversation_get_name(PURPLE_CONVERSATION(im));
    account = purple_conversation_get_account(PURPLE_CONVERSATION(im));
    @@ -262,22 +262,22 @@
    PurpleIMTypingState
    purple_im_conversation_get_typing_state(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
    + priv = purple_im_conversation_get_instance_private(im);
    return priv->typing_state;
    }
    void
    purple_im_conversation_start_typing_timeout(PurpleIMConversation *im, int timeout)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
    + priv = purple_im_conversation_get_instance_private(im);
    if (priv->typing_timeout > 0)
    purple_im_conversation_stop_typing_timeout(im);
    @@ -287,11 +287,11 @@
    void
    purple_im_conversation_stop_typing_timeout(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
    + priv = purple_im_conversation_get_instance_private(im);
    if (priv->typing_timeout == 0)
    return;
    @@ -302,22 +302,22 @@
    guint
    purple_im_conversation_get_typing_timeout(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
    + priv = purple_im_conversation_get_instance_private(im);
    return priv->typing_timeout;
    }
    void
    purple_im_conversation_set_type_again(PurpleIMConversation *im, unsigned int val)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
    + priv = purple_im_conversation_get_instance_private(im);
    if (val == 0)
    priv->type_again = 0;
    else
    @@ -327,22 +327,22 @@
    time_t
    purple_im_conversation_get_type_again(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
    + priv = purple_im_conversation_get_instance_private(im);
    return priv->type_again;
    }
    void
    purple_im_conversation_start_send_typed_timeout(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
    + priv = purple_im_conversation_get_instance_private(im);
    priv->send_typed_timeout = g_timeout_add_seconds(SEND_TYPED_TIMEOUT_SECONDS,
    send_typed_cb, im);
    }
    @@ -350,11 +350,11 @@
    void
    purple_im_conversation_stop_send_typed_timeout(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_IM_CONVERSATION(im));
    + priv = purple_im_conversation_get_instance_private(im);
    if (priv->send_typed_timeout == 0)
    return;
    @@ -365,11 +365,11 @@
    guint
    purple_im_conversation_get_send_typed_timeout(PurpleIMConversation *im)
    {
    - PurpleIMConversationPrivate *priv =
    - purple_im_conversation_get_instance_private(im);
    + PurpleIMConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_IM_CONVERSATION(im), 0);
    + priv = purple_im_conversation_get_instance_private(im);
    return priv->send_typed_timeout;
    }
    @@ -537,18 +537,15 @@
    conv_class->write_message = im_conversation_write_message;
    - im_properties[IM_PROP_TYPING_STATE] = g_param_spec_enum("typing-state",
    - "Typing state",
    - "Status of the user's typing of a message.",
    - PURPLE_TYPE_IM_TYPING_STATE, PURPLE_IM_NOT_TYPING,
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
    - G_PARAM_STATIC_STRINGS);
    + im_properties[IM_PROP_TYPING_STATE] =
    + g_param_spec_enum("typing-state", "Typing state",
    + "Status of the user's typing of a message.",
    + PURPLE_TYPE_IM_TYPING_STATE, PURPLE_IM_NOT_TYPING,
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    - im_properties[IM_PROP_ICON] = g_param_spec_pointer("icon",
    - "Buddy icon",
    - "The buddy icon for the IM.",
    - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
    - G_PARAM_STATIC_STRINGS);
    + im_properties[IM_PROP_ICON] = g_param_spec_pointer(
    + "icon", "Buddy icon", "The buddy icon for the IM.",
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    g_object_class_install_properties(obj_class, IM_PROP_LAST, im_properties);
    }
    @@ -603,34 +600,35 @@
    GList *
    purple_chat_conversation_get_users(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return g_hash_table_get_values(priv->users);
    }
    guint
    purple_chat_conversation_get_users_count(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), 0);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return g_hash_table_size(priv->users);
    }
    void
    purple_chat_conversation_ignore(PurpleChatConversation *chat, const char *name)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    g_return_if_fail(name != NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    +
    /* Make sure the user isn't already ignored. */
    if (purple_chat_conversation_is_ignored_user(chat, name))
    return;
    @@ -642,13 +640,14 @@
    void
    purple_chat_conversation_unignore(PurpleChatConversation *chat, const char *name)
    {
    + PurpleChatConversationPrivate *priv = NULL;
    GList *item;
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    g_return_if_fail(name != NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    +
    /* Make sure the user is actually ignored. */
    if (!purple_chat_conversation_is_ignored_user(chat, name))
    return;
    @@ -666,11 +665,11 @@
    GList *
    purple_chat_conversation_set_ignored(PurpleChatConversation *chat, GList *ignored)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    priv->ignored = ignored;
    return ignored;
    }
    @@ -678,11 +677,11 @@
    GList *
    purple_chat_conversation_get_ignored(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return priv->ignored;
    }
    @@ -728,11 +727,12 @@
    void
    purple_chat_conversation_set_topic(PurpleChatConversation *chat, const char *who, const char *topic)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    GObject *obj;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    +
    + priv = purple_chat_conversation_get_instance_private(chat);
    g_free(priv->who);
    g_free(priv->topic);
    @@ -756,33 +756,33 @@
    const char *
    purple_chat_conversation_get_topic(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return priv->topic;
    }
    const char *
    purple_chat_conversation_get_topic_who(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return priv->who;
    }
    void
    purple_chat_conversation_set_id(PurpleChatConversation *chat, int id)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    + priv = purple_chat_conversation_get_instance_private(chat);
    priv->id = id;
    g_object_notify_by_pspec(G_OBJECT(chat), chat_properties[CHAT_PROP_ID]);
    @@ -791,25 +791,25 @@
    int
    purple_chat_conversation_get_id(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, -1);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), -1);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return priv->id;
    }
    static void
    chat_conversation_write_message(PurpleConversation *conv, PurpleMessage *msg)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(
    - PURPLE_CHAT_CONVERSATION(conv));
    + PurpleChatConversationPrivate *priv = NULL;
    PurpleMessageFlags flags;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(conv));
    g_return_if_fail(msg != NULL);
    + priv = purple_chat_conversation_get_instance_private(PURPLE_CHAT_CONVERSATION(conv));
    +
    /* Don't display this if the person who wrote it is ignored. */
    if (purple_message_get_author(msg) && purple_chat_conversation_is_ignored_user(
    PURPLE_CHAT_CONVERSATION(conv), purple_message_get_author(msg)))
    @@ -858,11 +858,10 @@
    GList *ul, *fl;
    GList *cbuddies = NULL;
    - priv = purple_chat_conversation_get_instance_private(chat);
    -
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    g_return_if_fail(users != NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    conv = PURPLE_CONVERSATION(chat);
    ops = purple_conversation_get_ui_ops(conv);
    @@ -962,12 +961,11 @@
    char tmp[BUF_LONG];
    gboolean is_me = FALSE;
    - priv = purple_chat_conversation_get_instance_private(chat);
    -
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    g_return_if_fail(old_user != NULL);
    g_return_if_fail(new_user != NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    conv = PURPLE_CONVERSATION(chat);
    ops = purple_conversation_get_ui_ops(conv);
    account = purple_conversation_get_account(conv);
    @@ -1082,11 +1080,10 @@
    GList *l;
    gboolean quiet;
    - priv = purple_chat_conversation_get_instance_private(chat);
    -
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    g_return_if_fail(users != NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    conv = PURPLE_CONVERSATION(chat);
    gc = purple_conversation_get_connection(conv);
    @@ -1151,14 +1148,14 @@
    void
    purple_chat_conversation_clear_users(PurpleChatConversation *chat)
    {
    + PurpleChatConversationPrivate *priv = NULL;
    PurpleConversationUiOps *ops;
    GHashTableIter it;
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    gchar *name;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    + priv = purple_chat_conversation_get_instance_private(chat);
    ops = purple_conversation_get_ui_ops(PURPLE_CONVERSATION(chat));
    if (ops != NULL && ops->chat_remove_users != NULL) {
    @@ -1184,11 +1181,11 @@
    }
    void purple_chat_conversation_set_nick(PurpleChatConversation *chat, const char *nick) {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    + priv = purple_chat_conversation_get_instance_private(chat);
    g_free(priv->nick);
    priv->nick = g_strdup(purple_normalize(
    purple_conversation_get_account(PURPLE_CONVERSATION(chat)), nick));
    @@ -1197,11 +1194,11 @@
    }
    const char *purple_chat_conversation_get_nick(PurpleChatConversation *chat) {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return priv->nick;
    }
    @@ -1276,11 +1273,11 @@
    void
    purple_chat_conversation_leave(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat));
    + priv = purple_chat_conversation_get_instance_private(chat);
    priv->left = TRUE;
    if (!g_object_get_data(G_OBJECT(chat), "is-finalizing"))
    @@ -1292,11 +1289,11 @@
    gboolean
    purple_chat_conversation_has_left(PurpleChatConversation *chat)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, TRUE);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), TRUE);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return priv->left;
    }
    @@ -1337,12 +1334,12 @@
    PurpleChatUser *
    purple_chat_conversation_find_user(PurpleChatConversation *chat, const char *name)
    {
    - PurpleChatConversationPrivate *priv =
    - purple_chat_conversation_get_instance_private(chat);
    + PurpleChatConversationPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_CONVERSATION(chat), NULL);
    g_return_val_if_fail(name != NULL, NULL);
    + priv = purple_chat_conversation_get_instance_private(chat);
    return g_hash_table_lookup(priv->users, name);
    }
    @@ -1487,8 +1484,7 @@
    g_hash_table_destroy(priv->users);
    priv->users = NULL;
    - g_list_foreach(priv->ignored, (GFunc)g_free, NULL);
    - g_list_free(priv->ignored);
    + g_list_free_full(priv->ignored, g_free);
    priv->ignored = NULL;
    g_free(priv->who);
    @@ -1639,10 +1635,10 @@
    purple_chat_user_get_alias(PurpleChatUser *cb)
    {
    PurpleChatUserPrivate *priv;
    - priv = purple_chat_user_get_instance_private(cb);
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
    + priv = purple_chat_user_get_instance_private(cb);
    return priv->alias;
    }
    @@ -1650,10 +1646,10 @@
    purple_chat_user_get_name(PurpleChatUser *cb)
    {
    PurpleChatUserPrivate *priv;
    - priv = purple_chat_user_get_instance_private(cb);
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
    + priv = purple_chat_user_get_instance_private(cb);
    return priv->name;
    }
    @@ -1664,9 +1660,10 @@
    PurpleConversationUiOps *ops;
    PurpleChatUserFlags oldflags;
    PurpleChatUserPrivate *priv;
    - priv = purple_chat_user_get_instance_private(cb);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_USER(cb));
    +
    + priv = purple_chat_user_get_instance_private(cb);
    if (flags == priv->flags)
    return;
    @@ -1689,10 +1686,10 @@
    purple_chat_user_get_flags(PurpleChatUser *cb)
    {
    PurpleChatUserPrivate *priv;
    - priv = purple_chat_user_get_instance_private(cb);
    - g_return_val_if_fail(priv != NULL, PURPLE_CHAT_USER_NONE);
    + g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), PURPLE_CHAT_USER_NONE);
    + priv = purple_chat_user_get_instance_private(cb);
    return priv->flags;
    }
    @@ -1717,10 +1714,10 @@
    PurpleChatConversation *chat)
    {
    PurpleChatUserPrivate *priv;
    - priv = purple_chat_user_get_instance_private(cb);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_CHAT_USER(cb));
    + priv = purple_chat_user_get_instance_private(cb);
    priv->chat = chat;
    g_object_notify_by_pspec(G_OBJECT(cb), cu_properties[CU_PROP_CHAT]);
    @@ -1730,10 +1727,10 @@
    purple_chat_user_get_chat(PurpleChatUser *cb)
    {
    PurpleChatUserPrivate *priv;
    - priv = purple_chat_user_get_instance_private(cb);
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), NULL);
    + priv = purple_chat_user_get_instance_private(cb);
    return priv->chat;
    }
    @@ -1741,10 +1738,10 @@
    purple_chat_user_is_buddy(PurpleChatUser *cb)
    {
    PurpleChatUserPrivate *priv;
    - priv = purple_chat_user_get_instance_private(cb);
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_CHAT_USER(cb), FALSE);
    + priv = purple_chat_user_get_instance_private(cb);
    return priv->buddy;
    }
    --- a/libpurple/conversationtypes.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/conversationtypes.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_CONVERSATION_TYPES_H_
    -#define _PURPLE_CONVERSATION_TYPES_H_
    +#ifndef PURPLE_CONVERSATION_TYPES_H
    +#define PURPLE_CONVERSATION_TYPES_H
    /**
    * SECTION:conversationtypes
    * @section_id: libpurple-conversationtypes
    @@ -377,13 +377,11 @@
    /**
    * purple_chat_conversation_set_ignored:
    * @chat: The chat.
    - * @ignored: (element-type char*):
    - * The list of ignored users.
    + * @ignored: (element-type utf8): The list of ignored users.
    *
    * Sets the list of ignored users in the chat room.
    *
    - * Returns: (element-type char*) (transfer none):
    - * The list passed.
    + * Returns: (element-type utf8) (transfer none): The list passed.
    */
    GList *purple_chat_conversation_set_ignored(PurpleChatConversation *chat, GList *ignored);
    @@ -393,8 +391,7 @@
    *
    * Returns the list of ignored users in the chat room.
    *
    - * Returns: (element-type char*) (transfer none):
    - * The list of ignored users.
    + * Returns: (element-type utf8) (transfer none): The list of ignored users.
    */
    GList *purple_chat_conversation_get_ignored(PurpleChatConversation *chat);
    @@ -494,20 +491,17 @@
    /**
    * purple_chat_conversation_add_users:
    - * @chat: The chat.
    - * @users: (element-type char*):
    - * The list of users to add.
    - * @extra_msgs: (element-type char*) (nullable):
    - * An extra message to display with the join message for each
    - * user. This list may be shorter than @users, in which
    - * case, the users after the end of extra_msgs will not have
    - * an extra message. By extension, this means that extra_msgs
    - * can simply be %NULL and none of the users will have an
    - * extra message.
    - * @flags: (element-type PurpleChatUserFlags):
    - * The list of flags for each user.
    - * This list data should be an int converted to pointer using
    - * GINT_TO_POINTER(flag)
    + * @chat: The chat.
    + * @users: (element-type utf8): The list of users to add.
    + * @extra_msgs: (element-type utf8) (nullable): An extra message to display
    + * with the join message for each user. This list may be shorter
    + * than @users, in which case, the users after the end of
    + * extra_msgs will not have an extra message. By extension, this
    + * means that extra_msgs can simply be %NULL and none of the users
    + * will have an extra message.
    + * @flags: (element-type PurpleChatUserFlags): The list of flags for each user.
    + * This list data should be an int converted to pointer using
    + * GINT_TO_POINTER(flag)
    * @new_arrivals: Decides whether or not to show join notices.
    *
    * Adds a list of users to a chat.
    @@ -545,8 +539,7 @@
    /**
    * purple_chat_conversation_remove_users:
    * @chat: The chat.
    - * @users: (element-type char*)
    - * The users that are being removed.
    + * @users: (element-type utf8): The users that are being removed.
    * @reason: The optional reason given for the removal. Can be %NULL.
    *
    * Removes a list of users from a chat, optionally with a single reason.
    @@ -765,4 +758,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_CONVERSATION_TYPES_H_ */
    +#endif /* PURPLE_CONVERSATION_TYPES_H */
    --- a/libpurple/core.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/core.c Tue Oct 08 21:48:28 2019 -0500
    @@ -72,8 +72,11 @@
    ui_version = ui_info ? g_hash_table_lookup(ui_info, "version") : NULL;
    if (ui_name) {
    - ui_full_name = g_strdup_printf("%s%s%s", ui_name,
    - ui_version ? " " : "", ui_version);
    + if (ui_version) {
    + ui_full_name = g_strdup_printf("%s %s", ui_name, ui_version);
    + } else {
    + ui_full_name = g_strdup(ui_name);
    + }
    }
    purple_debug_info("main", "Launching %s%slibpurple %s",
    @@ -81,6 +84,7 @@
    ui_full_name ? " with " : "",
    purple_core_get_version());
    + g_free(ui_full_name);
    }
    gboolean
    --- a/libpurple/core.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/core.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_CORE_H_
    -#define _PURPLE_CORE_H_
    +#ifndef PURPLE_CORE_H
    +#define PURPLE_CORE_H
    /**
    * SECTION:core
    * @section_id: libpurple-core
    @@ -207,8 +207,8 @@
    * </tbody></tgroup>
    * </informaltable>
    *
    - * Returns: A GHashTable with strings for keys and values. This
    - * hash table must not be freed and should not be modified.
    + * Returns: (transfer none): A GHashTable with strings for keys and values.
    + * This hash table should not be modified.
    *
    */
    GHashTable* purple_core_get_ui_info(void);
    @@ -228,7 +228,7 @@
    G_END_DECLS
    -#endif /* _PURPLE_CORE_H_ */
    +#endif /* PURPLE_CORE_H */
    /*
    --- a/libpurple/countingnode.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/countingnode.c Tue Oct 08 21:48:28 2019 -0500
    @@ -56,77 +56,77 @@
    int
    purple_counting_node_get_total_size(PurpleCountingNode *counter)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, -1);
    + g_return_val_if_fail(PURPLE_IS_COUNTING_NODE(counter), -1);
    + priv = purple_counting_node_get_instance_private(counter);
    return priv->totalsize;
    }
    int
    purple_counting_node_get_current_size(PurpleCountingNode *counter)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, -1);
    + g_return_val_if_fail(PURPLE_IS_COUNTING_NODE(counter), -1);
    + priv = purple_counting_node_get_instance_private(counter);
    return priv->currentsize;
    }
    int
    purple_counting_node_get_online_count(PurpleCountingNode *counter)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, -1);
    + g_return_val_if_fail(PURPLE_IS_COUNTING_NODE(counter), -1);
    + priv = purple_counting_node_get_instance_private(counter);
    return priv->onlinecount;
    }
    void
    purple_counting_node_change_total_size(PurpleCountingNode *counter, int delta)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_COUNTING_NODE(counter));
    + priv = purple_counting_node_get_instance_private(counter);
    purple_counting_node_set_total_size(counter, priv->totalsize + delta);
    }
    void
    purple_counting_node_change_current_size(PurpleCountingNode *counter, int delta)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_COUNTING_NODE(counter));
    + priv = purple_counting_node_get_instance_private(counter);
    purple_counting_node_set_current_size(counter, priv->currentsize + delta);
    }
    void
    purple_counting_node_change_online_count(PurpleCountingNode *counter, int delta)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_COUNTING_NODE(counter));
    + priv = purple_counting_node_get_instance_private(counter);
    purple_counting_node_set_online_count(counter, priv->onlinecount + delta);
    }
    void
    purple_counting_node_set_total_size(PurpleCountingNode *counter, int totalsize)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_COUNTING_NODE(counter));
    + priv = purple_counting_node_get_instance_private(counter);
    priv->totalsize = totalsize;
    g_object_notify_by_pspec(G_OBJECT(counter), properties[PROP_TOTAL_SIZE]);
    @@ -135,11 +135,11 @@
    void
    purple_counting_node_set_current_size(PurpleCountingNode *counter, int currentsize)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_COUNTING_NODE(counter));
    + priv = purple_counting_node_get_instance_private(counter);
    priv->currentsize = currentsize;
    g_object_notify_by_pspec(G_OBJECT(counter), properties[PROP_CURRENT_SIZE]);
    @@ -148,11 +148,11 @@
    void
    purple_counting_node_set_online_count(PurpleCountingNode *counter, int onlinecount)
    {
    - PurpleCountingNodePrivate *priv =
    - purple_counting_node_get_instance_private(counter);
    + PurpleCountingNodePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_COUNTING_NODE(counter));
    + priv = purple_counting_node_get_instance_private(counter);
    priv->onlinecount = onlinecount;
    g_object_notify_by_pspec(G_OBJECT(counter), properties[PROP_ONLINE_COUNT]);
    --- a/libpurple/debug.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/debug.c Tue Oct 08 21:48:28 2019 -0500
    @@ -65,9 +65,11 @@
    if (!iface)
    return;
    - if (!debug_enabled && ((iface == NULL) || (iface->print == NULL) ||
    - (iface->is_enabled && !iface->is_enabled(ops, level, category))))
    + if (!debug_enabled &&
    + ((iface->print == NULL) ||
    + (iface->is_enabled && !iface->is_enabled(ops, level, category)))) {
    return;
    + }
    arg_s = g_strdup_vprintf(format, args);
    g_strchomp(arg_s); /* strip trailing linefeeds */
    @@ -108,8 +110,9 @@
    g_free(ts_s);
    }
    - if (iface != NULL && iface->print != NULL)
    + if (iface->print != NULL) {
    iface->print(ops, level, category, arg_s);
    + }
    g_free(arg_s);
    }
    --- a/libpurple/debug.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/debug.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_DEBUG_H_
    -#define _PURPLE_DEBUG_H_
    +#ifndef PURPLE_DEBUG_H
    +#define PURPLE_DEBUG_H
    /**
    * SECTION:debug
    * @section_id: libpurple-debug
    @@ -259,7 +259,7 @@
    * Returns the UI operations structure used when outputting debug
    * information.
    *
    - * Returns: The UI operations structure in use.
    + * Returns: (transfer none): The UI operations structure in use.
    */
    PurpleDebugUi *purple_debug_get_ui(void);
    @@ -276,4 +276,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_DEBUG_H_ */
    +#endif /* PURPLE_DEBUG_H */
    --- a/libpurple/e2ee.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/e2ee.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_E2EE_H_
    -#define _PURPLE_E2EE_H_
    +#ifndef PURPLE_E2EE_H
    +#define PURPLE_E2EE_H
    /**
    * SECTION:e2ee
    * @section_id: libpurple-e2ee
    @@ -254,4 +254,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_E2EE_H_ */
    +#endif /* PURPLE_E2EE_H */
    --- a/libpurple/eventloop.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/eventloop.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_EVENTLOOP_H_
    -#define _PURPLE_EVENTLOOP_H_
    +#ifndef PURPLE_EVENTLOOP_H
    +#define PURPLE_EVENTLOOP_H
    /**
    * SECTION:eventloop
    * @section_id: libpurple-eventloop
    @@ -108,4 +108,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_EVENTLOOP_H_ */
    +#endif /* PURPLE_EVENTLOOP_H */
    --- a/libpurple/example/nullclient.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/example/nullclient.c Tue Oct 08 21:48:28 2019 -0500
    @@ -51,22 +51,7 @@
    static PurpleConversationUiOps null_conv_uiops =
    {
    - NULL, /* create_conversation */
    - NULL, /* destroy_conversation */
    - NULL, /* write_chat */
    - NULL, /* write_im */
    - null_write_conv, /* write_conv */
    - NULL, /* chat_add_users */
    - NULL, /* chat_rename_user */
    - NULL, /* chat_remove_users */
    - NULL, /* chat_update_user */
    - NULL, /* present */
    - NULL, /* has_focus */
    - NULL, /* send_confirm */
    - NULL,
    - NULL,
    - NULL,
    - NULL
    + .write_conv = null_write_conv,
    };
    static void
    @@ -81,17 +66,7 @@
    static PurpleCoreUiOps null_core_uiops =
    {
    - NULL,
    - NULL,
    - null_ui_init,
    - NULL,
    -
    - /* padding */
    - NULL,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    + .ui_init = null_ui_init,
    };
    static void
    @@ -217,16 +192,21 @@
    }
    g_list_free(list);
    - printf("Select the protocol [0-%d]: ", i-1);
    - res = fgets(name, sizeof(name), stdin);
    - if (!res) {
    - fprintf(stderr, "Failed to gets protocol selection.");
    - abort();
    + num = -1;
    + while (num < 0 || num >= i) {
    + printf("Select the protocol [0-%d]: ", i - 1);
    + res = fgets(name, sizeof(name), stdin);
    + if (!res) {
    + fprintf(stderr, "Failed to get protocol selection.");
    + abort();
    + }
    + if (sscanf(name, "%d", &num) != 1) {
    + num = -1;
    + }
    }
    - if (sscanf(name, "%d", &num) == 1)
    - protocol = g_list_nth_data(names, num);
    + protocol = g_list_nth_data(names, num);
    if (!protocol) {
    - fprintf(stderr, "Failed to gets protocol.");
    + fprintf(stderr, "Failed to get protocol.");
    abort();
    }
    --- a/libpurple/glibcompat.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/glibcompat.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GLIBCOMPAT_H_
    -#define _GLIBCOMPAT_H_
    +#ifndef PURPLE_GLIBCOMPAT_H
    +#define PURPLE_GLIBCOMPAT_H
    /*
    * SECTION:glibcompat
    * @section_id: libpurple-glibcompat
    @@ -80,4 +80,4 @@
    } G_STMT_END
    #endif
    -#endif /* _GLIBCOMPAT_H_ */
    +#endif /* PURPLE_GLIBCOMPAT_H */
    --- a/libpurple/group.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/group.c Tue Oct 08 21:48:28 2019 -0500
    @@ -97,17 +97,18 @@
    * TODO: If merging, prompt the user if they want to merge.
    */
    void purple_group_set_name(PurpleGroup *source, const char *name) {
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    + PurpleGroupPrivate *priv = NULL;
    PurpleGroup *dest;
    gchar *old_name;
    gchar *new_name;
    GList *moved_buddies = NULL;
    GSList *accts;
    - PurpleGroupPrivate *priv = purple_group_get_instance_private(source);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_GROUP(source));
    g_return_if_fail(name != NULL);
    + priv = purple_group_get_instance_private(source);
    +
    new_name = purple_utf8_strip_unprintables(name);
    if (*new_name == '\0' || purple_strequal(new_name, priv->name)) {
    @@ -177,12 +178,12 @@
    }
    /* Save our changes */
    - if (ops && ops->save_node)
    - ops->save_node(PURPLE_BLIST_NODE(source));
    + purple_blist_save_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(source));
    /* Update the UI */
    - if (ops && ops->update)
    - ops->update(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(source));
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(source));
    /* Notify all protocols */
    /* TODO: Is this condition needed? Seems like it would always be TRUE */
    @@ -208,7 +209,7 @@
    buddies = g_list_append(buddies, (PurpleBlistNode *)buddy);
    }
    - if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, rename_group)) {
    + if(PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, rename_group)) {
    purple_protocol_server_iface_rename_group(protocol, gc, old_name, source, buddies);
    } else {
    GList *cur, *groups = NULL;
    @@ -234,10 +235,11 @@
    }
    const char *purple_group_get_name(PurpleGroup *group) {
    - PurpleGroupPrivate *priv = purple_group_get_instance_private(group);
    + PurpleGroupPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_GROUP(group), NULL);
    + priv = purple_group_get_instance_private(group);
    return priv->name;
    }
    @@ -293,12 +295,11 @@
    purple_group_constructed(GObject *object) {
    PurpleGroup *group = PURPLE_GROUP(object);
    PurpleGroupPrivate *priv = purple_group_get_instance_private(group);
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    G_OBJECT_CLASS(purple_group_parent_class)->constructed(object);
    - if (ops && ops->new_node)
    - ops->new_node(PURPLE_BLIST_NODE(group));
    + purple_blist_new_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(group));
    priv->is_constructed = TRUE;
    }
    --- a/libpurple/http.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/http.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_HTTP_H_
    -#define _PURPLE_HTTP_H_
    +#ifndef PURPLE_HTTP_H
    +#define PURPLE_HTTP_H
    /**
    * SECTION:http
    * @section_id: libpurple-http
    @@ -981,4 +981,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_HTTP_H_ */
    +#endif /* PURPLE_HTTP_H */
    --- a/libpurple/idle.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/idle.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_IDLE_H_
    -#define _PURPLE_IDLE_H_
    +#ifndef PURPLE_IDLE_H
    +#define PURPLE_IDLE_H
    /**
    * SECTION:idle
    * @section_id: libpurple-idle
    @@ -119,4 +119,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_IDLE_H_ */
    +#endif /* PURPLE_IDLE_H */
    --- a/libpurple/image-store.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/image-store.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_IMAGE_STORE_H_
    -#define _PURPLE_IMAGE_STORE_H_
    +#ifndef PURPLE_IMAGE_STORE_H
    +#define PURPLE_IMAGE_STORE_H
    /**
    * SECTION:image-store
    * @include:image-store.h
    @@ -163,4 +163,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_IMAGE_STORE_H_ */
    +#endif /* PURPLE_IMAGE_STORE_H */
    --- a/libpurple/image.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/image.h Tue Oct 08 21:48:28 2019 -0500
    @@ -43,6 +43,7 @@
    #define PURPLE_TYPE_IMAGE purple_image_get_type()
    struct _PurpleImageClass {
    + /*< private >*/
    GObjectClass parent_class;
    void (*purple_reserved1)(void);
    --- a/libpurple/internal.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/internal.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_INTERNAL_H_
    -#define _PURPLE_INTERNAL_H_
    +#ifndef PURPLE_INTERNAL_H
    +#define PURPLE_INTERNAL_H
    /*
    * SECTION:internal
    * @section_id: libpurple-internal
    @@ -369,4 +369,4 @@
    void
    _purple_conversation_write_common(PurpleConversation *conv, PurpleMessage *msg);
    -#endif /* _PURPLE_INTERNAL_H_ */
    +#endif /* PURPLE_INTERNAL_H */
    --- a/libpurple/keyring.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/keyring.c Tue Oct 08 21:48:28 2019 -0500
    @@ -240,9 +240,9 @@
    current_change_tracker = NULL;
    if (tracker->error == NULL) {
    - tracker->error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_UNKNOWN,
    - _("An unknown error has occured."));
    + tracker->error = g_error_new_literal(
    + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_UNKNOWN,
    + _("An unknown error has occured."));
    }
    if (tracker->cb != NULL)
    @@ -379,10 +379,10 @@
    "session already running.\n");
    if (cb == NULL)
    return;
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_INTERNAL,
    - _("There is a password migration session already "
    - "running."));
    + error = g_error_new_literal(
    + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_INTERNAL,
    + _("There is a password migration session already "
    + "running."));
    cb(error, data);
    g_error_free(error);
    return;
    @@ -407,8 +407,9 @@
    if (oldkeyring == NULL) { /* No keyring was set before. */
    if (purple_debug_is_verbose()) {
    - purple_debug_misc("keyring", "Setting keyring for the "
    - "first time: %s.\n", newkeyring->id);
    + purple_debug_misc("keyring",
    + "Setting keyring for the first time: %s.\n",
    + (newkeyring != NULL) ? newkeyring->id : "(null)");
    }
    purple_keyring_inuse = newkeyring;
    @@ -680,11 +681,9 @@
    keyring = purple_keyring_find_keyring_by_id(keyring_id);
    if (keyring == NULL) {
    - if (error != NULL) {
    - *error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_BACKENDFAIL,
    - _("Specified keyring is not registered."));
    - }
    + g_set_error_literal(error, PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_BACKENDFAIL,
    + _("Specified keyring is not registered."));
    purple_debug_warning("Keyring", "Specified keyring is not "
    "registered, cannot import password info for account "
    "%s.\n", purple_keyring_print_account(account));
    @@ -694,12 +693,9 @@
    inuse = purple_keyring_get_inuse();
    if (inuse == NULL) {
    PurpleKeyringFailedImport *import;
    - if (error != NULL) {
    - *error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_NOKEYRING,
    - _("No keyring loaded, cannot import password "
    - "info."));
    - }
    + g_set_error_literal(
    + error, PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOKEYRING,
    + _("No keyring loaded, cannot import password info."));
    purple_debug_warning("Keyring",
    "No keyring loaded, cannot import password info for "
    "account %s.\n", purple_keyring_print_account(account));
    @@ -714,12 +710,9 @@
    }
    if (inuse != keyring) {
    - if (error != NULL) {
    - *error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_INTERNAL,
    - _("Specified keyring ID does not match the "
    - "loaded one."));
    - }
    + g_set_error_literal(
    + error, PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_INTERNAL,
    + _("Specified keyring ID does not match the loaded one."));
    purple_debug_error("keyring",
    "Specified keyring %s is not currently used (%s). "
    "Data will be lost.\n", keyring_id,
    @@ -761,10 +754,9 @@
    purple_keyring_failed_imports, account);
    if (import == NULL) {
    - *error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_NOKEYRING,
    - _("No keyring configured, cannot export "
    - "password info."));
    + g_set_error_literal(
    + error, PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_NOKEYRING,
    + _("No keyring configured, cannot export password info."));
    purple_debug_warning("keyring",
    "No keyring configured, cannot export password "
    "info.\n");
    @@ -822,9 +814,9 @@
    "quitting.\n");
    if (cb == NULL)
    return;
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_INTERNAL,
    - _("Cannot request a password while quitting."));
    + error = g_error_new_literal(
    + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_INTERNAL,
    + _("Cannot request a password while quitting."));
    cb(account, NULL, error, data);
    g_error_free(error);
    return;
    @@ -837,9 +829,9 @@
    if (cb == NULL)
    return;
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_NOKEYRING,
    - _("No keyring configured."));
    + error = g_error_new_literal(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_NOKEYRING,
    + _("No keyring configured."));
    cb(account, NULL, error, data);
    g_error_free(error);
    return;
    @@ -899,9 +891,9 @@
    "quitting.\n");
    if (cb == NULL)
    return;
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_INTERNAL,
    - _("Cannot save a password while quitting."));
    + error = g_error_new_literal(
    + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_INTERNAL,
    + _("Cannot save a password while quitting."));
    cb(account, error, data);
    g_error_free(error);
    return;
    @@ -912,9 +904,9 @@
    "password migration.\n");
    if (cb == NULL)
    return;
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_INTERNAL,
    - _("Cannot save a password during password migration."));
    + error = g_error_new_literal(
    + PURPLE_KEYRING_ERROR, PURPLE_KEYRING_ERROR_INTERNAL,
    + _("Cannot save a password during password migration."));
    cb(account, error, data);
    g_error_free(error);
    return;
    @@ -924,9 +916,9 @@
    if (inuse == NULL) {
    if (cb == NULL)
    return;
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_NOKEYRING,
    - _("No keyring configured."));
    + error = g_error_new_literal(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_NOKEYRING,
    + _("No keyring configured."));
    cb(account, error, data);
    g_error_free(error);
    return;
    @@ -1292,10 +1284,13 @@
    plugins = purple_plugins_find_all();
    for (it = plugins; it != NULL; it = it->next) {
    PurplePlugin *plugin = PURPLE_PLUGIN(it->data);
    - PurplePluginInfo *info = purple_plugin_get_info(plugin);
    + GPluginPluginInfo *info =
    + GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin));
    - if (strncmp(purple_plugin_info_get_id(info), "keyring-", 8) != 0)
    + if (!purple_str_has_prefix(gplugin_plugin_info_get_id(info),
    + "keyring-")) {
    continue;
    + }
    if (purple_plugin_is_loaded(plugin))
    continue;
    --- a/libpurple/keyring.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/keyring.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_KEYRING_H_
    -#define _PURPLE_KEYRING_H_
    +#ifndef PURPLE_KEYRING_H
    +#define PURPLE_KEYRING_H
    /**
    * SECTION:keyring
    * @section_id: libpurple-keyring
    @@ -646,4 +646,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_KEYRING_H_ */
    +#endif /* PURPLE_KEYRING_H */
    --- a/libpurple/log.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/log.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1240,8 +1240,9 @@
    data = log->logger_data;
    /* if we can't write to the file, give up before we hurt ourselves */
    - if(!data->file)
    + if (!data || !data->file) {
    return 0;
    + }
    dt = g_date_time_to_local(log->time);
    date = g_date_time_format(dt, "%c");
    @@ -1889,10 +1890,9 @@
    set->name = set->normalized_name = name;
    /* Search the buddy list to find the account and to determine if this is a buddy. */
    - for (gnode = purple_blist_get_root();
    + for (gnode = purple_blist_get_default_root();
    !found && gnode != NULL;
    - gnode = purple_blist_node_get_sibling_next(gnode))
    - {
    + gnode = purple_blist_node_get_sibling_next(gnode)) {
    if (!PURPLE_IS_GROUP(gnode))
    continue;
    --- a/libpurple/log.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/log.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_LOG_H_
    -#define _PURPLE_LOG_H_
    +#ifndef PURPLE_LOG_H
    +#define PURPLE_LOG_H
    /**
    * SECTION:log
    * @section_id: libpurple-log
    @@ -67,7 +67,7 @@
    * @finalize: Called when the log is destroyed
    * @list: This function returns a sorted #GList of available PurpleLogs
    * @read: Given one of the logs returned by the logger's list function,
    - * this returns the contents of the log in #GtkWebView markup
    + * this returns the contents of the log
    * @size: Given one of the logs returned by the logger's list function,
    * this returns the size of the log in bytes
    * @total_size: Returns the total size of all the logs. If this is undefined a
    @@ -272,7 +272,7 @@
    *
    * Returns a list of all available logs
    *
    - * Returns: (element-type PurpleLog): A sorted list of logs
    + * Returns: (element-type PurpleLog) (transfer full): A sorted list of logs.
    */
    GList *purple_log_get_logs(PurpleLogType type, const char *name, PurpleAccount *account);
    @@ -292,8 +292,8 @@
    * destroyed. If a PurpleLogSet is removed from the GHashTable, it
    * must be freed with purple_log_set_free().
    *
    - * Returns: (element-type PurpleLogSet PurpleLogSet): All available unique log
    - * sets.
    + * Returns: (element-type PurpleLogSet PurpleLogSet) (transfer full): All
    + * available unique log sets.
    */
    GHashTable *purple_log_get_log_sets(void);
    @@ -303,7 +303,7 @@
    *
    * Returns a list of all available system logs
    *
    - * Returns: (element-type PurpleLog): A sorted list of logs
    + * Returns: (element-type PurpleLog) (transfer full): A sorted list of logs.
    */
    GList *purple_log_get_system_logs(PurpleAccount *account);
    @@ -449,7 +449,8 @@
    * It should only be passed to purple_log_logger_new() and never
    * called directly.
    *
    - * Returns: (element-type PurpleLog): A sorted list of logs matching the parameters.
    + * Returns: (element-type PurpleLog) (transfer full): A sorted list of logs
    + * matching the parameters.
    */
    GList *purple_log_common_lister(PurpleLogType type, const char *name,
    PurpleAccount *account, const char *ext,
    @@ -554,7 +555,7 @@
    *
    * Creates a new logger
    *
    - * Returns: The new logger
    + * Returns: (transfer full): The new logger.
    */
    PurpleLogLogger *purple_log_logger_new(const char *id, const char *name, int functions, ...);
    @@ -595,9 +596,9 @@
    *
    * Returns the current logger
    *
    - * Returns: logger The current logger
    + * Returns: (transfer none): The current logger.
    */
    -PurpleLogLogger *purple_log_logger_get (void);
    +PurpleLogLogger *purple_log_logger_get(void);
    /**
    * purple_log_logger_get_options:
    @@ -639,4 +640,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_LOG_H_ */
    +#endif /* PURPLE_LOG_H */
    --- a/libpurple/media-gst.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media-gst.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_MEDIA_GST_H_
    -#define _PURPLE_MEDIA_GST_H_
    +#ifndef PURPLE_MEDIA_GST_H
    +#define PURPLE_MEDIA_GST_H
    /**
    * SECTION:media-gst
    * @section_id: libpurple-media-gst
    @@ -239,4 +239,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_MEDIA_GST_H_ */
    +#endif /* PURPLE_MEDIA_GST_H */
    --- a/libpurple/media.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1424,7 +1424,7 @@
    gchar dtmf, guint8 volume, guint16 duration)
    {
    #ifdef USE_VV
    - PurpleMediaBackendIface *backend_iface = NULL;
    + PurpleMediaBackendInterface *backend_iface = NULL;
    if (media)
    {
    --- a/libpurple/media.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_MEDIA_H_
    -#define _PURPLE_MEDIA_H_
    +#ifndef PURPLE_MEDIA_H
    +#define PURPLE_MEDIA_H
    /**
    * SECTION:media
    * @section_id: libpurple-media
    @@ -110,7 +110,7 @@
    *
    * Gets the PurpleAccount this media session is on.
    *
    - * Returns: The account retrieved.
    + * Returns: (transfer none): The account retrieved.
    */
    PurpleAccount *purple_media_get_account(PurpleMedia *media);
    @@ -185,8 +185,10 @@
    * - "sdes-note" : The NOTE to put in SDES messages
    * - "sdes-phone" : The PHONE to put in SDES messages
    */
    +G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    void purple_media_set_params(PurpleMedia *media,
    guint num_params, GParameter *params);
    +G_GNUC_END_IGNORE_DEPRECATIONS
    /**
    * purple_media_get_available_params:
    @@ -196,7 +198,8 @@
    *
    * The list is owned by the #PurpleMedia internals and should NOT be freed.
    *
    - * Returns: NULL-terminated array of names of supported parameters.
    + * Returns: (array zero-terminated=1) (transfer none): Names of supported
    + * parameters.
    */
    const gchar **purple_media_get_available_params(PurpleMedia *media);
    @@ -229,10 +232,12 @@
    *
    * Returns: %TRUE The stream was added successfully, %FALSE otherwise.
    */
    +G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    gboolean purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
    const gchar *who, PurpleMediaSessionType type,
    gboolean initiator, const gchar *transmitter,
    guint num_params, GParameter *params);
    +G_GNUC_END_IGNORE_DEPRECATIONS
    /**
    * purple_media_get_session_type:
    @@ -262,7 +267,8 @@
    *
    * Gets the codecs from a session.
    *
    - * Returns: (element-type PurpleMediaCodec): The retrieved codecs.
    + * Returns: (element-type PurpleMediaCodec) (transfer full): The retrieved
    + * codecs.
    */
    GList *purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id);
    @@ -289,7 +295,8 @@
    *
    * Gets the local candidates from a stream.
    *
    - * Returns: (element-type PurpleMediaCandidate): The local candidates.
    + * Returns: (element-type PurpleMediaCandidate) (transfer full): The local
    + * candidates.
    */
    GList *purple_media_get_local_candidates(PurpleMedia *media,
    const gchar *sess_id,
    @@ -304,7 +311,7 @@
    *
    * Gets the active local candidates for the stream.
    *
    - * Returns: (element-type PurpleMediaCandidate): The active
    + * Returns: (element-type PurpleMediaCandidate) (transfer full): The active
    * candidates retrieved.
    */
    GList *purple_media_get_active_local_candidates(PurpleMedia *media,
    @@ -319,7 +326,7 @@
    *
    * Gets the active remote candidates for the stream.
    *
    - * Returns: (element-type PurpleMediaCandidate): The remote
    + * Returns: (element-type PurpleMediaCandidate) (transfer full): The remote
    * candidates retrieved.
    */
    GList *purple_media_get_active_remote_candidates(PurpleMedia *media,
    @@ -515,4 +522,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_MEDIA_H_ */
    +#endif /* PURPLE_MEDIA_H */
    --- a/libpurple/media/backend-fs2.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media/backend-fs2.c Tue Oct 08 21:48:28 2019 -0500
    @@ -35,16 +35,12 @@
    #include <farstream/fs-utils.h>
    #include <gst/gststructure.h>
    -#if !GST_CHECK_VERSION(1,0,0)
    -#define gst_registry_get() gst_registry_get_default()
    -#endif
    -
    /** @copydoc _PurpleMediaBackendFs2Session */
    typedef struct _PurpleMediaBackendFs2Session PurpleMediaBackendFs2Session;
    /** @copydoc _PurpleMediaBackendFs2Stream */
    typedef struct _PurpleMediaBackendFs2Stream PurpleMediaBackendFs2Stream;
    -static void purple_media_backend_iface_init(PurpleMediaBackendIface *iface);
    +static void purple_media_backend_iface_init(PurpleMediaBackendInterface *iface);
    static gboolean
    gst_bus_cb(GstBus *bus, GstMessage *msg, PurpleMediaBackendFs2 *self);
    @@ -510,7 +506,7 @@
    }
    static void
    -purple_media_backend_iface_init(PurpleMediaBackendIface *iface)
    +purple_media_backend_iface_init(PurpleMediaBackendInterface *iface)
    {
    iface->add_stream = purple_media_backend_fs2_add_stream;
    iface->add_remote_candidates =
    @@ -1891,34 +1887,6 @@
    (GSourceFunc)src_pad_added_cb_cb, stream);
    }
    -#ifdef HAVE_FARSIGHT
    -static GValueArray *
    -append_relay_info(GValueArray *relay_info, const gchar *ip, gint port,
    - const gchar *username, const gchar *password, const gchar *type)
    -{
    - GValue value;
    - GstStructure *turn_setup = gst_structure_new("relay-info",
    - "ip", G_TYPE_STRING, ip,
    - "port", G_TYPE_UINT, port,
    - "username", G_TYPE_STRING, username,
    - "password", G_TYPE_STRING, password,
    - "relay-type", G_TYPE_STRING, type,
    - NULL);
    -
    - if (turn_setup) {
    - memset(&value, 0, sizeof(GValue));
    - g_value_init(&value, GST_TYPE_STRUCTURE);
    - gst_value_set_structure(&value, turn_setup);
    -G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    - relay_info = g_value_array_append(relay_info, &value);
    -G_GNUC_END_IGNORE_DEPRECATIONS
    - gst_structure_free(turn_setup);
    - }
    -
    - return relay_info;
    -}
    -#endif
    -
    static gboolean
    create_stream(PurpleMediaBackendFs2 *self,
    const gchar *sess_id, const gchar *who,
    @@ -1944,10 +1912,8 @@
    TURN modes, like Google f.ex. */
    gboolean got_turn_from_protocol = FALSE;
    guint i;
    -#ifndef HAVE_FARSIGHT
    GPtrArray *relay_info = g_ptr_array_new_full (1, (GDestroyNotify) gst_structure_free);
    gboolean ret;
    -#endif
    session = get_session(self, sess_id);
    @@ -1971,10 +1937,8 @@
    if (fsstream == NULL) {
    if (err) {
    - purple_debug_error("backend-fs2",
    - "Error creating stream: %s\n",
    - err && err->message ?
    - err->message : "NULL");
    + purple_debug_error("backend-fs2", "Error creating stream: %s\n",
    + err->message ? err->message : "NULL");
    g_error_free(err);
    } else
    purple_debug_error("backend-fs2",
    @@ -2009,11 +1973,6 @@
    }
    if (turn_ip && purple_strequal("nice", transmitter) && !got_turn_from_protocol) {
    -G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    -#ifdef HAVE_FARSIGHT
    - GValueArray *relay_info = g_value_array_new(0);
    -#endif
    -G_GNUC_END_IGNORE_DEPRECATIONS
    gint port;
    const gchar *username = purple_prefs_get_string(
    "/purple/network/turn_username");
    @@ -2023,10 +1982,6 @@
    /* UDP */
    port = purple_prefs_get_int("/purple/network/turn_port");
    if (port > 0) {
    -#ifdef HAVE_FARSIGHT
    - relay_info = append_relay_info(relay_info, turn_ip, port, username,
    - password, "udp");
    -#else
    g_ptr_array_add (relay_info,
    gst_structure_new ("relay-info",
    "ip", G_TYPE_STRING, turn_ip,
    @@ -2035,16 +1990,11 @@
    "password", G_TYPE_STRING, password,
    "relay-type", G_TYPE_STRING, "udp",
    NULL));
    -#endif
    }
    /* TCP */
    port = purple_prefs_get_int("/purple/network/turn_port_tcp");
    if (port > 0) {
    -#ifdef HAVE_FARSIGHT
    - relay_info = append_relay_info(relay_info, turn_ip, port, username,
    - password, "tcp");
    -#else
    g_ptr_array_add (relay_info,
    gst_structure_new ("relay-info",
    "ip", G_TYPE_STRING, turn_ip,
    @@ -2053,7 +2003,6 @@
    "password", G_TYPE_STRING, password,
    "relay-type", G_TYPE_STRING, "tcp",
    NULL));
    -#endif
    }
    /* TURN over SSL is only supported by libnice for Google's "psuedo" SSL mode
    @@ -2062,39 +2011,11 @@
    purple_debug_info("backend-fs2",
    "Setting relay-info on new stream\n");
    _params[_num_params].name = "relay-info";
    -G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    -#ifdef HAVE_FARSIGHT
    - g_value_init(&_params[_num_params].value, G_TYPE_VALUE_ARRAY);
    - g_value_set_boxed(&_params[_num_params].value, relay_info);
    - g_value_array_free(relay_info);
    -#else
    g_value_init(&_params[_num_params].value, G_TYPE_PTR_ARRAY);
    g_value_set_boxed(&_params[_num_params].value, relay_info);
    -#endif
    -G_GNUC_END_IGNORE_DEPRECATIONS
    _num_params++;
    }
    -#ifdef HAVE_FARSIGHT
    - fsstream = fs_session_new_stream(session->session, participant,
    - initiator == TRUE ? type_direction :
    - (type_direction & FS_DIRECTION_RECV), transmitter,
    - _num_params, _params, &err);
    - g_free(_params);
    -
    - if (fsstream == NULL) {
    - if (err) {
    - purple_debug_error("backend-fs2",
    - "Error creating stream: %s\n",
    - err && err->message ?
    - err->message : "NULL");
    - g_error_free(err);
    - } else
    - purple_debug_error("backend-fs2",
    - "Error creating stream\n");
    - return FALSE;
    - }
    -#else
    ret = fs_stream_set_transmitter(fsstream, transmitter,
    _params, _num_params, &err);
    for (i = 0 ; i < _num_params ; i++)
    @@ -2109,7 +2030,6 @@
    g_clear_error(&err);
    return FALSE;
    }
    -#endif
    stream = g_new0(PurpleMediaBackendFs2Stream, 1);
    stream->participant = g_strdup(who);
    --- a/libpurple/media/backend-fs2.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media/backend-fs2.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _MEDIA_BACKEND_FS2_H_
    -#define _MEDIA_BACKEND_FS2_H_
    +#ifndef PURPLE_MEDIA_BACKEND_FS2_H
    +#define PURPLE_MEDIA_BACKEND_FS2_H
    /*
    * SECTION:backend-fs2
    * @section_id: libpurple-backend-fs2
    @@ -66,4 +66,4 @@
    G_END_DECLS
    -#endif /* _MEDIA_BACKEND_FS2_H_ */
    +#endif /* PURPLE_MEDIA_BACKEND_FS2_H */
    --- a/libpurple/media/backend-iface.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media/backend-iface.c Tue Oct 08 21:48:28 2019 -0500
    @@ -101,7 +101,7 @@
    static GType iface_type = 0;
    if (iface_type == 0) {
    static const GTypeInfo info = {
    - sizeof(PurpleMediaBackendIface),
    + sizeof(PurpleMediaBackendInterface),
    purple_media_backend_base_init,
    NULL,
    NULL,
    @@ -195,7 +195,7 @@
    const gchar *sess_id, const gchar *cipher,
    const gchar *auth, const gchar *key, gsize key_len)
    {
    - PurpleMediaBackendIface *backend_iface;
    + PurpleMediaBackendInterface *backend_iface;
    g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE);
    backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(self);
    @@ -210,7 +210,7 @@
    const gchar *cipher, const gchar *auth,
    const gchar *key, gsize key_len)
    {
    - PurpleMediaBackendIface *backend_iface;
    + PurpleMediaBackendInterface *backend_iface;
    g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE);
    backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(self);
    @@ -240,7 +240,7 @@
    purple_media_backend_set_send_rtcp_mux(PurpleMediaBackend *self,
    const gchar *sess_id, const gchar *participant, gboolean send_rtcp_mux)
    {
    - PurpleMediaBackendIface *backend_iface;
    + PurpleMediaBackendInterface *backend_iface;
    g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE);
    backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(self);
    --- a/libpurple/media/backend-iface.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media/backend-iface.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _MEDIA_BACKEND_IFACE_H_
    -#define _MEDIA_BACKEND_IFACE_H_
    +#ifndef PURPLE_MEDIA_BACKEND_IFACE_H
    +#define PURPLE_MEDIA_BACKEND_IFACE_H
    /**
    * SECTION:backend-iface
    * @section_id: libpurple-backend-iface
    @@ -38,7 +38,7 @@
    #define PURPLE_TYPE_MEDIA_BACKEND (purple_media_backend_get_type())
    #define PURPLE_IS_MEDIA_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_BACKEND))
    #define PURPLE_MEDIA_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_BACKEND, PurpleMediaBackend))
    -#define PURPLE_MEDIA_BACKEND_GET_INTERFACE(inst)(G_TYPE_INSTANCE_GET_INTERFACE((inst), PURPLE_TYPE_MEDIA_BACKEND, PurpleMediaBackendIface))
    +#define PURPLE_MEDIA_BACKEND_GET_INTERFACE(inst)(G_TYPE_INSTANCE_GET_INTERFACE((inst), PURPLE_TYPE_MEDIA_BACKEND, PurpleMediaBackendInterface))
    /**
    * PurpleMediaBackend:
    @@ -48,13 +48,13 @@
    typedef struct _PurpleMediaBackend PurpleMediaBackend;
    /**
    - * PurpleMediaBackendIface:
    + * PurpleMediaBackendInterface:
    *
    * A structure to derive media backends from.
    */
    -typedef struct _PurpleMediaBackendIface PurpleMediaBackendIface;
    +typedef struct _PurpleMediaBackendInterface PurpleMediaBackendInterface;
    -struct _PurpleMediaBackendIface
    +struct _PurpleMediaBackendInterface
    {
    /*< private >*/
    GTypeInterface parent_iface; /* The parent iface class */
    @@ -287,4 +287,4 @@
    G_END_DECLS
    -#endif /* _MEDIA_BACKEND_IFACE_H_ */
    +#endif /* PURPLE_MEDIA_BACKEND_IFACE_H */
    --- a/libpurple/media/candidate.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media/candidate.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_MEDIA_CANDIDATE_H_
    -#define _PURPLE_MEDIA_CANDIDATE_H_
    +#ifndef PURPLE_MEDIA_CANDIDATE_H
    +#define PURPLE_MEDIA_CANDIDATE_H
    /**
    * SECTION:candidate
    * @section_id: libpurple-candidate
    @@ -71,7 +71,7 @@
    *
    * Copies a PurpleMediaCandidate.
    *
    - * Returns: The copy of the PurpleMediaCandidate.
    + * Returns: (transfer full): The copy of the PurpleMediaCandidate.
    */
    PurpleMediaCandidate *purple_media_candidate_copy(
    PurpleMediaCandidate *candidate);
    @@ -83,7 +83,8 @@
    *
    * Copies a GList of PurpleMediaCandidate and its contents.
    *
    - * Returns: (element-type PurpleMediaCandidate): The copy of the GList.
    + * Returns: (element-type PurpleMediaCandidate) (transfer full): The copy of
    + * the candidate list.
    */
    GList *purple_media_candidate_list_copy(GList *candidates);
    @@ -225,5 +226,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_MEDIA_CANDIDATE_H_ */
    -
    +#endif /* PURPLE_MEDIA_CANDIDATE_H */
    --- a/libpurple/media/codec.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media/codec.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_MEDIA_CODEC_H_
    -#define _PURPLE_MEDIA_CODEC_H_
    +#ifndef PURPLE_MEDIA_CODEC_H
    +#define PURPLE_MEDIA_CODEC_H
    /**
    * SECTION:codec
    * @section_id: libpurple-codec
    @@ -161,7 +161,7 @@
    *
    * Copies a PurpleMediaCodec object.
    *
    - * Returns: The copy of the codec.
    + * Returns: (transfer full): The copy of the codec.
    */
    PurpleMediaCodec *purple_media_codec_copy(PurpleMediaCodec *codec);
    @@ -172,7 +172,8 @@
    *
    * Copies a GList of PurpleMediaCodec and its contents.
    *
    - * Returns: (element-type PurpleMediaCodec): The copy of the GList.
    + * Returns: (element-type PurpleMediaCodec) (transfer full): The copy of the
    + * codec list.
    */
    GList *purple_media_codec_list_copy(GList *codecs);
    @@ -197,5 +198,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_MEDIA_CODEC_H_ */
    -
    +#endif /* PURPLE_MEDIA_CODEC_H */
    --- a/libpurple/media/enum-types.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/media/enum-types.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_MEDIA_ENUM_TYPES_H_
    -#define _PURPLE_MEDIA_ENUM_TYPES_H_
    +#ifndef PURPLE_MEDIA_ENUM_TYPES_H
    +#define PURPLE_MEDIA_ENUM_TYPES_H
    /**
    * SECTION:enum-types
    * @section_id: libpurple-enum-types
    @@ -194,4 +194,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_MEDIA_ENUM_TYPES_ */
    +#endif /* PURPLE_MEDIA_ENUM_TYPES */
    --- a/libpurple/mediamanager.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/mediamanager.c Tue Oct 08 21:48:28 2019 -0500
    @@ -957,14 +957,13 @@
    cb_data = info->user_data;
    g_mutex_unlock (&manager->priv->appdata_mutex);
    - if (readable_cb)
    - readable_cb (manager, media, session_id, participant, cb_data);
    + readable_cb(manager, media, session_id, participant, cb_data);
    g_mutex_lock (&manager->priv->appdata_mutex);
    g_object_unref (media);
    g_free (session_id);
    g_free (participant);
    - if (cb_token == 0 || cb_token != *cb_token_ptr) {
    + if (cb_token != *cb_token_ptr) {
    /* We got cancelled */
    g_mutex_unlock (&manager->priv->appdata_mutex);
    return FALSE;
    --- a/libpurple/mediamanager.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/mediamanager.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_MEDIA_MANAGER_H_
    -#define _PURPLE_MEDIA_MANAGER_H_
    +#ifndef PURPLE_MEDIA_MANAGER_H
    +#define PURPLE_MEDIA_MANAGER_H
    /**
    * SECTION:mediamanager
    * @section_id: libpurple-mediamanager
    @@ -366,4 +366,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_MEDIA_MANAGER_H_ */
    +#endif /* PURPLE_MEDIA_MANAGER_H */
    --- a/libpurple/memorypool.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/memorypool.c Tue Oct 08 21:48:28 2019 -0500
    @@ -99,12 +99,13 @@
    static gpointer
    purple_memory_pool_alloc_impl(PurpleMemoryPool *pool, gsize size, guint alignment)
    {
    - PurpleMemoryPoolPrivate *priv =
    - purple_memory_pool_get_instance_private(pool);
    + PurpleMemoryPoolPrivate *priv = NULL;
    PurpleMemoryPoolBlock *blk;
    gpointer mem = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_MEMORY_POOL(pool), NULL);
    +
    + priv = purple_memory_pool_get_instance_private(pool);
    if (priv->disabled) {
    /* XXX: this may cause some leaks */
    @@ -167,8 +168,6 @@
    purple_memory_pool_get_instance_private(pool);
    PurpleMemoryPoolBlock *blk;
    - g_return_if_fail(priv != NULL);
    -
    blk = priv->first_block;
    priv->first_block = NULL;
    priv->last_block = NULL;
    @@ -187,11 +186,11 @@
    void
    purple_memory_pool_set_block_size(PurpleMemoryPool *pool, gulong block_size)
    {
    - PurpleMemoryPoolPrivate *priv =
    - purple_memory_pool_get_instance_private(pool);
    + PurpleMemoryPoolPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_MEMORY_POOL(pool));
    + priv = purple_memory_pool_get_instance_private(pool);
    priv->block_size = block_size;
    g_object_notify_by_pspec(G_OBJECT(pool), properties[PROP_BLOCK_SIZE]);
    }
    --- a/libpurple/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -161,7 +161,7 @@
    purple_filebase = 'purple-@0@'.format(purple_major_version)
    purple_include_base = join_paths(purple_filebase, 'libpurple')
    -if enable_gst
    +if gstreamer.found()
    purple_coresources += 'media/backend-fs2.c'
    endif
    if enable_vv
    @@ -295,7 +295,7 @@
    purple_mediaheaders)
    Purple_gir_includes = ['GObject-2.0', 'Gio-2.0', gplugin_gir]
    - if enable_gst
    + if gstreamer.found()
    Purple_gir_includes += ['Gst-1.0']
    endif
    --- a/libpurple/message.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/message.c Tue Oct 08 21:48:28 2019 -0500
    @@ -127,10 +127,11 @@
    guint
    purple_message_get_id(PurpleMessage *msg)
    {
    - PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
    + PurpleMessagePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_MESSAGE(msg), 0);
    + priv = purple_message_get_instance_private(msg);
    return priv->id;
    }
    @@ -145,20 +146,22 @@
    const gchar *
    purple_message_get_author(PurpleMessage *msg)
    {
    - PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
    + PurpleMessagePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_MESSAGE(msg), NULL);
    + priv = purple_message_get_instance_private(msg);
    return priv->author;
    }
    const gchar *
    purple_message_get_recipient(PurpleMessage *msg)
    {
    - PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
    + PurpleMessagePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_MESSAGE(msg), NULL);
    + priv = purple_message_get_instance_private(msg);
    return priv->recipient;
    }
    @@ -171,9 +174,10 @@
    const gchar *
    purple_message_get_author_alias(PurpleMessage *msg)
    {
    - PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
    + PurpleMessagePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_MESSAGE(msg), NULL);
    + priv = purple_message_get_instance_private(msg);
    if (priv->author_alias == NULL)
    return purple_message_get_author(msg);
    @@ -190,10 +194,11 @@
    const gchar *
    purple_message_get_contents(PurpleMessage *msg)
    {
    - PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
    + PurpleMessagePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_MESSAGE(msg), NULL);
    + priv = purple_message_get_instance_private(msg);
    return priv->contents;
    }
    @@ -214,10 +219,11 @@
    guint64
    purple_message_get_time(PurpleMessage *msg)
    {
    - PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
    + PurpleMessagePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_MESSAGE(msg), 0);
    + priv = purple_message_get_instance_private(msg);
    return priv->msgtime;
    }
    @@ -230,10 +236,11 @@
    PurpleMessageFlags
    purple_message_get_flags(PurpleMessage *msg)
    {
    - PurpleMessagePrivate *priv = purple_message_get_instance_private(msg);
    + PurpleMessagePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_MESSAGE(msg), 0);
    + priv = purple_message_get_instance_private(msg);
    return priv->flags;
    }
    --- a/libpurple/message.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/message.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_MESSAGE_H_
    -#define _PURPLE_MESSAGE_H_
    +#ifndef PURPLE_MESSAGE_H
    +#define PURPLE_MESSAGE_H
    /**
    * SECTION:message
    * @include:message.h
    @@ -111,7 +111,7 @@
    *
    * Finds the message with a given @id.
    *
    - * Returns: the #PurpleMessage, or %NULL if not found.
    + * Returns: (transfer none): The #PurpleMessage, or %NULL if not found.
    */
    PurpleMessage *
    purple_message_find_by_id(guint id);
    @@ -237,4 +237,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_MESSAGE_H_ */
    +#endif /* PURPLE_MESSAGE_H */
    --- a/libpurple/nat-pmp.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/nat-pmp.h Tue Oct 08 21:48:28 2019 -0500
    @@ -24,8 +24,8 @@
    * OF SUCH DAMAGE.
    */
    -#ifndef _PURPLE_NAT_PMP_H
    -#define _PURPLE_NAT_PMP_H
    +#ifndef PURPLE_NAT_PMP_H
    +#define PURPLE_NAT_PMP_H
    /**
    * SECTION:nat-pmp
    * @section_id: libpurple-nat-pmp
    @@ -81,5 +81,4 @@
    G_END_DECLS
    -#endif
    -
    +#endif /* PURPLE_NAT_PMP_H */
    --- a/libpurple/network.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/network.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_NETWORK_H_
    -#define _PURPLE_NETWORK_H_
    +#ifndef PURPLE_NETWORK_H
    +#define PURPLE_NETWORK_H
    /**
    * SECTION:network
    * @section_id: libpurple-network
    @@ -87,7 +87,7 @@
    * Note: The caller must free this list. If libpurple was built with
    * support for it, this function also enumerates IPv6 addresses.
    *
    - * Returns: (element-type utf8): A list of local IP addresses.
    + * Returns: (element-type utf8) (transfer full): A list of local IP addresses.
    */
    GList *purple_network_get_all_local_system_ips(void);
    @@ -316,4 +316,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_NETWORK_H_ */
    +#endif /* PURPLE_NETWORK_H */
    --- a/libpurple/notify.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/notify.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#define _PURPLE_NOTIFY_C_
    #include "internal.h"
    #include "notify.h"
    @@ -267,8 +266,7 @@
    for (l = results->rows; l; l = g_list_delete_link(l, l)) {
    GList *row = l->data;
    - g_list_foreach(row, (GFunc)g_free, NULL);
    - g_list_free(row);
    + g_list_free_full(row, g_free);
    }
    for (l = results->columns; l; l = g_list_delete_link(l, l)) {
    --- a/libpurple/notify.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/notify.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_NOTIFY_H_
    -#define _PURPLE_NOTIFY_H_
    +#ifndef PURPLE_NOTIFY_H
    +#define PURPLE_NOTIFY_H
    /**
    * SECTION:notify
    * @section_id: libpurple-notify
    @@ -877,4 +877,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_NOTIFY_H_ */
    +#endif /* PURPLE_NOTIFY_H */
    --- a/libpurple/pluginpref.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/pluginpref.c Tue Oct 08 21:48:28 2019 -0500
    @@ -65,8 +65,7 @@
    {
    g_return_if_fail(frame != NULL);
    - g_list_foreach(frame->prefs, (GFunc)purple_plugin_pref_destroy, NULL);
    - g_list_free(frame->prefs);
    + g_list_free_full(frame->prefs, (GDestroyNotify)purple_plugin_pref_destroy);
    g_free(frame);
    }
    --- a/libpurple/pluginpref.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/pluginpref.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    *
    */
    -#ifndef _PURPLE_PLUGINPREF_H_
    -#define _PURPLE_PLUGINPREF_H_
    +#ifndef PURPLE_PLUGINPREF_H
    +#define PURPLE_PLUGINPREF_H
    /**
    * SECTION:pluginpref
    * @section_id: libpurple-pluginpref
    @@ -309,4 +309,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_PLUGINPREF_H_ */
    +#endif /* PURPLE_PLUGINPREF_H */
    --- a/libpurple/plugins.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins.c Tue Oct 08 21:48:28 2019 -0500
    @@ -95,7 +95,8 @@
    if (priv->error) {
    purple_debug_error("plugins", "Failed to load plugin %s: %s",
    - purple_plugin_get_filename(plugin), priv->error);
    + gplugin_plugin_get_filename(plugin),
    + priv->error);
    g_set_error(error, PURPLE_PLUGINS_DOMAIN, 0,
    "Plugin is not loadable: %s", priv->error);
    @@ -120,7 +121,7 @@
    loaded_plugins = g_list_prepend(loaded_plugins, plugin);
    purple_debug_info("plugins", "Loaded plugin %s\n",
    - purple_plugin_get_filename(plugin));
    + gplugin_plugin_get_filename(plugin));
    purple_signal_emit(purple_plugins_get_handle(), "plugin-load", plugin);
    }
    @@ -136,7 +137,7 @@
    info = purple_plugin_get_info(plugin);
    if (info) {
    purple_debug_info("plugins", "Unloading plugin %s\n",
    - purple_plugin_get_filename(plugin));
    + gplugin_plugin_get_filename(plugin));
    }
    return TRUE;
    @@ -185,8 +186,8 @@
    if (!gplugin_manager_load_plugin(plugin, &err)) {
    purple_debug_error("plugins", "Failed to load plugin %s: %s",
    - purple_plugin_get_filename(plugin),
    - (err ? err->message : "Unknown reason"));
    + gplugin_plugin_get_filename(plugin),
    + err ? err->message : "Unknown reason");
    if (error)
    *error = g_error_copy(err);
    @@ -210,8 +211,8 @@
    if (!gplugin_manager_unload_plugin(plugin, &err)) {
    purple_debug_error("plugins", "Failed to unload plugin %s: %s",
    - purple_plugin_get_filename(plugin),
    - (err ? err->message : "Unknown reason"));
    + gplugin_plugin_get_filename(plugin),
    + err ? err->message : "Unknown reason");
    if (error)
    *error = g_error_copy(err);
    @@ -231,14 +232,6 @@
    return (gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED);
    }
    -const gchar *
    -purple_plugin_get_filename(PurplePlugin *plugin)
    -{
    - g_return_val_if_fail(plugin != NULL, NULL);
    -
    - return gplugin_plugin_get_filename(plugin);
    -}
    -
    PurplePluginInfo *
    purple_plugin_get_info(PurplePlugin *plugin)
    {
    @@ -269,29 +262,6 @@
    plugins_to_disable = g_list_prepend(plugins_to_disable, plugin);
    }
    -GType
    -purple_plugin_register_type(PurplePlugin *plugin, GType parent,
    - const gchar *name, const GTypeInfo *info,
    - GTypeFlags flags)
    -{
    - g_return_val_if_fail(G_IS_TYPE_MODULE(plugin), G_TYPE_INVALID);
    -
    - return g_type_module_register_type(G_TYPE_MODULE(plugin),
    - parent, name, info, flags);
    -}
    -
    -void
    -purple_plugin_add_interface(PurplePlugin *plugin, GType instance_type,
    - GType interface_type,
    - const GInterfaceInfo *interface_info)
    -{
    - g_return_if_fail(G_IS_TYPE_MODULE(plugin));
    -
    - g_type_module_add_interface(G_TYPE_MODULE(plugin),
    - instance_type, interface_type,
    - interface_info);
    -}
    -
    gboolean
    purple_plugin_is_internal(PurplePlugin *plugin)
    {
    @@ -395,9 +365,10 @@
    purple_plugin_info_constructed(GObject *object)
    {
    PurplePluginInfo *info = PURPLE_PLUGIN_INFO(object);
    + GPluginPluginInfo *ginfo = GPLUGIN_PLUGIN_INFO(info);
    PurplePluginInfoPrivate *priv =
    purple_plugin_info_get_instance_private(info);
    - const char *id = purple_plugin_info_get_id(info);
    + const char *id = gplugin_plugin_info_get_id(ginfo);
    guint32 version;
    G_OBJECT_CLASS(purple_plugin_info_parent_class)->constructed(object);
    @@ -413,7 +384,7 @@
    id, priv->error);
    }
    - version = purple_plugin_info_get_abi_version(info);
    + version = gplugin_plugin_info_get_abi_version(ginfo);
    if (PURPLE_PLUGIN_ABI_MAJOR_VERSION(version) != PURPLE_MAJOR_VERSION ||
    PURPLE_PLUGIN_ABI_MINOR_VERSION(version) > PURPLE_MINOR_VERSION)
    {
    @@ -518,181 +489,69 @@
    return PURPLE_PLUGIN_INFO(info);
    }
    -const gchar *
    -purple_plugin_info_get_id(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_id(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_name(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_version(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_version(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_category(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_category(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_summary(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_summary(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_description(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_description(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar * const *
    -purple_plugin_info_get_authors(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_authors(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_website(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_website(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_icon(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_icon(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_license_id(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_license_id(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_license_text(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_license_text(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar *
    -purple_plugin_info_get_license_url(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_license_url(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -const gchar * const *
    -purple_plugin_info_get_dependencies(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, NULL);
    -
    - return gplugin_plugin_info_get_dependencies(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    -guint32
    -purple_plugin_info_get_abi_version(const PurplePluginInfo *info)
    -{
    - g_return_val_if_fail(info != NULL, 0);
    -
    - return gplugin_plugin_info_get_abi_version(GPLUGIN_PLUGIN_INFO(info));
    -}
    -
    PurplePluginActionsCb
    purple_plugin_info_get_actions_cb(PurplePluginInfo *info)
    {
    - PurplePluginInfoPrivate *priv =
    - purple_plugin_info_get_instance_private(info);
    + PurplePluginInfoPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
    + priv = purple_plugin_info_get_instance_private(info);
    return priv->actions_cb;
    }
    PurplePluginExtraCb
    purple_plugin_info_get_extra_cb(PurplePluginInfo *info)
    {
    - PurplePluginInfoPrivate *priv =
    - purple_plugin_info_get_instance_private(info);
    + PurplePluginInfoPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
    + priv = purple_plugin_info_get_instance_private(info);
    return priv->extra_cb;
    }
    PurplePluginPrefFrameCb
    purple_plugin_info_get_pref_frame_cb(PurplePluginInfo *info)
    {
    - PurplePluginInfoPrivate *priv =
    - purple_plugin_info_get_instance_private(info);
    + PurplePluginInfoPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
    + priv = purple_plugin_info_get_instance_private(info);
    return priv->pref_frame_cb;
    }
    PurplePluginPrefRequestCb
    purple_plugin_info_get_pref_request_cb(PurplePluginInfo *info)
    {
    - PurplePluginInfoPrivate *priv =
    - purple_plugin_info_get_instance_private(info);
    + PurplePluginInfoPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
    + priv = purple_plugin_info_get_instance_private(info);
    return priv->pref_request_cb;
    }
    PurplePluginInfoFlags
    purple_plugin_info_get_flags(PurplePluginInfo *info)
    {
    - PurplePluginInfoPrivate *priv =
    - purple_plugin_info_get_instance_private(info);
    + PurplePluginInfoPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), 0);
    + priv = purple_plugin_info_get_instance_private(info);
    return priv->flags;
    }
    const gchar *
    purple_plugin_info_get_error(PurplePluginInfo *info)
    {
    - PurplePluginInfoPrivate *priv =
    - purple_plugin_info_get_instance_private(info);
    + PurplePluginInfoPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
    + priv = purple_plugin_info_get_instance_private(info);
    return priv->error;
    }
    @@ -705,7 +564,7 @@
    }
    gpointer
    -purple_plugin_info_get_ui_data(const PurplePluginInfo *info)
    +purple_plugin_info_get_ui_data(PurplePluginInfo *info)
    {
    g_return_val_if_fail(PURPLE_IS_PLUGIN_INFO(info), NULL);
    @@ -747,19 +606,8 @@
    return purple_plugin_action_new(action->label, action->callback);
    }
    -GType
    -purple_plugin_action_get_type(void)
    -{
    - static GType type = 0;
    -
    - if (G_UNLIKELY(type == 0)) {
    - type = g_boxed_type_register_static("PurplePluginAction",
    - (GBoxedCopyFunc)purple_plugin_action_copy,
    - (GBoxedFreeFunc)purple_plugin_action_free);
    - }
    -
    - return type;
    -}
    +G_DEFINE_BOXED_TYPE(PurplePluginAction, purple_plugin_action,
    + purple_plugin_action_copy, purple_plugin_action_free)
    /**************************************************************************
    * Plugins API
    @@ -822,7 +670,7 @@
    if (!priv->unloaded && purple_plugin_info_get_flags(info) &
    PURPLE_PLUGIN_INFO_FLAGS_AUTO_LOAD) {
    purple_debug_info("plugins", "Auto-loading plugin %s\n",
    - purple_plugin_get_filename(plugin));
    + gplugin_plugin_get_filename(plugin));
    purple_plugin_load(plugin, NULL);
    }
    }
    @@ -863,7 +711,8 @@
    for (l = plugins; l != NULL; l = l->next) {
    PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
    - if (purple_strequal(purple_plugin_get_filename(plugin), filename)) {
    + if (purple_strequal(gplugin_plugin_get_filename(plugin),
    + filename)) {
    g_list_free(plugins);
    return plugin;
    }
    @@ -892,7 +741,9 @@
    continue;
    if (!g_list_find(plugins_to_disable, plugin))
    - files = g_list_append(files, (gchar *)purple_plugin_get_filename(plugin));
    + files = g_list_append(
    + files,
    + (gchar *)gplugin_plugin_get_filename(plugin));
    }
    purple_prefs_set_path_list(key, files);
    --- a/libpurple/plugins.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_PLUGINS_H_
    -#define _PURPLE_PLUGINS_H_
    +#ifndef PURPLE_PLUGINS_H
    +#define PURPLE_PLUGINS_H
    /**
    * SECTION:plugins
    * @section_id: libpurple-plugins
    @@ -54,17 +54,9 @@
    typedef GPluginPluginInterface PurplePluginInterface;
    #define PURPLE_TYPE_PLUGIN_INFO (purple_plugin_info_get_type())
    -#define PURPLE_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfo))
    -#define PURPLE_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfoClass))
    -#define PURPLE_IS_PLUGIN_INFO(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PLUGIN_INFO))
    -#define PURPLE_IS_PLUGIN_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_PLUGIN_INFO))
    -#define PURPLE_PLUGIN_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_PLUGIN_INFO, PurplePluginInfoClass))
    -
    typedef struct _PurplePluginInfo PurplePluginInfo;
    -typedef struct _PurplePluginInfoClass PurplePluginInfoClass;
    #define PURPLE_TYPE_PLUGIN_ACTION (purple_plugin_action_get_type())
    -
    typedef struct _PurplePluginAction PurplePluginAction;
    #include "pluginpref.h"
    @@ -145,18 +137,12 @@
    gpointer ui_data;
    };
    -struct _PurplePluginInfoClass {
    - GPluginPluginInfoClass parent_class;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    /**
    * PurplePluginAction:
    + * @label: The label to display in the user interface.
    + * @callback: The function to call when the user wants to perform this action.
    + * @plugin: The plugin that this action belongs to.
    + * @user_data: User data to pass to @callback.
    *
    * Represents an action that the plugin can perform. This shows up in the Tools
    * menu, under a submenu with the name of the plugin.
    @@ -222,113 +208,6 @@
    return pluginunload(PURPLE_PLUGIN(p), e); \
    }
    -/**
    - * PURPLE_DEFINE_TYPE:
    - * @TN: The name of the new type, in Camel case.
    - * @t_n: The name of the new type, in lowercase, words separated by '_'.
    - * @T_P: The #GType of the parent type.
    - *
    - * A convenience macro for type implementations, which defines a *_get_type()
    - * function; and a *_register_type() function for use in your plugin's load
    - * function. You must define an instance initialization function *_init()
    - * and a class initialization function *_class_init() for the type.
    - */
    -#define PURPLE_DEFINE_TYPE(TN, t_n, T_P) \
    - PURPLE_DEFINE_DYNAMIC_TYPE(TN, t_n, T_P)
    -
    -/**
    - * PURPLE_DEFINE_TYPE_EXTENDED:
    - * @TN: The name of the new type, in Camel case.
    - * @t_n: The name of the new type, in lowercase, words separated by '_'.
    - * @T_P: The #GType of the parent type.
    - * @flags: #GTypeFlags to register the type with.
    - * @CODE: Custom code that gets inserted in *_get_type().
    - *
    - * A more general version of PURPLE_DEFINE_TYPE() which allows you to
    - * specify #GTypeFlags and custom code.
    - */
    -#define PURPLE_DEFINE_TYPE_EXTENDED \
    - PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED
    -
    -/**
    - * PURPLE_IMPLEMENT_INTERFACE_STATIC:
    - * @TYPE_IFACE: The #GType of the interface to add.
    - * @iface_init: The interface init function.
    - *
    - * A convenience macro to ease static interface addition in the CODE section
    - * of PURPLE_DEFINE_TYPE_EXTENDED(). You should use this macro if the
    - * interface is a part of the libpurple core.
    - */
    -#define PURPLE_IMPLEMENT_INTERFACE_STATIC(TYPE_IFACE, iface_init) { \
    - const GInterfaceInfo interface_info = { \
    - (GInterfaceInitFunc) iface_init, NULL, NULL \
    - }; \
    - g_type_add_interface_static(type_id, TYPE_IFACE, &interface_info); \
    -}
    -
    -/**
    - * PURPLE_IMPLEMENT_INTERFACE:
    - * @TYPE_IFACE: The #GType of the interface to add.
    - * @iface_init: The interface init function.
    - *
    - * A convenience macro to ease interface addition in the CODE section
    - * of PURPLE_DEFINE_TYPE_EXTENDED(). You should use this macro if the
    - * interface lives in the plugin.
    - */
    -#define PURPLE_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init) \
    - PURPLE_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)
    -
    -/**
    - * PURPLE_DEFINE_DYNAMIC_TYPE:
    - *
    - * A convenience macro for dynamic type implementations.
    - */
    -#define PURPLE_DEFINE_DYNAMIC_TYPE(TN, t_n, T_P) \
    - PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED (TN, t_n, T_P, 0, {})
    -
    -/**
    - * PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED:
    - *
    - * A more general version of PURPLE_DEFINE_DYNAMIC_TYPE().
    - */
    -#define PURPLE_DEFINE_DYNAMIC_TYPE_EXTENDED(TypeName, type_name, TYPE_PARENT, flags, CODE) \
    -static GType type_name##_type_id = 0; \
    -G_MODULE_EXPORT GType type_name##_get_type(void) { \
    - return type_name##_type_id; \
    -} \
    -void type_name##_register_type(PurplePlugin *); \
    -void type_name##_register_type(PurplePlugin *plugin) { \
    - GType type_id; \
    - const GTypeInfo type_info = { \
    - sizeof (TypeName##Class), \
    - (GBaseInitFunc) NULL, \
    - (GBaseFinalizeFunc) NULL, \
    - (GClassInitFunc) type_name##_class_init, \
    - (GClassFinalizeFunc) NULL, \
    - NULL, \
    - sizeof (TypeName), \
    - 0, \
    - (GInstanceInitFunc) type_name##_init, \
    - NULL \
    - }; \
    - type_id = purple_plugin_register_type(plugin, TYPE_PARENT, #TypeName, \
    - &type_info, (GTypeFlags) flags); \
    - type_name##_type_id = type_id; \
    - { CODE ; } \
    -}
    -
    -/**
    - * PURPLE_IMPLEMENT_INTERFACE_DYNAMIC:
    - *
    - * A convenience macro to ease dynamic interface addition.
    - */
    -#define PURPLE_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) { \
    - const GInterfaceInfo interface_info = { \
    - (GInterfaceInitFunc) iface_init, NULL, NULL \
    - }; \
    - purple_plugin_add_interface(plugin, type_id, TYPE_IFACE, &interface_info); \
    -}
    -
    G_BEGIN_DECLS
    /**************************************************************************/
    @@ -374,16 +253,6 @@
    gboolean purple_plugin_is_loaded(PurplePlugin *plugin);
    /**
    - * purple_plugin_get_filename:
    - * @plugin: The plugin.
    - *
    - * Returns a plugin's filename, along with the path.
    - *
    - * Returns: The plugin's filename.
    - */
    -const gchar *purple_plugin_get_filename(PurplePlugin *plugin);
    -
    -/**
    * purple_plugin_get_info:
    * @plugin: The plugin.
    *
    @@ -410,37 +279,6 @@
    void purple_plugin_disable(PurplePlugin *plugin);
    /**
    - * purple_plugin_register_type:
    - * @plugin: The plugin that is registering the type.
    - * @parent: Type from which this type will be derived.
    - * @name: Name of the new type.
    - * @info: Information to initialize and destroy a type's classes and
    - * instances.
    - * @flags: Bitwise combination of values that determines the nature
    - * (e.g. abstract or not) of the type.
    - *
    - * Registers a new dynamic type.
    - *
    - * Returns: The new GType, or %G_TYPE_INVALID if registration failed.
    - */
    -GType purple_plugin_register_type(PurplePlugin *plugin, GType parent,
    - const gchar *name, const GTypeInfo *info,
    - GTypeFlags flags);
    -
    -/**
    - * purple_plugin_add_interface:
    - * @plugin: The plugin that is adding the interface type.
    - * @instance_type: The GType of the instantiable type.
    - * @interface_type: The GType of the interface type.
    - * @interface_info: Information used to manage the interface type.
    - *
    - * Adds a dynamic interface type to an instantiable type.
    - */
    -void purple_plugin_add_interface(PurplePlugin *plugin, GType instance_type,
    - GType interface_type,
    - const GInterfaceInfo *interface_info);
    -
    -/**
    * purple_plugin_is_internal:
    * @plugin: The plugin.
    *
    @@ -473,7 +311,8 @@
    *
    * Returns: The #GType for the #PurplePluginInfo object.
    */
    -GType purple_plugin_info_get_type(void);
    +G_DECLARE_FINAL_TYPE(PurplePluginInfo, purple_plugin_info, PURPLE, PLUGIN_INFO,
    + GPluginPluginInfo)
    /**
    * purple_plugin_info_new:
    @@ -570,148 +409,6 @@
    G_GNUC_NULL_TERMINATED;
    /**
    - * purple_plugin_info_get_id:
    - * @info: The plugin's info instance.
    - *
    - * Returns a plugin's ID.
    - *
    - * Returns: The plugin's ID.
    - */
    -const gchar *purple_plugin_info_get_id(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_name:
    - * @info: The plugin's info instance.
    - *
    - * Returns a plugin's translated name.
    - *
    - * Returns: The name of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_name(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_version:
    - * @info: The plugin's info instance.
    - *
    - * Returns a plugin's version.
    - *
    - * Returns: The version of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_version(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_category:
    - * @info: The plugin's info instance.
    - *
    - * Returns a plugin's primary category.
    - *
    - * Returns: The primary category of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_category(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_summary:
    - * @info: The plugin's info instance.
    - *
    - * Returns a plugin's summary.
    - *
    - * Returns: The summary of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_summary(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_description:
    - * @info: The plugin's info instance.
    - *
    - * Returns a plugin's description.
    - *
    - * Returns: The description of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_description(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_authors:
    - * @info: The plugin's info instance.
    - *
    - * Returns a NULL-terminated list of the plugin's authors.
    - *
    - * Returns: The authors of the plugin, or %NULL.
    - */
    -const gchar * const *
    -purple_plugin_info_get_authors(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_website:
    - * @info: The plugin's info instance.
    - *
    - * Returns a plugin's website.
    - *
    - * Returns: The website of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_website(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_icon:
    - * @info: The plugin's info instance.
    - *
    - * Returns the path to a plugin's icon.
    - *
    - * Returns: The path to the plugin's icon, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_icon(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_license_id:
    - * @info: The plugin's info instance.
    - *
    - * Returns a short name of the plugin's license.
    - *
    - * Returns: The license name of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_license_id(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_license_text:
    - * @info: The plugin's info instance.
    - *
    - * Returns the text of a plugin's license.
    - *
    - * Returns: The license text of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_license_text(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_license_url:
    - * @info: The plugin's info instance.
    - *
    - * Returns the URL of a plugin's license.
    - *
    - * Returns: The license URL of the plugin, or %NULL.
    - */
    -const gchar *purple_plugin_info_get_license_url(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_dependencies:
    - * @info: The plugin's info instance.
    - *
    - * Returns a NULL-terminated list of IDs of plugins required by a plugin.
    - *
    - * Returns: The dependencies of the plugin, or %NULL.
    - */
    -const gchar * const *
    -purple_plugin_info_get_dependencies(const PurplePluginInfo *info);
    -
    -/**
    - * purple_plugin_info_get_abi_version:
    - * @info: The plugin's info instance.
    - *
    - * Returns the required purple ABI version for a plugin.
    - *
    - * Returns: The required purple ABI version for the plugin.
    - */
    -guint32 purple_plugin_info_get_abi_version(const PurplePluginInfo *info);
    -
    -/**
    * purple_plugin_info_get_actions_cb:
    * @info: The plugin info to get the callback from.
    *
    @@ -801,7 +498,7 @@
    * convenience field provided to the UIs--it is not
    * used by the libpurple core.
    */
    -gpointer purple_plugin_info_get_ui_data(const PurplePluginInfo *info);
    +gpointer purple_plugin_info_get_ui_data(PurplePluginInfo *info);
    /**************************************************************************/
    /* PluginAction API */
    @@ -943,4 +640,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_PLUGINS_H_ */
    +#endif /* PURPLE_PLUGINS_H */
    --- a/libpurple/plugins/autoaccept.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/autoaccept.c Tue Oct 08 21:48:28 2019 -0500
    @@ -31,16 +31,7 @@
    #include <glib.h>
    #include <glib/gstdio.h>
    -/* Purple headers */
    -#include <plugins.h>
    -#include <version.h>
    -
    -#include <action.h>
    -#include <buddylist.h>
    -#include <conversation.h>
    -#include <xfer.h>
    -#include <request.h>
    -#include <notify.h>
    +#include <purple.h>
    #define PREF_PREFIX "/plugins/core/" PLUGIN_ID
    #define PREF_PATH PREF_PREFIX "/path"
    @@ -145,7 +136,10 @@
    /* Split at the first dot, to avoid uniquifying "foo.tar.gz" to "foo.tar-2.gz" */
    name_and_ext = g_strsplit(escape, ".", 2);
    name = name_and_ext[0];
    - g_return_if_fail(name != NULL);
    + if (name == NULL) {
    + g_strfreev(name_and_ext);
    + g_return_if_reached();
    + }
    if (name_and_ext[1] != NULL) {
    /* g_strsplit does not include the separator in each chunk. */
    ext = g_strdup_printf(".%s", name_and_ext[1]);
    --- a/libpurple/plugins/buddynote.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/buddynote.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,12 +18,7 @@
    */
    #include "internal.h"
    -#include <action.h>
    -#include <debug.h>
    -#include <notify.h>
    -#include <request.h>
    -#include <signals.h>
    -#include <version.h>
    +#include <purple.h>
    static void
    dont_do_it_cb(PurpleBlistNode *node, const char *note)
    --- a/libpurple/plugins/codeinline.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/codeinline.c Tue Oct 08 21:48:28 2019 -0500
    @@ -21,10 +21,8 @@
    */
    #include "internal.h"
    -#include "plugins.h"
    -#include "notify.h"
    -#include "util.h"
    -#include "version.h"
    +
    +#include <purple.h>
    PurplePlugin *plugin_handle = NULL;
    --- a/libpurple/plugins/filectl.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/filectl.c Tue Oct 08 21:48:28 2019 -0500
    @@ -16,14 +16,7 @@
    #include <ctype.h>
    #include "internal.h"
    -#include "account.h"
    -#include "config.h"
    -#include "core.h"
    -#include "conversation.h"
    -#include "debug.h"
    -#include "eventloop.h"
    -#include "util.h"
    -#include "version.h"
    +#include <purple.h>
    #define FILECTL_PLUGIN_ID "core-filectl"
    static int check;
    --- a/libpurple/plugins/idle.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/idle.c Tue Oct 08 21:48:28 2019 -0500
    @@ -24,15 +24,7 @@
    #include "internal.h"
    -#include "connection.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "plugins.h"
    -#include "presence.h"
    -#include "request.h"
    -#include "server.h"
    -#include "status.h"
    -#include "version.h"
    +#include <purple.h>
    /* This plugin no longer depends on gtk */
    #define IDLE_PLUGIN_ID "core-idle"
    @@ -56,7 +48,7 @@
    protocol = purple_protocols_find(purple_account_get_protocol_id(account));
    g_return_val_if_fail(protocol != NULL, FALSE);
    - return PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, set_idle);
    + return PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, set_idle);
    }
    static void
    --- a/libpurple/plugins/joinpart.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/joinpart.c Tue Oct 08 21:48:28 2019 -0500
    @@ -21,10 +21,7 @@
    */
    #include "internal.h"
    -#include "conversation.h"
    -#include "debug.h"
    -#include "plugins.h"
    -#include "version.h"
    +#include <purple.h>
    #define JOINPART_PLUGIN_ID "core-rlaager-joinpart"
    --- a/libpurple/plugins/keyrings/internalkeyring.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/keyrings/internalkeyring.c Tue Oct 08 21:48:28 2019 -0500
    @@ -25,11 +25,7 @@
    */
    #include "internal.h"
    -#include "account.h"
    -#include "debug.h"
    -#include "keyring.h"
    -#include "plugins.h"
    -#include "version.h"
    +#include <purple.h>
    #include <nettle/aes.h>
    #include <nettle/cbc.h>
    @@ -441,9 +437,9 @@
    } else if (open)
    g_assert_not_reached();
    else if (req->cb.read != NULL /* || req->cb.write != NULL */ ) {
    - GError *error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_CANCELLED,
    - _("Operation cancelled."));
    + GError *error = g_error_new_literal(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_CANCELLED,
    + _("Operation cancelled."));
    if (req->type == INTKEYRING_REQUEST_READ) {
    req->cb.read(req->account, NULL, error,
    req->cb_data);
    @@ -686,9 +682,9 @@
    purple_account_get_username(account),
    purple_account_get_protocol_id(account));
    }
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_NOPASSWORD,
    - _("Password not found."));
    + error = g_error_new_literal(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_NOPASSWORD,
    + _("Password not found."));
    if (cb != NULL)
    cb(account, NULL, error, data);
    g_error_free(error);
    @@ -806,11 +802,9 @@
    }
    return TRUE;
    } else {
    - if (error != NULL) {
    - *error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_BACKENDFAIL,
    - _("Invalid password storage mode."));
    - }
    + g_set_error_literal(error, PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_BACKENDFAIL,
    + _("Invalid password storage mode."));
    return FALSE;
    }
    }
    --- a/libpurple/plugins/keyrings/kwallet.cpp Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,576 +0,0 @@
    -/**
    - * @file kwallet.cpp KWallet password storage
    - * @ingroup plugins
    - */
    -
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program ; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#include "internal.h"
    -#include "account.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "keyring.h"
    -#include "plugins.h"
    -#include "version.h"
    -
    -#include <QQueue>
    -#include <QCoreApplication>
    -#include <kwallet.h>
    -
    -#define KWALLET_NAME N_("KWallet")
    -#define KWALLET_DESCRIPTION N_("This plugin will store passwords in KWallet.")
    -#define KWALLET_AUTHORS { "QuLogic (qulogic[at]pidgin.im)", NULL }
    -#define KWALLET_ID "keyring-kwallet"
    -#define KWALLET_DOMAIN (g_quark_from_static_string(KWALLET_ID))
    -
    -#define KWALLET_WALLET_NAME KWallet::Wallet::NetworkWallet()
    -#define KWALLET_APP_NAME "Libpurple"
    -#define KWALLET_FOLDER_NAME "libpurple"
    -
    -PurpleKeyring *keyring_handler = NULL;
    -QCoreApplication *qCoreApp = NULL;
    -
    -namespace KWalletPlugin {
    -
    -class request
    -{
    - public:
    - virtual ~request();
    - virtual void detailedAbort(enum PurpleKeyringError error) = 0;
    - void abort();
    - virtual void execute(KWallet::Wallet *wallet) = 0;
    -
    - protected:
    - gpointer data;
    - PurpleAccount *account;
    - QString password;
    - bool noPassword;
    -};
    -
    -class engine : private QObject, private QQueue<request*>
    -{
    - Q_OBJECT
    -
    - public:
    - engine();
    - ~engine();
    - void queue(request *req);
    - void abortAll();
    - static engine *instance(bool create);
    - static void closeInstance(void);
    -
    - private slots:
    - void walletOpened(bool opened);
    - void walletClosed();
    -
    - private:
    - static engine *pinstance;
    -
    - bool connected;
    - bool failed;
    - bool closing;
    - bool externallyClosed;
    - bool busy;
    - bool closeAfterBusy;
    -
    - KWallet::Wallet *wallet;
    -
    - void reopenWallet();
    - void executeRequests();
    -};
    -
    -class save_request : public request
    -{
    - public:
    - save_request(PurpleAccount *account, const char *password,
    - PurpleKeyringSaveCallback cb, void *data);
    - void detailedAbort(enum PurpleKeyringError error);
    - void execute(KWallet::Wallet *wallet);
    -
    - private:
    - PurpleKeyringSaveCallback callback;
    -};
    -
    -class read_request : public request
    -{
    - public:
    - read_request(PurpleAccount *account,
    - PurpleKeyringReadCallback cb, void *data);
    - void detailedAbort(enum PurpleKeyringError error);
    - void execute(KWallet::Wallet *wallet);
    -
    - private:
    - PurpleKeyringReadCallback callback;
    -};
    -
    -}
    -
    -static gboolean
    -kwallet_is_enabled(void)
    -{
    - return KWallet::Wallet::isEnabled() ? TRUE : FALSE;
    -}
    -
    -KWalletPlugin::engine *KWalletPlugin::engine::pinstance = NULL;
    -
    -KWalletPlugin::request::~request()
    -{
    -}
    -
    -void
    -KWalletPlugin::request::abort()
    -{
    - detailedAbort(PURPLE_KEYRING_ERROR_CANCELLED);
    -}
    -
    -KWalletPlugin::engine::engine()
    -{
    - connected = false;
    - failed = false;
    - closing = false;
    - externallyClosed = false;
    - wallet = NULL;
    - busy = false;
    - closeAfterBusy = false;
    -
    - reopenWallet();
    -}
    -
    -void
    -KWalletPlugin::engine::reopenWallet()
    -{
    - if (closing) {
    - purple_debug_error("keyring-kwallet",
    - "wallet is closing right now\n");
    - failed = true;
    - return;
    - }
    -
    - connected = false;
    - failed = false;
    - externallyClosed = false;
    -
    - wallet = KWallet::Wallet::openWallet(KWALLET_WALLET_NAME, 0,
    - KWallet::Wallet::Asynchronous);
    - if (wallet == NULL) {
    - failed = true;
    - purple_debug_error("keyring-kwallet",
    - "failed opening a wallet\n");
    - return;
    - }
    -
    - failed |= !connect(wallet, SIGNAL(walletClosed()),
    - SLOT(walletClosed()));
    - failed |= !connect(wallet, SIGNAL(walletOpened(bool)),
    - SLOT(walletOpened(bool)));
    - if (failed) {
    - purple_debug_error("keyring-kwallet",
    - "failed connecting to wallet signal\n");
    - }
    -}
    -
    -KWalletPlugin::engine::~engine()
    -{
    - closing = true;
    -
    - abortAll();
    -
    - delete wallet;
    -
    - if (pinstance == this)
    - pinstance = NULL;
    -}
    -
    -void
    -KWalletPlugin::engine::abortAll()
    -{
    - int abortedCount = 0;
    -
    - while (!isEmpty()) {
    - request *req = dequeue();
    - req->abort();
    - delete req;
    - abortedCount++;
    - }
    -
    - if (abortedCount > 0) {
    - purple_debug_info("keyring-kwallet", "aborted requests: %d\n",
    - abortedCount);
    - }
    -}
    -
    -KWalletPlugin::engine *
    -KWalletPlugin::engine::instance(bool create)
    -{
    - if (pinstance == NULL && create)
    - pinstance = new engine;
    - return pinstance;
    -}
    -
    -void
    -KWalletPlugin::engine::closeInstance(void)
    -{
    - if (pinstance == NULL)
    - return;
    - if (pinstance->closing)
    - return;
    - if (pinstance->busy) {
    - purple_debug_misc("keyring-kwallet",
    - "current instance is busy, will be freed later\n");
    - pinstance->closeAfterBusy = true;
    - } else
    - delete pinstance;
    - pinstance = NULL;
    -}
    -
    -void
    -KWalletPlugin::engine::walletOpened(bool opened)
    -{
    - connected = opened;
    -
    - if (!opened) {
    - purple_debug_warning("keyring-kwallet",
    - "failed to open a wallet\n");
    - delete this;
    - return;
    - }
    -
    - if (!wallet->hasFolder(KWALLET_FOLDER_NAME)) {
    - if (!wallet->createFolder(KWALLET_FOLDER_NAME)) {
    - purple_debug_error("keyring-kwallet",
    - "couldn't create \"" KWALLET_FOLDER_NAME
    - "\" folder in wallet\n");
    - failed = true;
    - }
    - }
    - if (!failed)
    - wallet->setFolder(KWALLET_FOLDER_NAME);
    -
    - executeRequests();
    -}
    -
    -void
    -KWalletPlugin::engine::walletClosed()
    -{
    - if (!closing) {
    - purple_debug_info("keyring-kwallet",
    - "wallet was externally closed\n");
    - externallyClosed = true;
    - delete wallet;
    - wallet = NULL;
    - }
    -}
    -
    -void
    -KWalletPlugin::engine::queue(request *req)
    -{
    - enqueue(req);
    - executeRequests();
    -}
    -
    -void
    -KWalletPlugin::engine::executeRequests()
    -{
    - if (closing || busy)
    - return;
    - busy = true;
    - if (externallyClosed) {
    - reopenWallet();
    - } else if (connected || failed) {
    - while (!isEmpty()) {
    - request *req = dequeue();
    - if (connected)
    - req->execute(wallet);
    - else
    - req->abort();
    - delete req;
    - }
    - } else if (purple_debug_is_verbose()) {
    - purple_debug_misc("keyring-kwallet", "not yet connected\n");
    - }
    - busy = false;
    - if (closeAfterBusy) {
    - purple_debug_misc("keyring-kwallet",
    - "instance freed after being busy\n");
    - delete this;
    - }
    -}
    -
    -KWalletPlugin::save_request::save_request(PurpleAccount *acc, const char *pw,
    - PurpleKeyringSaveCallback cb, void *userdata)
    -{
    - account = acc;
    - data = userdata;
    - callback = cb;
    - password = QString(pw);
    - noPassword = (pw == NULL);
    -}
    -
    -KWalletPlugin::read_request::read_request(PurpleAccount *acc,
    - PurpleKeyringReadCallback cb, void *userdata)
    -{
    - account = acc;
    - data = userdata;
    - callback = cb;
    - password = QString();
    -}
    -
    -void
    -KWalletPlugin::save_request::detailedAbort(enum PurpleKeyringError error)
    -{
    - GError *gerror;
    - if (callback == NULL)
    - return;
    -
    - gerror = g_error_new(PURPLE_KEYRING_ERROR, error,
    - _("Failed to save password."));
    - callback(account, gerror, data);
    - g_error_free(gerror);
    -}
    -
    -void
    -KWalletPlugin::read_request::detailedAbort(enum PurpleKeyringError error)
    -{
    - GError *gerror;
    - if (callback == NULL)
    - return;
    -
    - gerror = g_error_new(PURPLE_KEYRING_ERROR, error,
    - _("Failed to read password."));
    - callback(account, NULL, gerror, data);
    - g_error_free(gerror);
    -}
    -
    -static QString
    -kwallet_account_key(PurpleAccount *account)
    -{
    - return QString(purple_account_get_protocol_id(account)) + ":" +
    - purple_account_get_username(account);
    -}
    -
    -void
    -KWalletPlugin::read_request::execute(KWallet::Wallet *wallet)
    -{
    - int result;
    -
    - g_return_if_fail(wallet != NULL);
    -
    - result = wallet->readPassword(kwallet_account_key(account), password);
    -
    - if (result != 0) {
    - purple_debug_warning("keyring-kwallet",
    - "failed to read password, result was %d\n", result);
    - abort();
    - return;
    - }
    -
    - purple_debug_misc("keyring-kwallet",
    - "Got password for account %s (%s).\n",
    - purple_account_get_username(account),
    - purple_account_get_protocol_id(account));
    -
    - if (callback != NULL)
    - callback(account, password.toUtf8().constData(), NULL, data);
    -}
    -
    -void
    -KWalletPlugin::save_request::execute(KWallet::Wallet *wallet)
    -{
    - int result;
    -
    - g_return_if_fail(wallet != NULL);
    -
    - if (noPassword)
    - result = wallet->removeEntry(kwallet_account_key(account));
    - else {
    - result = wallet->writePassword(kwallet_account_key(account),
    - password);
    - }
    -
    - if (result != 0) {
    - purple_debug_warning("keyring-kwallet",
    - "failed to write password, result was %d\n", result);
    - abort();
    - return;
    - }
    -
    - purple_debug_misc("keyring-kwallet",
    - "Password %s for account %s (%s).\n",
    - (noPassword ? "removed" : "saved"),
    - purple_account_get_username(account),
    - purple_account_get_protocol_id(account));
    -
    - if (callback != NULL)
    - callback(account, NULL, data);
    -}
    -
    -extern "C"
    -{
    -
    -static void
    -kwallet_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
    - gpointer data)
    -{
    - KWalletPlugin::read_request *req =
    - new KWalletPlugin::read_request(account, cb, data);
    -
    - if (KWallet::Wallet::keyDoesNotExist(KWALLET_WALLET_NAME,
    - KWALLET_FOLDER_NAME, kwallet_account_key(account)))
    - {
    - req->detailedAbort(PURPLE_KEYRING_ERROR_NOPASSWORD);
    - delete req;
    - }
    - else
    - KWalletPlugin::engine::instance(true)->queue(req);
    -}
    -
    -static void
    -kwallet_save(PurpleAccount *account, const char *password,
    - PurpleKeyringSaveCallback cb, gpointer data)
    -{
    - if (password == NULL && KWallet::Wallet::keyDoesNotExist(
    - KWALLET_WALLET_NAME, KWALLET_FOLDER_NAME,
    - kwallet_account_key(account)))
    - {
    - if (cb != NULL)
    - cb(account, NULL, data);
    - }
    - else
    - KWalletPlugin::engine::instance(true)->queue(
    - new KWalletPlugin::save_request(account, password, cb,
    - data));
    -}
    -
    -static void
    -kwallet_cancel(void)
    -{
    - KWalletPlugin::engine *instance =
    - KWalletPlugin::engine::instance(false);
    - if (instance)
    - instance->abortAll();
    -}
    -
    -static void *
    -kwallet_get_handle(void)
    -{
    - static int handle;
    -
    - return &handle;
    -}
    -
    -static const char *kwallet_get_ui_name(void)
    -{
    - GHashTable *ui_info;
    - const char *ui_name = NULL;
    -
    - ui_info = purple_core_get_ui_info();
    - if (ui_info != NULL)
    - ui_name = (const char*)g_hash_table_lookup(ui_info, "name");
    - if (ui_name == NULL)
    - ui_name = KWALLET_APP_NAME;
    -
    - return ui_name;
    -}
    -
    -static PurplePluginInfo *
    -plugin_query(GError **error)
    -{
    - const gchar * const authors[] = KWALLET_AUTHORS;
    -
    - return purple_plugin_info_new(
    - "id", KWALLET_ID,
    - "name", KWALLET_NAME,
    - "version", DISPLAY_VERSION,
    - "category", N_("Keyring"),
    - "summary", "KWallet Keyring Plugin",
    - "description", KWALLET_DESCRIPTION,
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
    - NULL
    - );
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin, GError **error)
    -{
    - if (!qCoreApp) {
    - int argc = 0;
    - qCoreApp = new QCoreApplication(argc, NULL);
    - qCoreApp->setApplicationName(kwallet_get_ui_name());
    - }
    -
    - if (!kwallet_is_enabled()) {
    - g_set_error(error, KWALLET_DOMAIN, 0, "KWallet service is disabled.");
    - purple_debug_info("keyring-kwallet",
    - "KWallet service is disabled\n");
    - return FALSE;
    - }
    -
    - keyring_handler = purple_keyring_new();
    -
    - purple_keyring_set_name(keyring_handler, _(KWALLET_NAME));
    - purple_keyring_set_id(keyring_handler, KWALLET_ID);
    - purple_keyring_set_read_password(keyring_handler, kwallet_read);
    - purple_keyring_set_save_password(keyring_handler, kwallet_save);
    - purple_keyring_set_cancel_requests(keyring_handler, kwallet_cancel);
    - purple_keyring_set_close_keyring(keyring_handler,
    - KWalletPlugin::engine::closeInstance);
    -
    - purple_keyring_register(keyring_handler);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin, GError **error)
    -{
    - if (purple_keyring_get_inuse() == keyring_handler) {
    - g_set_error(error, KWALLET_DOMAIN, 0, "The keyring is currently "
    - "in use.");
    - purple_debug_warning("keyring-kwallet",
    - "keyring in use, cannot unload\n");
    - return FALSE;
    - }
    -
    - purple_signals_disconnect_by_handle(kwallet_get_handle());
    -
    - KWalletPlugin::engine::closeInstance();
    -
    - purple_keyring_unregister(keyring_handler);
    - purple_keyring_free(keyring_handler);
    - keyring_handler = NULL;
    -
    - if (qCoreApp) {
    - delete qCoreApp;
    - qCoreApp = NULL;
    - }
    -
    - return TRUE;
    -}
    -
    -PURPLE_PLUGIN_INIT(kwallet_keyring, plugin_query, plugin_load, plugin_unload);
    -
    -} /* extern "C" */
    -
    -#include "kwallet.moc"
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/libpurple/plugins/keyrings/kwallet/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,13 @@
    +if PLUGINS
    + if kwallet.found()
    + kwallet_moc = qt5.preprocess(
    + moc_headers: 'purplekwallet.h',
    + dependencies: qt5_dep,
    + )
    +
    + kwallet_plugin = library('purplekwallet', 'purplekwallet.cpp', 'purplekwallet.h', kwallet_moc,
    + dependencies : [kwallet, qt5_dep, libpurple_dep],
    + name_prefix : '',
    + install : true, install_dir : PURPLE_PLUGINDIR)
    + endif
    +endif
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/libpurple/plugins/keyrings/kwallet/purplekwallet.cpp Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,496 @@
    +/**
    + * @file kwallet.cpp KWallet password storage
    + * @ingroup plugins
    + */
    +
    +/* purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program ; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + */
    +
    +#include "internal.h"
    +#include <purple.h>
    +
    +#include <QQueue>
    +#include <QCoreApplication>
    +
    +#include "purplekwallet.h"
    +
    +#define KWALLET_NAME N_("KWallet")
    +#define KWALLET_DESCRIPTION N_("This plugin will store passwords in KWallet.")
    +#define KWALLET_AUTHORS { "QuLogic (qulogic[at]pidgin.im)", NULL }
    +#define KWALLET_ID "keyring-kwallet"
    +#define KWALLET_DOMAIN (g_quark_from_static_string(KWALLET_ID))
    +
    +#define KWALLET_WALLET_NAME KWallet::Wallet::NetworkWallet()
    +#define KWALLET_APP_NAME "Libpurple"
    +#define KWALLET_FOLDER_NAME "libpurple"
    +
    +PurpleKeyring *keyring_handler = NULL;
    +QCoreApplication *qCoreApp = NULL;
    +
    +static gboolean
    +kwallet_is_enabled(void)
    +{
    + return KWallet::Wallet::isEnabled() ? TRUE : FALSE;
    +}
    +
    +KWalletPlugin::engine *KWalletPlugin::engine::pinstance = NULL;
    +
    +KWalletPlugin::request::~request()
    +{
    +}
    +
    +void
    +KWalletPlugin::request::abort()
    +{
    + detailedAbort(PURPLE_KEYRING_ERROR_CANCELLED);
    +}
    +
    +KWalletPlugin::engine::engine()
    +{
    + connected = false;
    + failed = false;
    + closing = false;
    + externallyClosed = false;
    + wallet = NULL;
    + busy = false;
    + closeAfterBusy = false;
    +
    + reopenWallet();
    +}
    +
    +void
    +KWalletPlugin::engine::reopenWallet()
    +{
    + if (closing) {
    + purple_debug_error("keyring-kwallet",
    + "wallet is closing right now\n");
    + failed = true;
    + return;
    + }
    +
    + connected = false;
    + failed = false;
    + externallyClosed = false;
    +
    + wallet = KWallet::Wallet::openWallet(KWALLET_WALLET_NAME, 0,
    + KWallet::Wallet::Asynchronous);
    + if (wallet == NULL) {
    + failed = true;
    + purple_debug_error("keyring-kwallet",
    + "failed opening a wallet\n");
    + return;
    + }
    +
    + failed |= !connect(wallet, SIGNAL(walletClosed()),
    + SLOT(walletClosed()));
    + failed |= !connect(wallet, SIGNAL(walletOpened(bool)),
    + SLOT(walletOpened(bool)));
    + if (failed) {
    + purple_debug_error("keyring-kwallet",
    + "failed connecting to wallet signal\n");
    + }
    +}
    +
    +KWalletPlugin::engine::~engine()
    +{
    + closing = true;
    +
    + abortAll();
    +
    + delete wallet;
    +
    + if (pinstance == this)
    + pinstance = NULL;
    +}
    +
    +void
    +KWalletPlugin::engine::abortAll()
    +{
    + int abortedCount = 0;
    +
    + while (!isEmpty()) {
    + request *req = dequeue();
    + req->abort();
    + delete req;
    + abortedCount++;
    + }
    +
    + if (abortedCount > 0) {
    + purple_debug_info("keyring-kwallet", "aborted requests: %d\n",
    + abortedCount);
    + }
    +}
    +
    +KWalletPlugin::engine *
    +KWalletPlugin::engine::instance(bool create)
    +{
    + if (pinstance == NULL && create)
    + pinstance = new engine;
    + return pinstance;
    +}
    +
    +void
    +KWalletPlugin::engine::closeInstance(void)
    +{
    + if (pinstance == NULL)
    + return;
    + if (pinstance->closing)
    + return;
    + if (pinstance->busy) {
    + purple_debug_misc("keyring-kwallet",
    + "current instance is busy, will be freed later\n");
    + pinstance->closeAfterBusy = true;
    + } else
    + delete pinstance;
    + pinstance = NULL;
    +}
    +
    +void
    +KWalletPlugin::engine::walletOpened(bool opened)
    +{
    + connected = opened;
    +
    + if (!opened) {
    + purple_debug_warning("keyring-kwallet",
    + "failed to open a wallet\n");
    + delete this;
    + return;
    + }
    +
    + if (!wallet->hasFolder(KWALLET_FOLDER_NAME)) {
    + if (!wallet->createFolder(KWALLET_FOLDER_NAME)) {
    + purple_debug_error("keyring-kwallet",
    + "couldn't create \"" KWALLET_FOLDER_NAME
    + "\" folder in wallet\n");
    + failed = true;
    + }
    + }
    + if (!failed)
    + wallet->setFolder(KWALLET_FOLDER_NAME);
    +
    + executeRequests();
    +}
    +
    +void
    +KWalletPlugin::engine::walletClosed()
    +{
    + if (!closing) {
    + purple_debug_info("keyring-kwallet",
    + "wallet was externally closed\n");
    + externallyClosed = true;
    + delete wallet;
    + wallet = NULL;
    + }
    +}
    +
    +void
    +KWalletPlugin::engine::queue(request *req)
    +{
    + enqueue(req);
    + executeRequests();
    +}
    +
    +void
    +KWalletPlugin::engine::executeRequests()
    +{
    + if (closing || busy)
    + return;
    + busy = true;
    + if (externallyClosed) {
    + reopenWallet();
    + } else if (connected || failed) {
    + while (!isEmpty()) {
    + request *req = dequeue();
    + if (connected)
    + req->execute(wallet);
    + else
    + req->abort();
    + delete req;
    + }
    + } else if (purple_debug_is_verbose()) {
    + purple_debug_misc("keyring-kwallet", "not yet connected\n");
    + }
    + busy = false;
    + if (closeAfterBusy) {
    + purple_debug_misc("keyring-kwallet",
    + "instance freed after being busy\n");
    + delete this;
    + }
    +}
    +
    +KWalletPlugin::save_request::save_request(PurpleAccount *acc, const char *pw,
    + PurpleKeyringSaveCallback cb, void *userdata)
    +{
    + account = acc;
    + data = userdata;
    + callback = cb;
    + password = QString(pw);
    + noPassword = (pw == NULL);
    +}
    +
    +KWalletPlugin::read_request::read_request(PurpleAccount *acc,
    + PurpleKeyringReadCallback cb, void *userdata)
    +{
    + account = acc;
    + data = userdata;
    + callback = cb;
    + password = QString();
    +}
    +
    +void
    +KWalletPlugin::save_request::detailedAbort(enum PurpleKeyringError error)
    +{
    + GError *gerror;
    + if (callback == NULL)
    + return;
    +
    + gerror = g_error_new(PURPLE_KEYRING_ERROR, error,
    + _("Failed to save password."));
    + callback(account, gerror, data);
    + g_error_free(gerror);
    +}
    +
    +void
    +KWalletPlugin::read_request::detailedAbort(enum PurpleKeyringError error)
    +{
    + GError *gerror;
    + if (callback == NULL)
    + return;
    +
    + gerror = g_error_new(PURPLE_KEYRING_ERROR, error,
    + _("Failed to read password."));
    + callback(account, NULL, gerror, data);
    + g_error_free(gerror);
    +}
    +
    +static QString
    +kwallet_account_key(PurpleAccount *account)
    +{
    + return QString(purple_account_get_protocol_id(account)) + ":" +
    + purple_account_get_username(account);
    +}
    +
    +void
    +KWalletPlugin::read_request::execute(KWallet::Wallet *wallet)
    +{
    + int result;
    +
    + g_return_if_fail(wallet != NULL);
    +
    + result = wallet->readPassword(kwallet_account_key(account), password);
    +
    + if (result != 0) {
    + purple_debug_warning("keyring-kwallet",
    + "failed to read password, result was %d\n", result);
    + abort();
    + return;
    + }
    +
    + purple_debug_misc("keyring-kwallet",
    + "Got password for account %s (%s).\n",
    + purple_account_get_username(account),
    + purple_account_get_protocol_id(account));
    +
    + if (callback != NULL)
    + callback(account, password.toUtf8().constData(), NULL, data);
    +}
    +
    +void
    +KWalletPlugin::save_request::execute(KWallet::Wallet *wallet)
    +{
    + int result;
    +
    + g_return_if_fail(wallet != NULL);
    +
    + if (noPassword)
    + result = wallet->removeEntry(kwallet_account_key(account));
    + else {
    + result = wallet->writePassword(kwallet_account_key(account),
    + password);
    + }
    +
    + if (result != 0) {
    + purple_debug_warning("keyring-kwallet",
    + "failed to write password, result was %d\n", result);
    + abort();
    + return;
    + }
    +
    + purple_debug_misc("keyring-kwallet",
    + "Password %s for account %s (%s).\n",
    + (noPassword ? "removed" : "saved"),
    + purple_account_get_username(account),
    + purple_account_get_protocol_id(account));
    +
    + if (callback != NULL)
    + callback(account, NULL, data);
    +}
    +
    +extern "C"
    +{
    +
    +static void
    +kwallet_read(PurpleAccount *account, PurpleKeyringReadCallback cb,
    + gpointer data)
    +{
    + KWalletPlugin::read_request *req =
    + new KWalletPlugin::read_request(account, cb, data);
    +
    + if (KWallet::Wallet::keyDoesNotExist(KWALLET_WALLET_NAME,
    + KWALLET_FOLDER_NAME, kwallet_account_key(account)))
    + {
    + req->detailedAbort(PURPLE_KEYRING_ERROR_NOPASSWORD);
    + delete req;
    + }
    + else
    + KWalletPlugin::engine::instance(true)->queue(req);
    +}
    +
    +static void
    +kwallet_save(PurpleAccount *account, const char *password,
    + PurpleKeyringSaveCallback cb, gpointer data)
    +{
    + if (password == NULL && KWallet::Wallet::keyDoesNotExist(
    + KWALLET_WALLET_NAME, KWALLET_FOLDER_NAME,
    + kwallet_account_key(account)))
    + {
    + if (cb != NULL)
    + cb(account, NULL, data);
    + }
    + else
    + KWalletPlugin::engine::instance(true)->queue(
    + new KWalletPlugin::save_request(account, password, cb,
    + data));
    +}
    +
    +static void
    +kwallet_cancel(void)
    +{
    + KWalletPlugin::engine *instance =
    + KWalletPlugin::engine::instance(false);
    + if (instance)
    + instance->abortAll();
    +}
    +
    +static void *
    +kwallet_get_handle(void)
    +{
    + static int handle;
    +
    + return &handle;
    +}
    +
    +static const char *kwallet_get_ui_name(void)
    +{
    + GHashTable *ui_info;
    + const char *ui_name = NULL;
    +
    + ui_info = purple_core_get_ui_info();
    + if (ui_info != NULL)
    + ui_name = (const char*)g_hash_table_lookup(ui_info, "name");
    + if (ui_name == NULL)
    + ui_name = KWALLET_APP_NAME;
    +
    + return ui_name;
    +}
    +
    +static PurplePluginInfo *
    +plugin_query(GError **error)
    +{
    + const gchar * const authors[] = KWALLET_AUTHORS;
    +
    + return purple_plugin_info_new(
    + "id", KWALLET_ID,
    + "name", KWALLET_NAME,
    + "version", DISPLAY_VERSION,
    + "category", N_("Keyring"),
    + "summary", "KWallet Keyring Plugin",
    + "description", KWALLET_DESCRIPTION,
    + "authors", authors,
    + "website", PURPLE_WEBSITE,
    + "abi-version", PURPLE_ABI_VERSION,
    + "flags", PURPLE_PLUGIN_INFO_FLAGS_INTERNAL,
    +
    + NULL
    + );
    +}
    +
    +static gboolean
    +plugin_load(PurplePlugin *plugin, GError **error)
    +{
    + if (!qCoreApp) {
    + int argc = 0;
    + qCoreApp = new QCoreApplication(argc, NULL);
    + qCoreApp->setApplicationName(kwallet_get_ui_name());
    + }
    +
    + if (!kwallet_is_enabled()) {
    + g_set_error(error, KWALLET_DOMAIN, 0, "KWallet service is disabled.");
    + purple_debug_info("keyring-kwallet",
    + "KWallet service is disabled\n");
    + return FALSE;
    + }
    +
    + keyring_handler = purple_keyring_new();
    +
    + purple_keyring_set_name(keyring_handler, _(KWALLET_NAME));
    + purple_keyring_set_id(keyring_handler, KWALLET_ID);
    + purple_keyring_set_read_password(keyring_handler, kwallet_read);
    + purple_keyring_set_save_password(keyring_handler, kwallet_save);
    + purple_keyring_set_cancel_requests(keyring_handler, kwallet_cancel);
    + purple_keyring_set_close_keyring(keyring_handler,
    + KWalletPlugin::engine::closeInstance);
    +
    + purple_keyring_register(keyring_handler);
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +plugin_unload(PurplePlugin *plugin, GError **error)
    +{
    + if (purple_keyring_get_inuse() == keyring_handler) {
    + g_set_error(error, KWALLET_DOMAIN, 0, "The keyring is currently "
    + "in use.");
    + purple_debug_warning("keyring-kwallet",
    + "keyring in use, cannot unload\n");
    + return FALSE;
    + }
    +
    + purple_signals_disconnect_by_handle(kwallet_get_handle());
    +
    + KWalletPlugin::engine::closeInstance();
    +
    + purple_keyring_unregister(keyring_handler);
    + purple_keyring_free(keyring_handler);
    + keyring_handler = NULL;
    +
    + if (qCoreApp) {
    + delete qCoreApp;
    + qCoreApp = NULL;
    + }
    +
    + return TRUE;
    +}
    +
    +PURPLE_PLUGIN_INIT(kwallet_keyring, plugin_query, plugin_load, plugin_unload);
    +
    +} /* extern "C" */
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/libpurple/plugins/keyrings/kwallet/purplekwallet.h Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,100 @@
    +/* purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program ; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + */
    +#include "internal.h"
    +#include "keyring.h"
    +
    +#include <kwallet.h>
    +#include <QQueue>
    +
    +namespace KWalletPlugin {
    +
    +class request
    +{
    + public:
    + virtual ~request();
    + virtual void detailedAbort(enum PurpleKeyringError error) = 0;
    + void abort();
    + virtual void execute(KWallet::Wallet *wallet) = 0;
    +
    + protected:
    + gpointer data;
    + PurpleAccount *account;
    + QString password;
    + bool noPassword;
    +};
    +
    +class engine : private QObject, private QQueue<request*>
    +{
    + Q_OBJECT
    +
    + public:
    + engine();
    + ~engine();
    + void queue(request *req);
    + void abortAll();
    + static engine *instance(bool create);
    + static void closeInstance(void);
    +
    + private slots:
    + void walletOpened(bool opened);
    + void walletClosed();
    +
    + private:
    + static engine *pinstance;
    +
    + bool connected;
    + bool failed;
    + bool closing;
    + bool externallyClosed;
    + bool busy;
    + bool closeAfterBusy;
    +
    + KWallet::Wallet *wallet;
    +
    + void reopenWallet();
    + void executeRequests();
    +};
    +
    +class save_request : public request
    +{
    + public:
    + save_request(PurpleAccount *account, const char *password,
    + PurpleKeyringSaveCallback cb, void *data);
    + void detailedAbort(enum PurpleKeyringError error);
    + void execute(KWallet::Wallet *wallet);
    +
    + private:
    + PurpleKeyringSaveCallback callback;
    +};
    +
    +class read_request : public request
    +{
    + public:
    + read_request(PurpleAccount *account,
    + PurpleKeyringReadCallback cb, void *data);
    + void detailedAbort(enum PurpleKeyringError error);
    + void execute(KWallet::Wallet *wallet);
    +
    + private:
    + PurpleKeyringReadCallback callback;
    +};
    +
    +}
    --- a/libpurple/plugins/keyrings/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/keyrings/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -1,12 +1,14 @@
    if PLUGINS
    - if enable_nettle
    + subdir('kwallet')
    +
    + if nettle.found()
    internalkeyring_plugin = library('internalkeyring', 'internalkeyring.c',
    dependencies : [nettle, libpurple_dep],
    name_prefix : '',
    install : true, install_dir : PURPLE_PLUGINDIR)
    endif
    - if enable_secret_service
    + if secretservice.found()
    secretsservice_plugin = library('secretservice', 'secretservice.c',
    dependencies : [secretservice, libpurple_dep],
    name_prefix : '',
    @@ -19,13 +21,4 @@
    name_prefix : '',
    install : true, install_dir : PURPLE_PLUGINDIR)
    endif
    -
    - if enable_kwallet
    - kwallet_moc = moc.process('kwallet.cpp')
    -
    - kwallet_plugin = library('kwallet', 'kwallet.cpp', kwallet_moc,
    - dependencies : [kwallet, qt4, libpurple_dep],
    - name_prefix : '',
    - install : true, install_dir : PURPLE_PLUGINDIR)
    - endif
    endif # PLUGINS
    --- a/libpurple/plugins/keyrings/secretservice.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/keyrings/secretservice.c Tue Oct 08 21:48:28 2019 -0500
    @@ -33,11 +33,7 @@
    */
    #include "internal.h"
    -#include "account.h"
    -#include "debug.h"
    -#include "keyring.h"
    -#include "plugins.h"
    -#include "version.h"
    +#include <purple.h>
    #include <libsecret/secret.h>
    @@ -87,9 +83,9 @@
    old_error = *error;
    if (g_error_matches(old_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    - new_error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_CANCELLED,
    - _("Operation cancelled."));
    + new_error = g_error_new_literal(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_CANCELLED,
    + _("Operation cancelled."));
    } else if (g_error_matches(old_error, G_DBUS_ERROR,
    G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND) ||
    g_error_matches(old_error, G_DBUS_ERROR,
    --- a/libpurple/plugins/keyrings/wincred.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/keyrings/wincred.c Tue Oct 08 21:48:28 2019 -0500
    @@ -25,10 +25,7 @@
    */
    #include "internal.h"
    -#include "debug.h"
    -#include "keyring.h"
    -#include "plugins.h"
    -#include "version.h"
    +#include <purple.h>
    #include <wincred.h>
    --- a/libpurple/plugins/log_reader.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/log_reader.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,17 +1,25 @@
    +/*
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of the
    + * License, or (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful, but
    + * WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    + * 02111-1301, USA.
    + */
    +
    #include "internal.h"
    #include <stdio.h>
    -#include "debug.h"
    -#include "glibcompat.h"
    -#include "log.h"
    -#include "plugins.h"
    -#include "pluginpref.h"
    -#include "prefs.h"
    -#include "stringref.h"
    -#include "util.h"
    -#include "version.h"
    -#include "xmlnode.h"
    +#include <purple.h>
    /* Where is the Windows partition mounted? */
    #ifndef PURPLE_LOG_READER_WINDOWS_MOUNT_POINT
    @@ -1837,7 +1845,6 @@
    purple_str_has_prefix(line, QIP_LOG_OUT_MESSAGE_ESC)) {
    char *tmp;
    - const char *buddy_name;
    is_in_message = purple_str_has_prefix(line, QIP_LOG_IN_MESSAGE_ESC);
    @@ -1846,9 +1853,6 @@
    if (!c)
    break;
    - /* XXX: Do we need buddy_name when we have buddy->alias? */
    - buddy_name = ++c;
    -
    /* Find the last '(' character. */
    if ((tmp = strchr(c, '\n')) != NULL) {
    while (*tmp && *tmp != '(') --tmp;
    @@ -1884,9 +1888,8 @@
    if (is_in_message) {
    const char *alias = NULL;
    - if (buddy_name != NULL && buddy != NULL &&
    - (alias = purple_buddy_get_alias(buddy)))
    - {
    + if (buddy != NULL &&
    + (alias = purple_buddy_get_alias(buddy))) {
    g_string_append_printf(formatted,
    "<span style=\"color: #A82F2F;\">"
    "<b>%s</b></span>: ", alias);
    @@ -2188,7 +2191,7 @@
    if (fseek(file, data->offset, SEEK_SET) != 0) {
    fclose(file);
    - free(contents);
    + g_free(contents);
    g_return_val_if_reached(g_strdup(""));
    }
    data->length = fread(contents, 1, data->length, file);
    --- a/libpurple/plugins/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -77,4 +77,10 @@
    dependencies : [libpurple_dep],
    name_prefix : '',
    )
    +
    + purple_toast = library('purple-toast', 'purple-toast.c',
    + dependencies : [libpurple_dep],
    + name_prefix: '',
    + install : true, install_dir : PURPLE_PLUGINDIR)
    +
    endif # PLUGINS
    --- a/libpurple/plugins/offlinemsg.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/offlinemsg.c Tue Oct 08 21:48:28 2019 -0500
    @@ -27,15 +27,7 @@
    #define PLUGIN_DESCRIPTION N_("Save messages sent to an offline user as pounce.")
    #define PLUGIN_AUTHORS {"Sadrul H Chowdhury <sadrul@users.sourceforge.net>", NULL}
    -/* Purple headers */
    -#include <version.h>
    -
    -#include <buddylist.h>
    -#include <conversation.h>
    -#include <core.h>
    -#include <debug.h>
    -#include <pounce.h>
    -#include <request.h>
    +#include <purple.h>
    #define PREF_PREFIX "/plugins/core/" PLUGIN_ID
    #define PREF_ALWAYS PREF_PREFIX "/always"
    --- a/libpurple/plugins/one_time_password.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/one_time_password.c Tue Oct 08 21:48:28 2019 -0500
    @@ -19,11 +19,7 @@
    * 02111-1301, USA.
    */
    #include "internal.h"
    -#include "debug.h"
    -#include "plugins.h"
    -#include "version.h"
    -#include "account.h"
    -#include "accountopt.h"
    +#include <purple.h>
    #define PLUGIN_ID "core-one_time_password"
    #define PREF_NAME PLUGIN_ID "_enabled"
    --- a/libpurple/plugins/psychic.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/psychic.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,19 +1,22 @@
    -
    +/*
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of the
    + * License, or (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful, but
    + * WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    + * 02111-1301, USA.
    + */
    #include "internal.h"
    -
    -#include "account.h"
    -#include "buddylist.h"
    -#include "conversation.h"
    -#include "debug.h"
    -#include "signals.h"
    -#include "status.h"
    -#include "version.h"
    -
    -#include "plugins.h"
    -#include "pluginpref.h"
    -#include "prefs.h"
    -
    +#include <purple.h>
    #define PLUGIN_ID "core-psychic"
    #define PLUGIN_NAME N_("Psychic Mode")
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/libpurple/plugins/purple-toast.c Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,240 @@
    +/* pidgin
    + *
    + * Pidgin is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + */
    +#include <glib.h>
    +#include <gmodule.h>
    +#include <gplugin.h>
    +
    +#include <purple.h>
    +
    +/* make the compiler happy... */
    +G_MODULE_EXPORT GPluginPluginInfo *gplugin_query(GError **error);
    +G_MODULE_EXPORT gboolean gplugin_load(GPluginPlugin *plugin, GError **error);
    +G_MODULE_EXPORT gboolean gplugin_unload(GPluginPlugin *plugin, GError **error);
    +
    +/******************************************************************************
    + * Helpers
    + *****************************************************************************/
    +static GApplication *
    +purple_toast_get_application(void) {
    + static GApplication *application = NULL;
    +
    + if(G_UNLIKELY(application == NULL)) {
    + application = g_application_get_default();
    +
    + if(application == NULL) {
    + application = g_application_new(NULL, G_APPLICATION_FLAGS_NONE);
    + g_application_register(application, NULL, NULL);
    + }
    + }
    +
    + return application;
    +}
    +
    +static void
    +purple_toast_show_notification(const gchar *title,
    + const gchar *body,
    + GIcon *icon)
    +{
    + GNotification *notification = g_notification_new(title);
    + gchar *stripped = purple_markup_strip_html(body);
    +
    + g_notification_set_body(notification, stripped);
    + g_free(stripped);
    +
    + if(G_IS_ICON(icon)) {
    + g_notification_set_icon(notification, icon);
    + }
    +
    + g_application_send_notification(purple_toast_get_application(),
    + NULL,
    + notification);
    +
    + g_object_unref(G_OBJECT(notification));
    +}
    +
    +static GIcon *
    +purple_toast_find_icon(PurpleAccount *account,
    + PurpleBuddy *buddy,
    + const gchar *sender)
    +{
    + GIcon *icon = NULL;
    +
    + if(PURPLE_IS_BUDDY(buddy)) {
    + PurpleBuddyIcon *buddy_icon = purple_buddy_get_icon(buddy);
    +
    + if(buddy_icon) {
    + const gchar *filename = NULL;
    +
    + filename = purple_buddy_icon_get_full_path(buddy_icon);
    + if(filename != NULL) {
    + GFile *file = g_file_new_for_path(filename);
    +
    + icon = g_file_icon_new(file);
    +
    + g_object_unref(file);
    + }
    + }
    + } else {
    + PurpleImage *image = NULL;
    + const gchar *path = NULL;
    +
    + image = purple_buddy_icons_find_account_icon(account);
    + if(PURPLE_IS_IMAGE(image)) {
    + path = purple_image_get_path(image);
    +
    + if(path) {
    + GFile *file = g_file_new_for_path(path);
    +
    + icon = g_file_icon_new(file);
    +
    + g_object_unref(G_OBJECT(file));
    + }
    + g_object_unref(G_OBJECT(image));
    + }
    + }
    +
    + /* We should probably have a fallback, but we need a libpurple or
    + * pidgin icon or something to make that happen.
    + */
    +
    + return icon;
    +}
    +
    +/******************************************************************************
    + * Callbacks
    + *****************************************************************************/
    +static void
    +purple_toast_im_message_received(PurpleAccount *account,
    + const gchar *sender,
    + const gchar *message,
    + PurpleConversation *conv,
    + PurpleMessageFlags flags,
    + gpointer data)
    +{
    + PurpleBuddy *buddy = NULL;
    + GIcon *icon = NULL;
    + const gchar *title = NULL;
    +
    + buddy = purple_blist_find_buddy(account, sender);
    + title = PURPLE_IS_BUDDY(buddy) ? purple_buddy_get_alias(buddy) : sender;
    + icon = purple_toast_find_icon(account, buddy, sender);
    +
    + purple_toast_show_notification(title, message, icon);
    +
    + if(G_IS_ICON(icon)) {
    + g_object_unref(G_OBJECT(icon));
    + }
    +}
    +
    +static void
    +purple_toast_chat_message_received(PurpleAccount *account,
    + gchar *sender,
    + gchar *message,
    + PurpleConversation *conv,
    + PurpleMessageFlags flags,
    + gpointer data)
    +{
    + PurpleBuddy *buddy = NULL;
    + GIcon *icon = NULL;
    + gchar *title = NULL;
    + const gchar *chat_name = NULL, *from = NULL;
    +
    + /* figure out the title */
    + chat_name = purple_conversation_get_name(PURPLE_CONVERSATION(conv));
    + if(chat_name) {
    + PurpleChat *chat = purple_blist_find_chat(account, chat_name);
    +
    + if(chat) {
    + chat_name = purple_chat_get_name(chat);
    + }
    + }
    +
    + from = sender;
    + buddy = purple_blist_find_buddy(account, sender);
    + if(PURPLE_IS_BUDDY(buddy)) {
    + from = purple_buddy_get_alias(buddy);
    + }
    +
    + title = g_strdup_printf("%s : %s", chat_name, from);
    +
    + /* figure out the icon */
    + icon = purple_toast_find_icon(account, buddy, sender);
    +
    + /* show the notification */
    + purple_toast_show_notification(title, message, icon);
    +
    + /* clean up our memory */
    + g_free(title);
    +
    + if(G_IS_ICON(icon)) {
    + g_object_unref(G_OBJECT(icon));
    + }
    +}
    +
    +/******************************************************************************
    + * Plugin Exports
    + *****************************************************************************/
    +G_MODULE_EXPORT GPluginPluginInfo *
    +gplugin_query(GError **error) {
    + PurplePluginInfo *info = NULL;
    +
    + const gchar * const authors[] = {
    + "Gary Kramlich <grim@reaperworld.com>",
    + NULL
    + };
    +
    + info = purple_plugin_info_new(
    + "id", "purple/toast",
    + "abi-version", PURPLE_ABI_VERSION,
    + "name", "Purple Toast",
    + "version", "0.0.1",
    + "authors", authors,
    + NULL
    + );
    +
    + return GPLUGIN_PLUGIN_INFO(info);
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_load(GPluginPlugin *plugin, GError **error) {
    + gpointer conv_handle = purple_conversations_get_handle();
    +
    + purple_signal_connect(conv_handle,
    + "received-im-msg",
    + plugin,
    + PURPLE_CALLBACK(purple_toast_im_message_received),
    + NULL
    + );
    +
    + purple_signal_connect(conv_handle,
    + "received-chat-msg",
    + plugin,
    + PURPLE_CALLBACK(purple_toast_chat_message_received),
    + NULL
    + );
    +
    + return TRUE;
    +}
    +
    +G_MODULE_EXPORT gboolean
    +gplugin_unload(GPluginPlugin *plugin, GError **error) {
    + return TRUE;
    +}
    --- a/libpurple/plugins/signals-test.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/signals-test.c Tue Oct 08 21:48:28 2019 -0500
    @@ -21,19 +21,10 @@
    #define SIGNAL_TEST_PLUGIN_ID "core-signals-test"
    #include "internal.h"
    +#include <purple.h>
    #include <stdio.h>
    -#include "connection.h"
    -#include "conversation.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "xfer.h"
    -#include "signals.h"
    -#include "version.h"
    -#include "status.h"
    -#include "sound.h"
    -
    /**************************************************************************
    * Account subsystem signal callbacks
    **************************************************************************/
    --- a/libpurple/plugins/simple.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/simple.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,7 +1,22 @@
    +/*
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of the
    + * License, or (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful, but
    + * WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    + * 02111-1301, USA.
    + */
    +
    #include "internal.h"
    -#include "debug.h"
    -#include "plugins.h"
    -#include "version.h"
    +#include <purple.h>
    /** Plugin id : type-author-name (to guarantee uniqueness) */
    #define SIMPLE_PLUGIN_ID "core-ewarmenhoven-simple"
    --- a/libpurple/plugins/statenotify.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/statenotify.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,14 +1,22 @@
    -#include "internal.h"
    +/*
    + * This program is free software; you can redistribute it and/or
    + * modify it under the terms of the GNU General Public License as
    + * published by the Free Software Foundation; either version 2 of the
    + * License, or (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful, but
    + * WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    + * General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    + * 02111-1301, USA.
    + */
    -#include "buddylist.h"
    -#include "conversation.h"
    -#include "debug.h"
    -#include "signals.h"
    -#include "version.h"
    -
    -#include "plugins.h"
    -#include "pluginpref.h"
    -#include "prefs.h"
    +#include "internal.h"
    +#include <purple.h>
    #define STATENOTIFY_PLUGIN_ID "core-statenotify"
    --- a/libpurple/plugins/test-request-input.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/plugins/test-request-input.c Tue Oct 08 21:48:28 2019 -0500
    @@ -22,9 +22,7 @@
    #include <glib.h>
    #include "internal.h"
    -#include "notify.h"
    -#include "plugins.h"
    -#include "version.h"
    +#include <purple.h>
    #define PREF_ROOT "/plugins"
    #define PREF_TEST "/plugins/tests"
    --- a/libpurple/pounce.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/pounce.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_POUNCE_H_
    -#define _PURPLE_POUNCE_H_
    +#ifndef PURPLE_POUNCE_H
    +#define PURPLE_POUNCE_H
    /**
    * SECTION:pounce
    * @section_id: libpurple-pounce
    @@ -246,7 +246,7 @@
    *
    * Returns the account that will do the pouncing.
    *
    - * Returns: The account that will pounce.
    + * Returns: (transfer none): The account that will pounce.
    */
    PurpleAccount *purple_pounce_get_pouncer(const PurplePounce *pounce);
    @@ -400,4 +400,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_POUNCE_H_ */
    +#endif /* PURPLE_POUNCE_H */
    --- a/libpurple/prefs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/prefs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -595,10 +595,8 @@
    break;
    case PURPLE_PREF_STRING_LIST:
    case PURPLE_PREF_PATH_LIST:
    - {
    - g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
    - g_list_free(pref->value.stringlist);
    - } break;
    + g_list_free_full(pref->value.stringlist, g_free);
    + break;
    case PURPLE_PREF_NONE:
    break;
    }
    @@ -989,8 +987,7 @@
    return;
    }
    - g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
    - g_list_free(pref->value.stringlist);
    + g_list_free_full(pref->value.stringlist, g_free);
    pref->value.stringlist = NULL;
    for(tmp = value; tmp; tmp = tmp->next) {
    @@ -1055,8 +1052,7 @@
    return;
    }
    - g_list_foreach(pref->value.stringlist, (GFunc)g_free, NULL);
    - g_list_free(pref->value.stringlist);
    + g_list_free_full(pref->value.stringlist, g_free);
    pref->value.stringlist = NULL;
    for(tmp = value; tmp; tmp = tmp->next)
    @@ -1576,7 +1572,6 @@
    GSList *cbs;
    PurplePrefsUiOps *uiop = purple_prefs_get_ui_ops();
    - cbs = ui_callbacks;
    for (cbs = ui_callbacks; cbs; cbs = cbs->next) {
    PurplePrefCallbackData *cb = cbs->data;
    if (cb->handle != handle) {
    --- a/libpurple/prefs.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/prefs.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    *
    */
    -#ifndef _PURPLE_PREFS_H_
    -#define _PURPLE_PREFS_H_
    +#ifndef PURPLE_PREFS_H
    +#define PURPLE_PREFS_H
    /**
    * SECTION:prefs
    * @section_id: libpurple-prefs
    @@ -433,7 +433,7 @@
    *
    * Get string list pref value
    *
    - * Returns: (element-type utf8): The value of the pref
    + * Returns: (transfer full) (element-type utf8): The value of the pref.
    */
    GList *purple_prefs_get_string_list(const char *name);
    @@ -453,7 +453,7 @@
    *
    * Get path list pref value
    *
    - * Returns: (element-type utf8): The value of the pref
    + * Returns: (transfer full) (element-type utf8): The value of the pref.
    */
    GList *purple_prefs_get_path_list(const char *name);
    @@ -463,9 +463,10 @@
    *
    * Returns a list of children for a pref
    *
    - * Returns: (element-type utf8): A list of newly allocated strings denoting the
    - * names of the children. Returns %NULL if there are no children or if
    - * pref doesn't exist. The caller must free all the strings and the list.
    + * Returns: (transfer full) (element-type utf8): A list of newly allocated
    + * strings denoting the names of the children. Returns %NULL if there
    + * are no children or if pref doesn't exist. The caller must free all
    + * the strings and the list.
    */
    GList *purple_prefs_get_children_names(const char *name);
    @@ -529,4 +530,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_PREFS_H_ */
    +#endif /* PURPLE_PREFS_H */
    --- a/libpurple/presence.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/presence.c Tue Oct 08 21:48:28 2019 -0500
    @@ -166,12 +166,15 @@
    void
    purple_presence_set_idle(PurplePresence *presence, gboolean idle, time_t idle_time)
    {
    + PurplePresencePrivate *priv = NULL;
    + PurplePresenceClass *klass = NULL;
    gboolean old_idle;
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    - PurplePresenceClass *klass = PURPLE_PRESENCE_GET_CLASS(presence);
    GObject *obj;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_PRESENCE(presence));
    +
    + priv = purple_presence_get_instance_private(presence);
    + klass = PURPLE_PRESENCE_GET_CLASS(presence);
    if (priv->idle == idle && priv->idle_time == idle_time)
    return;
    @@ -193,9 +196,11 @@
    void
    purple_presence_set_login_time(PurplePresence *presence, time_t login_time)
    {
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    + PurplePresencePrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_PRESENCE(presence));
    +
    + priv = purple_presence_get_instance_private(presence);
    if (priv->login_time == login_time)
    return;
    @@ -209,23 +214,26 @@
    GList *
    purple_presence_get_statuses(PurplePresence *presence)
    {
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    + PurplePresencePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
    + priv = purple_presence_get_instance_private(presence);
    return priv->statuses;
    }
    PurpleStatus *
    purple_presence_get_status(PurplePresence *presence, const char *status_id)
    {
    + PurplePresencePrivate *priv = NULL;
    PurpleStatus *status;
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    GList *l = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
    g_return_val_if_fail(status_id != NULL, NULL);
    + priv = purple_presence_get_instance_private(presence);
    +
    /* What's the purpose of this hash table? */
    status = (PurpleStatus *)g_hash_table_lookup(priv->status_table,
    status_id);
    @@ -251,10 +259,11 @@
    PurpleStatus *
    purple_presence_get_active_status(PurplePresence *presence)
    {
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    + PurplePresencePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), NULL);
    + priv = purple_presence_get_instance_private(presence);
    return priv->active_status;
    }
    @@ -323,30 +332,33 @@
    gboolean
    purple_presence_is_idle(PurplePresence *presence)
    {
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    + PurplePresencePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), FALSE);
    + priv = purple_presence_get_instance_private(presence);
    return purple_presence_is_online(presence) && priv->idle;
    }
    time_t
    purple_presence_get_idle_time(PurplePresence *presence)
    {
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    + PurplePresencePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), 0);
    + priv = purple_presence_get_instance_private(presence);
    return priv->idle_time;
    }
    time_t
    purple_presence_get_login_time(PurplePresence *presence)
    {
    - PurplePresencePrivate *priv = purple_presence_get_instance_private(presence);
    + PurplePresencePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_PRESENCE(presence), 0);
    + priv = purple_presence_get_instance_private(presence);
    return purple_presence_is_online(presence) ? priv->login_time : 0;
    }
    @@ -450,8 +462,7 @@
    purple_presence_get_instance_private(PURPLE_PRESENCE(object));
    if (priv->statuses) {
    - g_list_foreach(priv->statuses, (GFunc)g_object_unref, NULL);
    - g_list_free(priv->statuses);
    + g_list_free_full(priv->statuses, (GDestroyNotify)g_object_unref);
    priv->statuses = NULL;
    }
    @@ -591,10 +602,11 @@
    PurpleAccount *
    purple_account_presence_get_account(PurpleAccountPresence *presence)
    {
    - PurpleAccountPresencePrivate *priv = purple_account_presence_get_instance_private(presence);
    + PurpleAccountPresencePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_ACCOUNT_PRESENCE(presence), NULL);
    + priv = purple_account_presence_get_instance_private(presence);
    return priv->account;
    }
    @@ -774,7 +786,6 @@
    {
    PurpleBuddy *buddy = purple_buddy_presence_get_buddy(PURPLE_BUDDY_PRESENCE(presence));
    GDateTime *current_time = g_date_time_new_now_utc();
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    PurpleAccount *account = purple_buddy_get_account(buddy);
    gboolean idle = purple_presence_is_idle(presence);
    @@ -831,8 +842,8 @@
    * connect to buddy-[un]idle signals and update from there
    */
    - if (ops != NULL && ops->update != NULL)
    - ops->update(purple_blist_get_buddy_list(), (PurpleBlistNode *)buddy);
    + purple_blist_update_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    g_date_time_unref(current_time);
    }
    @@ -840,10 +851,11 @@
    PurpleBuddy *
    purple_buddy_presence_get_buddy(PurpleBuddyPresence *presence)
    {
    - PurpleBuddyPresencePrivate *priv = purple_buddy_presence_get_instance_private(presence);
    + PurpleBuddyPresencePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_BUDDY_PRESENCE(presence), NULL);
    + priv = purple_buddy_presence_get_instance_private(presence);
    return priv->buddy;
    }
    --- a/libpurple/presence.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/presence.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_PRESENCE_H_
    -#define _PURPLE_PRESENCE_H_
    +#ifndef PURPLE_PRESENCE_H
    +#define PURPLE_PRESENCE_H
    /**
    * SECTION:presence
    * @section_id: libpurple-presence
    @@ -157,7 +157,7 @@
    *
    * Returns the status with the specified ID from a presence.
    *
    - * Returns: The status if found, or NULL.
    + * Returns: (transfer none): The status if found, or %NULL.
    */
    PurpleStatus *purple_presence_get_status(PurplePresence *presence,
    const char *status_id);
    @@ -168,7 +168,7 @@
    *
    * Returns the active exclusive status from a presence.
    *
    - * Returns: The active exclusive status.
    + * Returns: (transfer none): The active exclusive status.
    */
    PurpleStatus *purple_presence_get_active_status(PurplePresence *presence);
    @@ -285,7 +285,7 @@
    *
    * Returns an account presence's account.
    *
    - * Returns: The presence's account.
    + * Returns: (transfer none): The presence's account.
    */
    PurpleAccount *purple_account_presence_get_account(PurpleAccountPresence *presence);
    @@ -319,7 +319,7 @@
    *
    * Returns the buddy presence's buddy.
    *
    - * Returns: The presence's buddy.
    + * Returns: (transfer none): The presence's buddy.
    */
    PurpleBuddy *purple_buddy_presence_get_buddy(PurpleBuddyPresence *presence);
    @@ -339,4 +339,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_PRESENCE_H_ */
    +#endif /* PURPLE_PRESENCE_H */
    --- a/libpurple/protocol.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocol.c Tue Oct 08 21:48:28 2019 -0500
    @@ -119,20 +119,6 @@
    protocol->icon_spec = NULL;
    }
    -void
    -purple_protocol_override(PurpleProtocol *protocol,
    - PurpleProtocolOverrideFlags flags)
    -{
    - g_return_if_fail(PURPLE_IS_PROTOCOL(protocol));
    -
    - if (flags & PURPLE_PROTOCOL_OVERRIDE_USER_SPLITS)
    - user_splits_free(protocol);
    - if (flags & PURPLE_PROTOCOL_OVERRIDE_PROTOCOL_OPTIONS)
    - account_options_free(protocol);
    - if (flags & PURPLE_PROTOCOL_OVERRIDE_ICON_SPEC)
    - icon_spec_free(protocol);
    -}
    -
    /**************************************************************************
    * GObject stuff
    **************************************************************************/
    @@ -255,14 +241,14 @@
    * Protocol Client Interface API
    **************************************************************************/
    #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolClientIface *client_iface = \
    - PURPLE_PROTOCOL_GET_CLIENT_IFACE(protocol); \
    + PurpleProtocolClientInterface *client_iface = \
    + PURPLE_PROTOCOL_CLIENT_GET_IFACE(protocol); \
    if (client_iface && client_iface->funcname) \
    client_iface->funcname(__VA_ARGS__);
    #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolClientIface *client_iface = \
    - PURPLE_PROTOCOL_GET_CLIENT_IFACE(protocol); \
    + PurpleProtocolClientInterface *client_iface = \
    + PURPLE_PROTOCOL_CLIENT_GET_IFACE(protocol); \
    if (client_iface && client_iface->funcname) \
    return client_iface->funcname(__VA_ARGS__); \
    else \
    @@ -275,11 +261,11 @@
    if (G_UNLIKELY(type == 0)) {
    static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolClientIface),
    + .class_size = sizeof(PurpleProtocolClientInterface),
    };
    type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolClientIface", &info, 0);
    + "PurpleProtocolClientInterface", &info, 0);
    }
    return type;
    }
    @@ -384,14 +370,14 @@
    * Protocol Server Interface API
    **************************************************************************/
    #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolServerIface *server_iface = \
    - PURPLE_PROTOCOL_GET_SERVER_IFACE(protocol); \
    + PurpleProtocolServerInterface *server_iface = \
    + PURPLE_PROTOCOL_SERVER_GET_IFACE(protocol); \
    if (server_iface && server_iface->funcname) \
    server_iface->funcname(__VA_ARGS__);
    #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolServerIface *server_iface = \
    - PURPLE_PROTOCOL_GET_SERVER_IFACE(protocol); \
    + PurpleProtocolServerInterface *server_iface = \
    + PURPLE_PROTOCOL_SERVER_GET_IFACE(protocol); \
    if (server_iface && server_iface->funcname) \
    return server_iface->funcname(__VA_ARGS__); \
    else \
    @@ -404,11 +390,11 @@
    if (G_UNLIKELY(type == 0)) {
    static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolServerIface),
    + .class_size = sizeof(PurpleProtocolServerInterface),
    };
    type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolServerIface", &info, 0);
    + "PurpleProtocolServerInterface", &info, 0);
    }
    return type;
    }
    @@ -577,14 +563,14 @@
    * Protocol IM Interface API
    **************************************************************************/
    #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolIMIface *im_iface = \
    - PURPLE_PROTOCOL_GET_IM_IFACE(protocol); \
    + PurpleProtocolIMInterface *im_iface = \
    + PURPLE_PROTOCOL_IM_GET_IFACE(protocol); \
    if (im_iface && im_iface->funcname) \
    im_iface->funcname(__VA_ARGS__);
    #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolIMIface *im_iface = \
    - PURPLE_PROTOCOL_GET_IM_IFACE(protocol); \
    + PurpleProtocolIMInterface *im_iface = \
    + PURPLE_PROTOCOL_IM_GET_IFACE(protocol); \
    if (im_iface && im_iface->funcname) \
    return im_iface->funcname(__VA_ARGS__); \
    else \
    @@ -597,11 +583,11 @@
    if (G_UNLIKELY(type == 0)) {
    static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolIMIface),
    + .class_size = sizeof(PurpleProtocolIMInterface),
    };
    type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolIMIface", &info, 0);
    + "PurpleProtocolIMInterface", &info, 0);
    }
    return type;
    }
    @@ -627,14 +613,14 @@
    * Protocol Chat Interface API
    **************************************************************************/
    #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolChatIface *chat_iface = \
    - PURPLE_PROTOCOL_GET_CHAT_IFACE(protocol); \
    + PurpleProtocolChatInterface *chat_iface = \
    + PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol); \
    if (chat_iface && chat_iface->funcname) \
    chat_iface->funcname(__VA_ARGS__);
    #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolChatIface *chat_iface = \
    - PURPLE_PROTOCOL_GET_CHAT_IFACE(protocol); \
    + PurpleProtocolChatInterface *chat_iface = \
    + PURPLE_PROTOCOL_CHAT_GET_IFACE(protocol); \
    if (chat_iface && chat_iface->funcname) \
    return chat_iface->funcname(__VA_ARGS__); \
    else \
    @@ -647,11 +633,11 @@
    if (G_UNLIKELY(type == 0)) {
    static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolChatIface),
    + .class_size = sizeof(PurpleProtocolChatInterface),
    };
    type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolChatIface", &info, 0);
    + "PurpleProtocolChatInterface", &info, 0);
    }
    return type;
    }
    @@ -734,14 +720,14 @@
    * Protocol Privacy Interface API
    **************************************************************************/
    #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolPrivacyIface *privacy_iface = \
    - PURPLE_PROTOCOL_GET_PRIVACY_IFACE(protocol); \
    + PurpleProtocolPrivacyInterface *privacy_iface = \
    + PURPLE_PROTOCOL_PRIVACY_GET_IFACE(protocol); \
    if (privacy_iface && privacy_iface->funcname) \
    privacy_iface->funcname(__VA_ARGS__);
    #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolPrivacyIface *privacy_iface = \
    - PURPLE_PROTOCOL_GET_PRIVACY_IFACE(protocol); \
    + PurpleProtocolPrivacyInterface *privacy_iface = \
    + PURPLE_PROTOCOL_PRIVACY_GET_IFACE(protocol); \
    if (privacy_iface && privacy_iface->funcname) \
    return privacy_iface->funcname(__VA_ARGS__); \
    else \
    @@ -754,11 +740,11 @@
    if (G_UNLIKELY(type == 0)) {
    static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolPrivacyIface),
    + .class_size = sizeof(PurpleProtocolPrivacyInterface),
    };
    type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolPrivacyIface", &info, 0);
    + "PurpleProtocolPrivacyInterface", &info, 0);
    }
    return type;
    }
    @@ -802,81 +788,17 @@
    #undef DEFINE_PROTOCOL_FUNC
    /**************************************************************************
    - * Protocol Roomlist Interface API
    - **************************************************************************/
    -#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolRoomlistIface *roomlist_iface = \
    - PURPLE_PROTOCOL_GET_ROOMLIST_IFACE(protocol); \
    - if (roomlist_iface && roomlist_iface->funcname) \
    - roomlist_iface->funcname(__VA_ARGS__);
    -
    -#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolRoomlistIface *roomlist_iface = \
    - PURPLE_PROTOCOL_GET_ROOMLIST_IFACE(protocol); \
    - if (roomlist_iface && roomlist_iface->funcname) \
    - return roomlist_iface->funcname(__VA_ARGS__); \
    - else \
    - return defaultreturn;
    -
    -GType
    -purple_protocol_roomlist_iface_get_type(void)
    -{
    - static GType type = 0;
    -
    - if (G_UNLIKELY(type == 0)) {
    - static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolRoomlistIface),
    - };
    -
    - type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolRoomlistIface", &info, 0);
    - }
    - return type;
    -}
    -
    -PurpleRoomlist *
    -purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol,
    - PurpleConnection *gc)
    -{
    - DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_list, gc);
    -}
    -
    -void
    -purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol,
    - PurpleRoomlist *list)
    -{
    - DEFINE_PROTOCOL_FUNC(protocol, cancel, list);
    -}
    -
    -void
    -purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol,
    - PurpleRoomlist *list, PurpleRoomlistRoom *category)
    -{
    - DEFINE_PROTOCOL_FUNC(protocol, expand_category, list, category);
    -}
    -
    -char *
    -purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol,
    - PurpleRoomlistRoom *room)
    -{
    - DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, room_serialize, room);
    -}
    -
    -#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
    -#undef DEFINE_PROTOCOL_FUNC
    -
    -/**************************************************************************
    * Protocol Media Interface API
    **************************************************************************/
    #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolMediaIface *media_iface = \
    - PURPLE_PROTOCOL_GET_MEDIA_IFACE(protocol); \
    + PurpleProtocolMediaInterface *media_iface = \
    + PURPLE_PROTOCOL_MEDIA_GET_IFACE(protocol); \
    if (media_iface && media_iface->funcname) \
    media_iface->funcname(__VA_ARGS__);
    #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolMediaIface *media_iface = \
    - PURPLE_PROTOCOL_GET_MEDIA_IFACE(protocol); \
    + PurpleProtocolMediaInterface *media_iface = \
    + PURPLE_PROTOCOL_MEDIA_GET_IFACE(protocol); \
    if (media_iface && media_iface->funcname) \
    return media_iface->funcname(__VA_ARGS__); \
    else \
    @@ -889,11 +811,11 @@
    if (G_UNLIKELY(type == 0)) {
    static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolMediaIface),
    + .class_size = sizeof(PurpleProtocolMediaInterface),
    };
    type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolMediaIface", &info, 0);
    + "PurpleProtocolMediaInterface", &info, 0);
    }
    return type;
    }
    @@ -927,14 +849,14 @@
    * Protocol Factory Interface API
    **************************************************************************/
    #define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    - PurpleProtocolFactoryIface *factory_iface = \
    - PURPLE_PROTOCOL_GET_FACTORY_IFACE(protocol); \
    + PurpleProtocolFactoryInterface *factory_iface = \
    + PURPLE_PROTOCOL_FACTORY_GET_IFACE(protocol); \
    if (factory_iface && factory_iface->funcname) \
    factory_iface->funcname(__VA_ARGS__);
    #define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    - PurpleProtocolFactoryIface *factory_iface = \
    - PURPLE_PROTOCOL_GET_FACTORY_IFACE(protocol); \
    + PurpleProtocolFactoryInterface *factory_iface = \
    + PURPLE_PROTOCOL_FACTORY_GET_IFACE(protocol); \
    if (factory_iface && factory_iface->funcname) \
    return factory_iface->funcname(__VA_ARGS__); \
    else \
    @@ -947,11 +869,11 @@
    if (G_UNLIKELY(type == 0)) {
    static const GTypeInfo info = {
    - .class_size = sizeof(PurpleProtocolFactoryIface),
    + .class_size = sizeof(PurpleProtocolFactoryInterface),
    };
    type = g_type_register_static(G_TYPE_INTERFACE,
    - "PurpleProtocolFactoryIface", &info, 0);
    + "PurpleProtocolFactoryInterface", &info, 0);
    }
    return type;
    }
    @@ -979,13 +901,5 @@
    who, state);
    }
    -PurpleXfer *
    -purple_protocol_factory_iface_xfer_new(PurpleProtocol *protocol,
    - PurpleAccount *account, PurpleXferType type, const char *who)
    -{
    - DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, xfer_new, account, type,
    - who);
    -}
    -
    #undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
    #undef DEFINE_PROTOCOL_FUNC
    --- a/libpurple/protocol.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocol.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_PROTOCOL_H_
    -#define _PURPLE_PROTOCOL_H_
    +#ifndef PURPLE_PROTOCOL_H
    +#define PURPLE_PROTOCOL_H
    /**
    * SECTION:protocol
    * @section_id: libpurple-protocol
    @@ -56,21 +56,6 @@
    #include "whiteboard.h"
    /**
    - * PurpleProtocolOverrideFlags:
    - *
    - * Flags to indicate what base protocol's data a derived protocol wants to
    - * override.
    - *
    - * See purple_protocol_override().
    - */
    -typedef enum /*< flags >*/
    -{
    - PURPLE_PROTOCOL_OVERRIDE_USER_SPLITS = 1 << 1,
    - PURPLE_PROTOCOL_OVERRIDE_PROTOCOL_OPTIONS = 1 << 2,
    - PURPLE_PROTOCOL_OVERRIDE_ICON_SPEC = 1 << 3,
    -} PurpleProtocolOverrideFlags;
    -
    -/**
    * PurpleProtocol:
    * @id: Protocol ID
    * @name: Translated name of the protocol
    @@ -143,12 +128,12 @@
    void (*_purple_reserved4)(void);
    };
    -#define PURPLE_TYPE_PROTOCOL_CLIENT_IFACE (purple_protocol_client_iface_get_type())
    +#define PURPLE_TYPE_PROTOCOL_CLIENT (purple_protocol_client_iface_get_type())
    -typedef struct _PurpleProtocolClientIface PurpleProtocolClientIface;
    +typedef struct _PurpleProtocolClientInterface PurpleProtocolClientInterface;
    /**
    - * PurpleProtocolClientIface:
    + * PurpleProtocolClientInterface:
    * @get_actions: Returns the actions the protocol can perform. These will
    * show up in the Accounts menu, under a submenu with the name
    * of the account.
    @@ -209,7 +194,7 @@
    *
    * This interface provides a gateway between purple and the protocol.
    */
    -struct _PurpleProtocolClientIface
    +struct _PurpleProtocolClientInterface
    {
    /*< private >*/
    GTypeInterface parent_iface;
    @@ -243,16 +228,16 @@
    gssize (*get_max_message_size)(PurpleConversation *conv);
    };
    -#define PURPLE_PROTOCOL_HAS_CLIENT_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_CLIENT_IFACE))
    -#define PURPLE_PROTOCOL_GET_CLIENT_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CLIENT_IFACE, \
    - PurpleProtocolClientIface))
    +#define PURPLE_IS_PROTOCOL_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_CLIENT))
    +#define PURPLE_PROTOCOL_CLIENT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CLIENT, \
    + PurpleProtocolClientInterface))
    -#define PURPLE_TYPE_PROTOCOL_SERVER_IFACE (purple_protocol_server_iface_get_type())
    +#define PURPLE_TYPE_PROTOCOL_SERVER (purple_protocol_server_iface_get_type())
    -typedef struct _PurpleProtocolServerIface PurpleProtocolServerIface;
    +typedef struct _PurpleProtocolServerInterface PurpleProtocolServerInterface;
    /**
    - * PurpleProtocolServerIface:
    + * PurpleProtocolServerInterface:
    * @register_user: New user registration
    * @unregister_user: Remove the user from the server. The account can either be
    * connected or disconnected. After the removal is finished,
    @@ -320,7 +305,7 @@
    *
    * This interface provides a gateway between purple and the protocol's server.
    */
    -struct _PurpleProtocolServerIface
    +struct _PurpleProtocolServerInterface
    {
    /*< private >*/
    GTypeInterface parent_iface;
    @@ -381,16 +366,16 @@
    PurpleGetPublicAliasFailureCallback failure_cb);
    };
    -#define PURPLE_PROTOCOL_HAS_SERVER_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_SERVER_IFACE))
    -#define PURPLE_PROTOCOL_GET_SERVER_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_SERVER_IFACE, \
    - PurpleProtocolServerIface))
    +#define PURPLE_IS_PROTOCOL_SERVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_SERVER))
    +#define PURPLE_PROTOCOL_SERVER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_SERVER, \
    + PurpleProtocolServerInterface))
    -#define PURPLE_TYPE_PROTOCOL_IM_IFACE (purple_protocol_im_iface_get_type())
    +#define PURPLE_TYPE_PROTOCOL_IM (purple_protocol_im_iface_get_type())
    -typedef struct _PurpleProtocolIMIface PurpleProtocolIMIface;
    +typedef struct _PurpleProtocolIMInterface PurpleProtocolIMInterface;
    /**
    - * PurpleProtocolIMIface:
    + * PurpleProtocolIMInterface:
    * @send: This protocol function should return a positive value on
    * success. If the message is too big to be sent, return
    * <literal>-E2BIG</literal>. If the account is not connected,
    @@ -409,7 +394,7 @@
    *
    * This interface provides callbacks needed by protocols that implement IMs.
    */
    -struct _PurpleProtocolIMIface
    +struct _PurpleProtocolIMInterface
    {
    /*< private >*/
    GTypeInterface parent_iface;
    @@ -421,16 +406,16 @@
    PurpleIMTypingState state);
    };
    -#define PURPLE_PROTOCOL_HAS_IM_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_IM_IFACE))
    -#define PURPLE_PROTOCOL_GET_IM_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_IM_IFACE, \
    - PurpleProtocolIMIface))
    +#define PURPLE_IS_PROTOCOL_IM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_IM))
    +#define PURPLE_PROTOCOL_IM_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_IM, \
    + PurpleProtocolIMInterface))
    -#define PURPLE_TYPE_PROTOCOL_CHAT_IFACE (purple_protocol_chat_iface_get_type())
    +#define PURPLE_TYPE_PROTOCOL_CHAT (purple_protocol_chat_iface_get_type())
    -typedef struct _PurpleProtocolChatIface PurpleProtocolChatIface;
    +typedef struct _PurpleProtocolChatInterface PurpleProtocolChatInterface;
    /**
    - * PurpleProtocolChatIface:
    + * PurpleProtocolChatInterface:
    * @info: Returns a list of #PurpleProtocolChatEntry structs, which represent
    * information required by the protocol to join a chat. libpurple will
    * call join_chat along with the information filled by the user.
    @@ -492,7 +477,7 @@
    *
    * This interface provides callbacks needed by protocols that implement chats.
    */
    -struct _PurpleProtocolChatIface
    +struct _PurpleProtocolChatInterface
    {
    /*< private >*/
    GTypeInterface parent_iface;
    @@ -520,16 +505,16 @@
    void (*set_topic)(PurpleConnection *gc, int id, const char *topic);
    };
    -#define PURPLE_PROTOCOL_HAS_CHAT_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_CHAT_IFACE))
    -#define PURPLE_PROTOCOL_GET_CHAT_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CHAT_IFACE, \
    - PurpleProtocolChatIface))
    +#define PURPLE_IS_PROTOCOL_CHAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_CHAT))
    +#define PURPLE_PROTOCOL_CHAT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_CHAT, \
    + PurpleProtocolChatInterface))
    -#define PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE (purple_protocol_privacy_iface_get_type())
    +#define PURPLE_TYPE_PROTOCOL_PRIVACY (purple_protocol_privacy_iface_get_type())
    -typedef struct _PurpleProtocolPrivacyIface PurpleProtocolPrivacyIface;
    +typedef struct _PurpleProtocolPrivacyInterface PurpleProtocolPrivacyInterface;
    /**
    - * PurpleProtocolPrivacyIface:
    + * PurpleProtocolPrivacyInterface:
    * @add_permit: Add the buddy on the required authorized list.
    * @add_deny: Add the buddy on the required blocked list.
    * @rem_permit: Remove the buddy from the requried authorized list.
    @@ -540,7 +525,7 @@
    *
    * This interface provides privacy callbacks such as to permit/deny users.
    */
    -struct _PurpleProtocolPrivacyIface
    +struct _PurpleProtocolPrivacyInterface
    {
    /*< private >*/
    GTypeInterface parent_iface;
    @@ -557,48 +542,16 @@
    void (*set_permit_deny)(PurpleConnection *gc);
    };
    -#define PURPLE_PROTOCOL_HAS_PRIVACY_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE))
    -#define PURPLE_PROTOCOL_GET_PRIVACY_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE, \
    - PurpleProtocolPrivacyIface))
    +#define PURPLE_IS_PROTOCOL_PRIVACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_PRIVACY))
    +#define PURPLE_PROTOCOL_PRIVACY_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_PRIVACY, \
    + PurpleProtocolPrivacyInterface))
    -#define PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE (purple_protocol_roomlist_iface_get_type())
    +#define PURPLE_TYPE_PROTOCOL_MEDIA (purple_protocol_media_iface_get_type())
    -typedef struct _PurpleProtocolRoomlistIface PurpleProtocolRoomlistIface;
    +typedef struct _PurpleProtocolMediaInterface PurpleProtocolMediaInterface;
    /**
    - * PurpleProtocolRoomlistIface:
    - *
    - * The protocol roomlist interface.
    - *
    - * This interface provides callbacks for room listing.
    - */
    -struct _PurpleProtocolRoomlistIface
    -{
    - /*< private >*/
    - GTypeInterface parent_iface;
    -
    - /*< public >*/
    - PurpleRoomlist *(*get_list)(PurpleConnection *gc);
    -
    - void (*cancel)(PurpleRoomlist *list);
    -
    - void (*expand_category)(PurpleRoomlist *list,
    - PurpleRoomlistRoom *category);
    -
    - /* room list serialize */
    - char *(*room_serialize)(PurpleRoomlistRoom *room);
    -};
    -
    -#define PURPLE_PROTOCOL_HAS_ROOMLIST_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE))
    -#define PURPLE_PROTOCOL_GET_ROOMLIST_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE, \
    - PurpleProtocolRoomlistIface))
    -
    -#define PURPLE_TYPE_PROTOCOL_MEDIA_IFACE (purple_protocol_media_iface_get_type())
    -
    -typedef struct _PurpleProtocolMediaIface PurpleProtocolMediaIface;
    -
    -/**
    - * PurpleProtocolMediaIface:
    + * PurpleProtocolMediaInterface:
    * @initiate_session: Initiate a media session with the given contact.
    * <sbr/>@account: The account to initiate the media session
    * on.
    @@ -620,7 +573,7 @@
    *
    * This interface provides callbacks for media sessions on the protocol.
    */
    -struct _PurpleProtocolMediaIface
    +struct _PurpleProtocolMediaInterface
    {
    /*< private >*/
    GTypeInterface parent_iface;
    @@ -636,16 +589,16 @@
    guint8 volume, guint8 duration);
    };
    -#define PURPLE_PROTOCOL_HAS_MEDIA_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_MEDIA_IFACE))
    -#define PURPLE_PROTOCOL_GET_MEDIA_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_MEDIA_IFACE, \
    - PurpleProtocolMediaIface))
    +#define PURPLE_IS_PROTOCOL_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_MEDIA))
    +#define PURPLE_PROTOCOL_MEDIA_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_MEDIA, \
    + PurpleProtocolMediaInterface))
    -#define PURPLE_TYPE_PROTOCOL_FACTORY_IFACE (purple_protocol_factory_iface_get_type())
    +#define PURPLE_TYPE_PROTOCOL_FACTORY (purple_protocol_factory_iface_get_type())
    -typedef struct _PurpleProtocolFactoryIface PurpleProtocolFactoryIface;
    +typedef struct _PurpleProtocolFactoryInterface PurpleProtocolFactoryInterface;
    /**
    - * PurpleProtocolFactoryIface:
    + * PurpleProtocolFactoryInterface:
    * @connection_new: Creates a new protocol-specific connection object that
    * inherits #PurpleConnection.
    * @roomlist_new: Creates a new protocol-specific room list object that
    @@ -660,7 +613,7 @@
    * This interface provides callbacks for construction of protocol-specific
    * subclasses of some purple objects.
    */
    -struct _PurpleProtocolFactoryIface
    +struct _PurpleProtocolFactoryInterface
    {
    /*< private >*/
    GTypeInterface parent_iface;
    @@ -674,27 +627,24 @@
    PurpleWhiteboard *(*whiteboard_new)(PurpleAccount *account,
    const char *who, int state);
    -
    - PurpleXfer *(*xfer_new)(PurpleAccount *account, PurpleXferType type,
    - const char *who);
    };
    -#define PURPLE_PROTOCOL_HAS_FACTORY_IFACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_FACTORY_IFACE))
    -#define PURPLE_PROTOCOL_GET_FACTORY_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_FACTORY_IFACE, \
    - PurpleProtocolFactoryIface))
    +#define PURPLE_IS_PROTOCOL_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_FACTORY))
    +#define PURPLE_PROTOCOL_FACTORY_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_FACTORY, \
    + PurpleProtocolFactoryInterface))
    /**
    * PURPLE_PROTOCOL_IMPLEMENTS:
    * @protocol: The protocol in which to check
    - * @IFACE: The interface name in caps. e.g. <literal>CLIENT_IFACE</literal>
    + * @IFACE: The interface name in caps. e.g. <literal>CLIENT</literal>
    * @func: The function to check
    *
    * Returns: %TRUE if a protocol implements a function in an interface,
    * %FALSE otherwise.
    */
    #define PURPLE_PROTOCOL_IMPLEMENTS(protocol, IFACE, func) \
    - (PURPLE_PROTOCOL_HAS_##IFACE(protocol) && \
    - PURPLE_PROTOCOL_GET_##IFACE(protocol)->func != NULL)
    + (PURPLE_IS_PROTOCOL_##IFACE(protocol) && \
    + PURPLE_PROTOCOL_##IFACE##_GET_IFACE(protocol)->func != NULL)
    G_BEGIN_DECLS
    @@ -745,7 +695,8 @@
    *
    * Returns the user splits of a protocol.
    *
    - * Returns: The user splits of the protocol.
    + * Returns: (element-type PurpleAccountUserSplit) (transfer none): The user
    + * splits of the protocol.
    */
    GList *purple_protocol_get_user_splits(const PurpleProtocol *protocol);
    @@ -755,7 +706,8 @@
    *
    * Returns the account options for a protocol.
    *
    - * Returns: The account options for the protocol.
    + * Returns: (element-type PurpleAccountOption) (transfer none): The account
    + * options for the protocol.
    */
    GList *purple_protocol_get_account_options(const PurpleProtocol *protocol);
    @@ -779,19 +731,6 @@
    */
    PurpleWhiteboardOps *purple_protocol_get_whiteboard_ops(const PurpleProtocol *protocol);
    -/**
    - * purple_protocol_override:
    - * @protocol: The protocol instance.
    - * @flags: What instance data to delete.
    - *
    - * Lets derived protocol types override the base type's instance data, such as
    - * protocol options, user splits, icon spec, etc.
    - * This function is called in the *_init() function of your derived protocol,
    - * to delete the parent type's data so you can define your own.
    - */
    -void purple_protocol_override(PurpleProtocol *protocol,
    - PurpleProtocolOverrideFlags flags);
    -
    /**************************************************************************/
    /* Protocol Class API */
    /**************************************************************************/
    @@ -850,7 +789,7 @@
    /**
    * purple_protocol_client_iface_status_text:
    * @protocol: The #PurpleProtocol instance.
    - * @buddy: The #ProtocolBuddy instance.
    + * @buddy: The #PurpleBuddy instance.
    *
    * Gets the status text for @buddy.
    *
    @@ -1257,73 +1196,6 @@
    PurpleConnection *connection);
    /**************************************************************************/
    -/* Protocol Roomlist Interface API */
    -/**************************************************************************/
    -
    -/**
    - * purple_protocol_roomlist_iface_get_type:
    - *
    - * Returns: The #GType for the protocol roomlist interface.
    - *
    - * Since: 3.0.0
    - */
    -GType purple_protocol_roomlist_iface_get_type(void);
    -
    -/**
    - * purple_protocol_roomlist_iface_get_list:
    - * @protocol: The #PurpleProtocol instance.
    - * @gc: The #PurpleAccount to get the roomlist for.
    - *
    - * Gets the list of rooms for @gc.
    - *
    - * Returns: (transfer full): The roomlist for @gc.
    - *
    - * Since: 3.0.0
    - */
    -PurpleRoomlist *purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol,
    - PurpleConnection *gc);
    -
    -/**
    - * purple_protocol_roomlist_iface_cancel:
    - * @protocol: The #PurpleProtocol instance.
    - * @list: The #PurpleRoomlist instance.
    - *
    - * Requesting a roomlist can take a long time. This function cancels a request
    - * that's already in progress.
    - *
    - * Since: 3.0.0
    - */
    -void purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol,
    - PurpleRoomlist *list);
    -
    -/**
    - * purple_protocol_roomlist_iface_expand_category:
    - * @protocol: The #PurpleProtocol instance.
    - * @list: The #PurpleRoomlist instance.
    - * @category: The category to expand.
    - *
    - * Expands the given @category for @list.
    - *
    - * Since: 3.0.0
    - */
    -void purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol,
    - PurpleRoomlist *list, PurpleRoomlistRoom *category);
    -
    -/**
    - * purple_protocol_roomlist_iface_room_serialize:
    - * @protocol: The #PurpleProtocol instance.
    - * @room: The #PurpleRoomlistRoom instance.
    - *
    - * Serializes @room into a string that will be displayed in a user interface.
    - *
    - * Returns: (transfer full): The serialized form of @room.
    - *
    - * Since: 3.0.0
    - */
    -char *purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol,
    - PurpleRoomlistRoom *room);
    -
    -/**************************************************************************/
    /* Protocol Media Interface API */
    /**************************************************************************/
    @@ -1417,12 +1289,6 @@
    PurpleWhiteboard *purple_protocol_factory_iface_whiteboard_new(PurpleProtocol *protocol,
    PurpleAccount *account, const char *who, int state);
    -/**
    - * purple_protocol_factory_iface_xfer_new: (skip)
    - */
    -PurpleXfer *purple_protocol_factory_iface_xfer_new(PurpleProtocol *protocol,
    - PurpleAccount *account, PurpleXferType type, const char *who);
    -
    G_END_DECLS
    -#endif /* _PROTOCOL_H_ */
    +#endif /* PURPLE_PROTOCOL_H */
    --- a/libpurple/protocols.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols.c Tue Oct 08 21:48:28 2019 -0500
    @@ -578,33 +578,33 @@
    PurpleProtocolClass *klass;
    if (protocol_type == G_TYPE_INVALID) {
    - g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    - _("Protocol type is not registered"));
    + g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    + _("Protocol type is not registered"));
    return NULL;
    }
    if (!g_type_is_a(protocol_type, PURPLE_TYPE_PROTOCOL)) {
    - g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    - _("Protocol type does not inherit PurpleProtocol"));
    + g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    + _("Protocol type does not inherit PurpleProtocol"));
    return NULL;
    }
    if (G_TYPE_IS_ABSTRACT(protocol_type)) {
    - g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    - _("Protocol type is abstract"));
    + g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    + _("Protocol type is abstract"));
    return NULL;
    }
    protocol = g_object_new(protocol_type, NULL);
    if (!protocol) {
    - g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    - _("Could not create protocol instance"));
    + g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    + _("Could not create protocol instance"));
    return NULL;
    }
    if (!purple_protocol_get_id(protocol)) {
    - g_set_error(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    - _("Protocol does not provide an ID"));
    + g_set_error_literal(error, PURPLE_PROTOCOLS_DOMAIN, 0,
    + _("Protocol does not provide an ID"));
    g_object_unref(protocol);
    return NULL;
    --- a/libpurple/protocols.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_PROTOCOLS_H_
    -#define _PURPLE_PROTOCOLS_H_
    +#ifndef PURPLE_PROTOCOLS_H
    +#define PURPLE_PROTOCOLS_H
    /**
    * SECTION:protocols
    * @section_id: libpurple-protocols
    @@ -370,7 +370,7 @@
    *
    * Gets the safe maximum message size in bytes for the protocol.
    *
    - * See #PurpleProtocolClientIface's <literal>get_max_message_size</literal>.
    + * See #PurpleProtocolClientInterface's <literal>get_max_message_size</literal>.
    *
    * Returns: Maximum message size, 0 if unspecified, -1 for infinite.
    */
    @@ -386,6 +386,8 @@
    * @id: The protocol's ID.
    *
    * Finds a protocol by ID.
    + *
    + * Returns: (transfer none): The protocol, if found, or %NULL otherwise.
    */
    PurpleProtocol *purple_protocols_find(const char *id);
    @@ -397,7 +399,8 @@
    *
    * Adds a protocol to the list of protocols.
    *
    - * Returns: The protocol instance if the protocol was added, else %NULL.
    + * Returns: (transfer none): The protocol instance if the protocol was added,
    + * else %NULL.
    */
    PurpleProtocol *purple_protocols_add(GType protocol_type, GError **error);
    @@ -454,4 +457,4 @@
    G_END_DECLS
    -#endif /* _PROTOCOLS_H_ */
    +#endif /* PURPLE_PROTOCOLS_H */
    --- a/libpurple/protocols/bonjour/bonjour.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/bonjour.c Tue Oct 08 21:48:28 2019 -0500
    @@ -31,13 +31,7 @@
    #endif
    #include "internal.h"
    -
    -#include "account.h"
    -#include "accountopt.h"
    -#include "debug.h"
    -#include "plugins.h"
    -#include "util.h"
    -#include "version.h"
    +#include <purple.h>
    #include "bonjour.h"
    #include "mdns_common.h"
    @@ -112,8 +106,6 @@
    /* Start waiting for jabber connections (iChat style) */
    bd->jabber_data = g_new0(BonjourJabber, 1);
    - bd->jabber_data->socket = -1;
    - bd->jabber_data->socket6 = -1;
    bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT);
    bd->jabber_data->account = account;
    @@ -632,8 +624,9 @@
    }
    static void
    -bonjour_protocol_init(PurpleProtocol *protocol)
    +bonjour_protocol_init(BonjourProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountOption *option;
    protocol->id = "prpl-bonjour";
    @@ -664,16 +657,23 @@
    }
    static void
    -bonjour_protocol_class_init(PurpleProtocolClass *klass)
    +bonjour_protocol_class_init(BonjourProtocolClass *klass)
    {
    - klass->login = bonjour_login;
    - klass->close = bonjour_close;
    - klass->status_types = bonjour_status_types;
    - klass->list_icon = bonjour_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = bonjour_login;
    + protocol_class->close = bonjour_close;
    + protocol_class->status_types = bonjour_status_types;
    + protocol_class->list_icon = bonjour_list_icon;
    }
    static void
    -bonjour_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +bonjour_protocol_class_finalize(G_GNUC_UNUSED BonjourProtocolClass *klass)
    +{
    +}
    +
    +static void
    +bonjour_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->status_text = bonjour_status_text;
    client_iface->tooltip_text = bonjour_tooltip_text;
    @@ -682,7 +682,7 @@
    }
    static void
    -bonjour_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +bonjour_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->add_buddy = bonjour_fake_add_buddy;
    server_iface->remove_buddy = bonjour_remove_buddy;
    @@ -693,7 +693,7 @@
    }
    static void
    -bonjour_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +bonjour_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = bonjour_send_im;
    }
    @@ -706,21 +706,20 @@
    xfer_iface->new_xfer = bonjour_new_xfer;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - BonjourProtocol, bonjour_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + BonjourProtocol, bonjour_protocol, PURPLE_TYPE_PROTOCOL, 0,
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - bonjour_protocol_client_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + bonjour_protocol_client_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - bonjour_protocol_server_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + bonjour_protocol_server_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - bonjour_protocol_im_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + bonjour_protocol_im_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
    - bonjour_protocol_xfer_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    + bonjour_protocol_xfer_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -743,7 +742,9 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - bonjour_protocol_register_type(plugin);
    + bonjour_protocol_register_type(G_TYPE_MODULE(plugin));
    +
    + xep_xfer_register(G_TYPE_MODULE(plugin));
    my_protocol = purple_protocols_add(BONJOUR_TYPE_PROTOCOL, error);
    if (!my_protocol)
    --- a/libpurple/protocols/bonjour/bonjour.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/bonjour.h Tue Oct 08 21:48:28 2019 -0500
    @@ -23,13 +23,13 @@
    *
    */
    -#ifndef _BONJOUR_H_
    -#define _BONJOUR_H_
    +#ifndef PURPLE_BONJOUR_BONJOUR_H
    +#define PURPLE_BONJOUR_BONJOUR_H
    #include <gmodule.h>
    #include "internal.h"
    -#include "protocol.h"
    +#include <purple.h>
    #include "mdns_common.h"
    #include "jabber.h"
    @@ -79,4 +79,4 @@
    */
    const char *bonjour_get_jid(PurpleAccount *account);
    -#endif /* _BONJOUR_H_ */
    +#endif /* PURPLE_BONJOUR_BONJOUR_H */
    --- a/libpurple/protocols/bonjour/bonjour_ft.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/bonjour_ft.c Tue Oct 08 21:48:28 2019 -0500
    @@ -20,12 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
    */
    #include "internal.h"
    -#include "util.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "proxy.h"
    -#include "xfer.h"
    -#include "buddy.h"
    +#include <purple.h>
    +
    #include "bonjour.h"
    #include "bonjour_ft.h"
    @@ -38,11 +34,38 @@
    static void
    bonjour_xfer_receive(PurpleConnection *pc, const char *id, const char *sid, const char *from,
    const goffset filesize, const char *filename, int option);
    -static void bonjour_free_xfer(PurpleXfer *xfer);
    /* Look for specific xfer handle */
    static unsigned int next_id = 0;
    +struct _XepXfer
    +{
    + PurpleXfer parent;
    +
    + void *data;
    + char *filename;
    + int filesize;
    + char *iq_id;
    + char *sid;
    + char *recv_id;
    + char *buddy_ip;
    + int mode;
    + PurpleNetworkListenData *listen_data;
    + int sock5_req_state;
    + int rxlen;
    + char rx_buf[0x500];
    + char tx_buf[0x500];
    + PurpleProxyInfo *proxy_info;
    + PurpleProxyConnectData *proxy_connection;
    + char *jid;
    + char *proxy_host;
    + int proxy_port;
    + PurpleXmlNode *streamhost;
    + PurpleBuddy *pb;
    +};
    +
    +G_DEFINE_DYNAMIC_TYPE(XepXfer, xep_xfer, PURPLE_TYPE_XFER);
    +
    static void
    xep_ft_si_reject(BonjourData *bd, const char *id, const char *to, const char *error_code, const char *error_type)
    {
    @@ -84,25 +107,22 @@
    static void bonjour_xfer_cancel_send(PurpleXfer *xfer)
    {
    purple_debug_info("bonjour", "Bonjour-xfer-cancel-send.\n");
    - bonjour_free_xfer(xfer);
    }
    static void bonjour_xfer_request_denied(PurpleXfer *xfer)
    {
    - XepXfer *xf = purple_xfer_get_protocol_data(xfer);
    + XepXfer *xf = XEP_XFER(xfer);
    purple_debug_info("bonjour", "Bonjour-xfer-request-denied.\n");
    - if(xf)
    + if(xf) {
    xep_ft_si_reject(xf->data, xf->sid, purple_xfer_get_remote_user(xfer), "403", "cancel");
    -
    - bonjour_free_xfer(xfer);
    + }
    }
    static void bonjour_xfer_cancel_recv(PurpleXfer *xfer)
    {
    purple_debug_info("bonjour", "Bonjour-xfer-cancel-recv.\n");
    - bonjour_free_xfer(xfer);
    }
    struct socket_cleanup {
    @@ -140,8 +160,6 @@
    sc->handle = purple_input_add(sc->fd, PURPLE_INPUT_READ,
    _wait_for_socket_close, sc);
    }
    -
    - bonjour_free_xfer(xfer);
    }
    static PurpleXfer*
    @@ -161,7 +179,7 @@
    xfer = xfers->data;
    if(xfer == NULL)
    break;
    - xf = purple_xfer_get_protocol_data(xfer);
    + xf = XEP_XFER(xfer);
    if(xf == NULL)
    break;
    if(xf->sid && purple_xfer_get_remote_user(xfer) && purple_strequal(xf->sid, sid) &&
    @@ -179,7 +197,7 @@
    {
    PurpleXmlNode *si_node, *feature, *field, *file, *x;
    XepIq *iq;
    - XepXfer *xf = purple_xfer_get_protocol_data(xfer);
    + XepXfer *xf = XEP_XFER(xfer);
    BonjourData *bd = NULL;
    char buf[32];
    @@ -248,9 +266,7 @@
    if(!to || !xfer)
    return;
    - xf = purple_xfer_get_protocol_data(xfer);
    - if(!xf)
    - return;
    + xf = XEP_XFER(xfer);
    bd = xf->data;
    @@ -298,46 +314,6 @@
    purple_xmlnode_free(node);
    }
    -static void
    -bonjour_free_xfer(PurpleXfer *xfer)
    -{
    - XepXfer *xf;
    -
    - if(xfer == NULL) {
    - purple_debug_info("bonjour", "bonjour-free-xfer-null.\n");
    - return;
    - }
    -
    - purple_debug_misc("bonjour", "bonjour-free-xfer-%p.\n", xfer);
    -
    - xf = purple_xfer_get_protocol_data(xfer);
    - if(xf != NULL) {
    - BonjourData *bd = (BonjourData*)xf->data;
    - if(bd != NULL) {
    - bd->xfer_lists = g_slist_remove(bd->xfer_lists, xfer);
    - purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
    - }
    - if (xf->proxy_connection != NULL)
    - purple_proxy_connect_cancel(xf->proxy_connection);
    - if (xf->proxy_info != NULL)
    - purple_proxy_info_destroy(xf->proxy_info);
    - if (xf->listen_data != NULL)
    - purple_network_listen_cancel(xf->listen_data);
    - g_free(xf->iq_id);
    - g_free(xf->jid);
    - g_free(xf->proxy_host);
    - g_free(xf->buddy_ip);
    - g_free(xf->sid);
    -
    - purple_xmlnode_free_tree(xf->streamhost);
    -
    - g_free(xf);
    - purple_xfer_set_protocol_data(xfer, NULL);
    - }
    -
    - purple_debug_misc("bonjour", "Need close socket.\n");
    -}
    -
    PurpleXfer *
    bonjour_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
    {
    @@ -354,9 +330,14 @@
    return NULL;
    /* Build the file transfer handle */
    - xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_TYPE_SEND, who);
    - xep_xfer = g_new0(XepXfer, 1);
    - purple_xfer_set_protocol_data(xfer, xep_xfer);
    + xep_xfer = g_object_new(
    + XEP_TYPE_XFER,
    + "account", purple_connection_get_account(gc),
    + "type", PURPLE_XFER_TYPE_SEND,
    + "remote-user", who,
    + NULL
    + );
    + xfer = PURPLE_XFER(xep_xfer);
    xep_xfer->data = bd;
    purple_debug_info("bonjour", "Bonjour-new-xfer bd=%p data=%p.\n", bd, xep_xfer->data);
    @@ -366,10 +347,6 @@
    xep_xfer->mode = XEP_BYTESTREAMS;
    xep_xfer->sid = NULL;
    - purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
    - purple_xfer_set_cancel_send_fnc(xfer, bonjour_xfer_cancel_send);
    - purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
    -
    bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
    return xfer;
    @@ -401,9 +378,7 @@
    BonjourBuddy *bb;
    XepXfer *xf;
    - xf = purple_xfer_get_protocol_data(xfer);
    - if(xf == NULL)
    - return;
    + xf = XEP_XFER(xfer);
    purple_debug_info("bonjour", "Bonjour-xfer-init.\n");
    @@ -664,7 +639,7 @@
    char *tmp_iq_id;
    const char *jid, *host, *port;
    int portnum;
    - XepXfer *xf = purple_xfer_get_protocol_data(xfer);
    + XepXfer *xf = XEP_XFER(xfer);
    for(; streamhost; streamhost = purple_xmlnode_get_next_twin(streamhost)) {
    if(!(jid = purple_xmlnode_get_attrib(streamhost, "jid")) ||
    @@ -768,9 +743,16 @@
    purple_debug_info("bonjour", "bonjour-xfer-receive.\n");
    /* Build the file transfer handle */
    - xfer = purple_xfer_new(purple_connection_get_account(pc), PURPLE_XFER_TYPE_RECEIVE, from);
    - xf = g_new0(XepXfer, 1);
    - purple_xfer_set_protocol_data(xfer, xf);
    + xf = g_object_new(
    + XEP_TYPE_XFER,
    + "account", purple_connection_get_account(pc),
    + "type", PURPLE_XFER_TYPE_RECEIVE,
    + "remote-user", from,
    + NULL
    + );
    +
    + xfer = PURPLE_XFER(xf);
    +
    xf->data = bd;
    purple_xfer_set_filename(xfer, filename);
    xf->iq_id = g_strdup(id);
    @@ -778,10 +760,6 @@
    if(filesize > 0)
    purple_xfer_set_size(xfer, filesize);
    - purple_xfer_set_init_fnc(xfer, bonjour_xfer_init);
    - purple_xfer_set_request_denied_fnc(xfer, bonjour_xfer_request_denied);
    - purple_xfer_set_cancel_recv_fnc(xfer, bonjour_xfer_cancel_recv);
    - purple_xfer_set_end_fnc(xfer, bonjour_xfer_end);
    bd->xfer_lists = g_slist_append(bd->xfer_lists, xfer);
    @@ -792,7 +770,7 @@
    bonjour_sock5_request_cb(gpointer data, gint source, PurpleInputCondition cond)
    {
    PurpleXfer *xfer = data;
    - XepXfer *xf = purple_xfer_get_protocol_data(xfer);
    + XepXfer *xf = XEP_XFER(xfer);
    int acceptfd;
    int len = 0;
    @@ -921,7 +899,7 @@
    purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,
    bonjour_sock5_request_cb, xfer));
    - xf = purple_xfer_get_protocol_data(xfer);
    + xf = XEP_XFER(xfer);
    xf->listen_data = NULL;
    bd = xf->data;
    @@ -959,7 +937,7 @@
    return;
    purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n");
    - xf = purple_xfer_get_protocol_data(xfer);
    + xf = XEP_XFER(xfer);
    xf->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, FALSE,
    bonjour_bytestreams_listen, xfer);
    @@ -973,7 +951,7 @@
    bonjour_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
    {
    PurpleXfer *xfer = data;
    - XepXfer *xf = purple_xfer_get_protocol_data(xfer);
    + XepXfer *xf = XEP_XFER(xfer);
    XepIq *iq;
    PurpleXmlNode *q_node, *tmp_node;
    BonjourData *bd;
    @@ -1032,9 +1010,7 @@
    purple_debug_info("bonjour", "bonjour-bytestreams-connect.\n");
    - xf = purple_xfer_get_protocol_data(xfer);
    - if(!xf)
    - return;
    + xf = XEP_XFER(xfer);
    pb = xf->pb;
    name = purple_buddy_get_name(pb);
    @@ -1072,3 +1048,61 @@
    }
    }
    +static void
    +xep_xfer_init(XepXfer *xfer) {
    +
    +}
    +
    +static void
    +xep_xfer_finalize(GObject *obj) {
    + XepXfer *xf = XEP_XFER(obj);
    +
    + BonjourData *bd = (BonjourData*)xf->data;
    + if(bd != NULL) {
    + bd->xfer_lists = g_slist_remove(bd->xfer_lists, PURPLE_XFER(xf));
    + purple_debug_misc("bonjour", "B free xfer from lists(%p).\n", bd->xfer_lists);
    + }
    + if (xf->proxy_connection != NULL) {
    + purple_proxy_connect_cancel(xf->proxy_connection);
    + }
    + if (xf->proxy_info != NULL) {
    + purple_proxy_info_destroy(xf->proxy_info);
    + }
    + if (xf->listen_data != NULL) {
    + purple_network_listen_cancel(xf->listen_data);
    + }
    +
    + g_free(xf->iq_id);
    + g_free(xf->jid);
    + g_free(xf->proxy_host);
    + g_free(xf->buddy_ip);
    + g_free(xf->sid);
    +
    + g_clear_pointer(&xf->streamhost, purple_xmlnode_free_tree);
    +
    + G_OBJECT_CLASS(xep_xfer_parent_class)->finalize(obj);
    +}
    +
    +static void
    +xep_xfer_class_finalize(XepXferClass *klass) {
    +
    +}
    +
    +static void
    +xep_xfer_class_init(XepXferClass *klass) {
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    +
    + obj_class->finalize = xep_xfer_finalize;
    +
    + xfer_class->init = bonjour_xfer_init;
    + xfer_class->request_denied = bonjour_xfer_request_denied;
    + xfer_class->cancel_recv = bonjour_xfer_cancel_recv;
    + xfer_class->cancel_send = bonjour_xfer_cancel_send;
    + xfer_class->end = bonjour_xfer_end;
    +}
    +
    +void
    +xep_xfer_register(GTypeModule *module) {
    + xep_xfer_register_type(module);
    +}
    --- a/libpurple/protocols/bonjour/bonjour_ft.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/bonjour_ft.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,41 +19,22 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
    */
    -#ifndef _BONJOUR_FT_H_
    -#define _BONJOUR_FT_H_
    -#include "network.h"
    -#include "proxy.h"
    -typedef struct _XepXfer XepXfer;
    +#ifndef PURPLE_BONJOUR_BONJOUR_FT_H
    +#define PURPLE_BONJOUR_BONJOUR_FT_H
    +
    +#include <purple.h>
    +
    +G_BEGIN_DECLS
    +
    +#define XEP_TYPE_XFER (xep_xfer_get_type())
    +G_DECLARE_FINAL_TYPE(XepXfer, xep_xfer, XEP, XFER, PurpleXfer);
    +
    typedef enum {
    XEP_BYTESTREAMS = 1,
    XEP_IBB = 2,
    XEP_UNKNOWN = 4
    } XepSiMode;
    -struct _XepXfer
    -{
    - void *data;
    - char *filename;
    - int filesize;
    - char *iq_id;
    - char *sid;
    - char *recv_id;
    - char *buddy_ip;
    - int mode;
    - PurpleNetworkListenData *listen_data;
    - int sock5_req_state;
    - int rxlen;
    - char rx_buf[0x500];
    - char tx_buf[0x500];
    - PurpleProxyInfo *proxy_info;
    - PurpleProxyConnectData *proxy_connection;
    - char *jid;
    - char *proxy_host;
    - int proxy_port;
    - PurpleXmlNode *streamhost;
    - PurpleBuddy *pb;
    -};
    -
    /**
    * Create a new PurpleXfer
    *
    @@ -72,6 +53,10 @@
    void bonjour_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file);
    void xep_si_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb);
    -void
    -xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb);
    -#endif
    +void xep_bytestreams_parse(PurpleConnection *pc, PurpleXmlNode *packet, PurpleBuddy *pb);
    +
    +void xep_xfer_register(GTypeModule *module);
    +
    +G_END_DECLS
    +
    +#endif /* PURPLE_BONJOUR_BONJOUR_FT_H */
    --- a/libpurple/protocols/bonjour/buddy.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/buddy.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,12 +18,11 @@
    #include <stdlib.h>
    #include "internal.h"
    +#include <purple.h>
    +
    #include "buddy.h"
    -#include "account.h"
    -#include "buddylist.h"
    #include "bonjour.h"
    #include "mdns_interface.h"
    -#include "debug.h"
    /**
    * Creates a new buddy.
    @@ -41,24 +40,22 @@
    return buddy;
    }
    -#define _B_CLR(x) g_free(x); x = NULL;
    -
    -void clear_bonjour_buddy_values(BonjourBuddy *buddy) {
    -
    - _B_CLR(buddy->first)
    - _B_CLR(buddy->email);
    - _B_CLR(buddy->ext);
    - _B_CLR(buddy->jid);
    - _B_CLR(buddy->last);
    - _B_CLR(buddy->msg);
    - _B_CLR(buddy->nick);
    - _B_CLR(buddy->node);
    - _B_CLR(buddy->phsh);
    - _B_CLR(buddy->status);
    - _B_CLR(buddy->vc);
    - _B_CLR(buddy->ver);
    - _B_CLR(buddy->AIM);
    -
    +void
    +clear_bonjour_buddy_values(BonjourBuddy *buddy)
    +{
    + g_clear_pointer(&buddy->first, g_free);
    + g_clear_pointer(&buddy->email, g_free);
    + g_clear_pointer(&buddy->ext, g_free);
    + g_clear_pointer(&buddy->jid, g_free);
    + g_clear_pointer(&buddy->last, g_free);
    + g_clear_pointer(&buddy->msg, g_free);
    + g_clear_pointer(&buddy->nick, g_free);
    + g_clear_pointer(&buddy->node, g_free);
    + g_clear_pointer(&buddy->phsh, g_free);
    + g_clear_pointer(&buddy->status, g_free);
    + g_clear_pointer(&buddy->vc, g_free);
    + g_clear_pointer(&buddy->ver, g_free);
    + g_clear_pointer(&buddy->AIM, g_free);
    }
    void
    --- a/libpurple/protocols/bonjour/buddy.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/buddy.h Tue Oct 08 21:48:28 2019 -0500
    @@ -14,12 +14,13 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    */
    -#ifndef _BONJOUR_BUDDY
    -#define _BONJOUR_BUDDY
    +#ifndef PURPLE_BONJOUR_BUDDY_H
    +#define PURPLE_BONJOUR_BUDDY_H
    #include <glib.h>
    -#include "account.h"
    +#include <purple.h>
    +
    #include "jabber.h"
    typedef struct
    @@ -111,4 +112,4 @@
    */
    void bonjour_buddy_delete(BonjourBuddy *buddy);
    -#endif
    +#endif /* PURPLE_BONJOUR_BUDDY_H */
    --- a/libpurple/protocols/bonjour/dns_sd_proxy.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/dns_sd_proxy.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,5 +1,4 @@
    /*
    - *
    * Purple is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    * source distribution.
    @@ -20,6 +19,8 @@
    */
    #include "internal.h"
    +#include <purple.h>
    +
    #include "dns_sd_proxy.h"
    static DNSServiceErrorType (DNSSD_API* _DNSServiceAddRecord)(DNSServiceRef sdRef, DNSRecordRef *RecordRef, DNSServiceFlags flags,
    --- a/libpurple/protocols/bonjour/dns_sd_proxy.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/dns_sd_proxy.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,5 +1,4 @@
    /*
    - *
    * Purple is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    * source distribution.
    @@ -18,10 +17,8 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    */
    -#ifndef _DNS_SD_PROXY
    -#define _DNS_SD_PROXY
    -
    -#include <config.h>
    +#ifndef PURPLE_BONJOUR_DNS_SD_PROXY_H
    +#define PURPLE_BONJOUR_DNS_SD_PROXY_H
    #ifndef _MSC_VER
    #include <stdint.h>
    @@ -196,4 +193,4 @@
    #define TXTRecordSetValue(txtRecord, key, valueSize, value) \
    _wpurple_TXTRecordSetValue(txtRecord, key, valueSize, value)
    -#endif
    +#endif /* PURPLE_BONJOUR_DNS_SD_PROXY_H */
    --- a/libpurple/protocols/bonjour/jabber.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/jabber.c Tue Oct 08 21:48:28 2019 -0500
    @@ -21,6 +21,7 @@
    */
    #include "internal.h"
    +#include <purple.h>
    #ifndef _WIN32
    #include <net/if.h>
    @@ -46,16 +47,6 @@
    #include <ifaddrs.h>
    #endif
    -
    -#include "network.h"
    -#include "eventloop.h"
    -#include "connection.h"
    -#include "buddylist.h"
    -#include "xmlnode.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "util.h"
    -
    #include "jabber.h"
    #include "parser.h"
    #include "bonjour.h"
    @@ -86,7 +77,7 @@
    bonjour_jabber_conv_new(PurpleBuddy *pb, PurpleAccount *account, const char *ip) {
    BonjourJabberConversation *bconv = g_new0(BonjourJabberConversation, 1);
    - bconv->socket = -1;
    + bconv->cancellable = g_cancellable_new();
    bconv->tx_buf = purple_circular_buffer_new(512);
    bconv->tx_handler = 0;
    bconv->rx_handler = 0;
    @@ -232,7 +223,7 @@
    g_free(body);
    }
    -struct _match_buddies_by_address_t {
    +struct _match_buddies_by_address {
    const char *address;
    GSList *matched_buddies;
    };
    @@ -242,7 +233,7 @@
    {
    PurpleBuddy *pb = value;
    BonjourBuddy *bb = NULL;
    - struct _match_buddies_by_address_t *mbba = data;
    + struct _match_buddies_by_address *mbba = data;
    bb = purple_buddy_get_protocol_data(pb);
    @@ -267,32 +258,40 @@
    }
    static void
    -_send_data_write_cb(gpointer data, gint source, PurpleInputCondition cond)
    +_send_data_write_cb(GObject *stream, gpointer data)
    {
    PurpleBuddy *pb = data;
    BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
    BonjourJabberConversation *bconv = bb->conversation;
    - int ret, writelen;
    + gsize writelen;
    + gssize ret;
    + GError *error = NULL;
    writelen = purple_circular_buffer_get_max_read(bconv->tx_buf);
    if (writelen == 0) {
    - purple_input_remove(bconv->tx_handler);
    + g_source_remove(bconv->tx_handler);
    bconv->tx_handler = 0;
    return;
    }
    - ret = send(bconv->socket, purple_circular_buffer_get_output(bconv->tx_buf), writelen, 0);
    + ret = g_pollable_output_stream_write_nonblocking(
    + G_POLLABLE_OUTPUT_STREAM(stream),
    + purple_circular_buffer_get_output(bconv->tx_buf), writelen,
    + bconv->cancellable, &error);
    - if (ret < 0 && errno == EAGAIN)
    + if (ret < 0 && error->code == G_IO_ERROR_WOULD_BLOCK) {
    + g_clear_error(&error);
    return;
    - else if (ret <= 0) {
    + } else if (ret <= 0) {
    PurpleConversation *conv = NULL;
    PurpleAccount *account = NULL;
    - const char *error = g_strerror(errno);
    - purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n",
    - purple_buddy_get_name(pb), error ? error : "(null)");
    + purple_debug_error(
    + "bonjour",
    + "Error sending message to buddy %s error: %s",
    + purple_buddy_get_name(pb),
    + error ? error->message : "(null)");
    account = purple_buddy_get_account(pb);
    @@ -304,6 +303,7 @@
    bonjour_jabber_close_conversation(bb->conversation);
    bb->conversation = NULL;
    + g_clear_error(&error);
    return;
    }
    @@ -313,32 +313,38 @@
    static gint
    _send_data(PurpleBuddy *pb, char *message)
    {
    - gint ret;
    - int len = strlen(message);
    BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
    BonjourJabberConversation *bconv = bb->conversation;
    + gsize len = strlen(message);
    + gssize ret;
    + GError *error = NULL;
    /* If we're not ready to actually send, append it to the buffer */
    if (bconv->tx_handler != 0
    - || bconv->connect_data != NULL
    || bconv->sent_stream_start != FULLY_SENT
    || !bconv->recv_stream_start
    || purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
    ret = -1;
    - errno = EAGAIN;
    + g_set_error_literal(&error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK,
    + "Not yet ready to send.");
    } else {
    - ret = send(bconv->socket, message, len, 0);
    + ret = g_pollable_output_stream_write_nonblocking(
    + G_POLLABLE_OUTPUT_STREAM(bconv->output), message, len,
    + bconv->cancellable, &error);
    }
    - if (ret == -1 && errno == EAGAIN)
    + if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
    ret = 0;
    - else if (ret <= 0) {
    + g_clear_error(&error);
    + } else if (ret <= 0) {
    PurpleConversation *conv;
    PurpleAccount *account;
    - const char *error = g_strerror(errno);
    - purple_debug_error("bonjour", "Error sending message to buddy %s error: %s\n",
    - purple_buddy_get_name(pb), error ? error : "(null)");
    + purple_debug_error(
    + "bonjour",
    + "Error sending message to buddy %s error: %s",
    + purple_buddy_get_name(pb),
    + error ? error->message : "(null)");
    account = purple_buddy_get_account(pb);
    @@ -350,14 +356,23 @@
    bonjour_jabber_close_conversation(bb->conversation);
    bb->conversation = NULL;
    + g_clear_error(&error);
    return -1;
    }
    if (ret < len) {
    /* Don't interfere with the stream starting */
    - if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start && bconv->tx_handler == 0)
    - bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
    - _send_data_write_cb, pb);
    + if (bconv->sent_stream_start == FULLY_SENT &&
    + bconv->recv_stream_start && bconv->tx_handler == 0) {
    + GSource *source =
    + g_pollable_output_stream_create_source(
    + G_POLLABLE_OUTPUT_STREAM(bconv->output),
    + bconv->cancellable);
    + g_source_set_callback(source,
    + (GSourceFunc)_send_data_write_cb,
    + pb, NULL);
    + bconv->tx_handler = g_source_attach(source, NULL);
    + }
    purple_circular_buffer_append(bconv->tx_buf, message + ret, len - ret);
    }
    @@ -396,22 +411,26 @@
    bb->conversation = NULL;
    }
    -static void
    -_client_socket_handler(gpointer data, gint socket, PurpleInputCondition condition)
    +static gboolean
    +_client_socket_handler(GObject *stream, gpointer data)
    {
    BonjourJabberConversation *bconv = data;
    + GError *error = NULL;
    gssize len;
    static char message[4096];
    /* Read the data from the socket */
    - if ((len = recv(socket, message, sizeof(message) - 1, 0)) < 0) {
    - /* There have been an error reading from the socket */
    - if (len != -1 || errno != EAGAIN) {
    - const char *err = g_strerror(errno);
    -
    - purple_debug_warning("bonjour",
    - "receive of %" G_GSSIZE_FORMAT " error: %s\n",
    - len, err ? err : "(null)");
    + len = g_pollable_input_stream_read_nonblocking(
    + G_POLLABLE_INPUT_STREAM(stream), message, sizeof(message) - 1,
    + bconv->cancellable, &error);
    + if (len == -1) {
    + /* There has been an error reading from the socket */
    + if (error == NULL || (error->code != G_IO_ERROR_WOULD_BLOCK &&
    + error->code != G_IO_ERROR_CANCELLED)) {
    + purple_debug_warning(
    + "bonjour",
    + "receive of %" G_GSSIZE_FORMAT " error: %s",
    + len, error ? error->message : "(null)");
    bonjour_jabber_close_conversation(bconv);
    if (bconv->pb != NULL) {
    @@ -424,41 +443,47 @@
    /* I guess we really don't need to notify the user.
    * If they try to send another message it'll reconnect */
    }
    - return;
    + g_clear_error(&error);
    + return FALSE;
    } else if (len == 0) { /* The other end has closed the socket */
    const gchar *name = purple_buddy_get_name(bconv->pb);
    purple_debug_warning("bonjour", "Connection closed (without stream end) by %s.\n", (name) ? name : "(unknown)");
    bonjour_jabber_stream_ended(bconv);
    - return;
    + return FALSE;
    }
    message[len] = '\0';
    purple_debug_info("bonjour", "Receive: -%s- %" G_GSSIZE_FORMAT " bytes\n", message, len);
    bonjour_parser_process(bconv, message, len);
    +
    + return TRUE;
    }
    struct _stream_start_data {
    char *msg;
    };
    -
    static void
    -_start_stream(gpointer data, gint source, PurpleInputCondition condition)
    +_start_stream(GObject *stream, gpointer data)
    {
    BonjourJabberConversation *bconv = data;
    struct _stream_start_data *ss = bconv->stream_data;
    - int len, ret;
    + GError *error = NULL;
    + gsize len;
    + gssize ret;
    len = strlen(ss->msg);
    /* Start Stream */
    - ret = send(source, ss->msg, len, 0);
    + ret = g_pollable_output_stream_write_nonblocking(
    + G_POLLABLE_OUTPUT_STREAM(stream), ss->msg, len,
    + bconv->cancellable, &error);
    - if (ret == -1 && errno == EAGAIN)
    + if (ret == -1 && error->code == G_IO_ERROR_WOULD_BLOCK) {
    + g_clear_error(&error);
    return;
    - else if (ret <= 0) {
    - const char *err = g_strerror(errno);
    + } else if (ret <= 0) {
    PurpleConversation *conv;
    const char *bname = bconv->buddy_name;
    BonjourBuddy *bb = NULL;
    @@ -468,8 +493,11 @@
    bname = purple_buddy_get_name(bconv->pb);
    }
    - purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
    - bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
    + purple_debug_error(
    + "bonjour",
    + "Error starting stream with buddy %s at %s error: %s",
    + bname ? bname : "(unknown)", bconv->ip,
    + error ? error->message : "(null)");
    conv = PURPLE_CONVERSATION(purple_conversations_find_im_with_account(bname, bconv->account));
    if (conv != NULL)
    @@ -481,6 +509,7 @@
    if(bb != NULL)
    bb->conversation = NULL;
    + g_clear_error(&error);
    return;
    }
    @@ -497,19 +526,24 @@
    bconv->stream_data = NULL;
    /* Stream started; process the send buffer if there is one */
    - purple_input_remove(bconv->tx_handler);
    + g_source_remove(bconv->tx_handler);
    bconv->tx_handler = 0;
    bconv->sent_stream_start = FULLY_SENT;
    bonjour_jabber_stream_started(bconv);
    }
    -static gboolean bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv, int client_socket)
    +static gboolean
    +bonjour_jabber_send_stream_init(BonjourJabberConversation *bconv,
    + GError **error)
    {
    - int ret, len;
    - char *stream_start;
    + gchar *stream_start;
    + gsize len;
    + gssize ret;
    const char *bname = bconv->buddy_name;
    + g_return_val_if_fail(error != NULL, FALSE);
    +
    if (bconv->pb != NULL)
    bname = purple_buddy_get_name(bconv->pb);
    @@ -524,15 +558,18 @@
    bconv->sent_stream_start = PARTIALLY_SENT;
    /* Start the stream */
    - ret = send(client_socket, stream_start, len, 0);
    -
    - if (ret == -1 && errno == EAGAIN)
    + ret = g_pollable_output_stream_write_nonblocking(
    + G_POLLABLE_OUTPUT_STREAM(bconv->output), stream_start, len,
    + bconv->cancellable, error);
    + if (ret == -1 && (*error)->code == G_IO_ERROR_WOULD_BLOCK) {
    ret = 0;
    - else if (ret <= 0) {
    - const char *err = g_strerror(errno);
    -
    - purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
    - (*bname) ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
    + g_clear_error(error);
    + } else if (ret <= 0) {
    + purple_debug_error(
    + "bonjour",
    + "Error starting stream with buddy %s at %s error: %s",
    + (*bname) ? bname : "(unknown)", bconv->ip,
    + *error ? (*error)->message : "(null)");
    if (bconv->pb) {
    PurpleConversation *conv;
    @@ -543,7 +580,12 @@
    PURPLE_MESSAGE_ERROR);
    }
    - close(client_socket);
    + purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
    + G_INPUT_STREAM(bconv->input),
    + G_OUTPUT_STREAM(bconv->output));
    + g_clear_object(&bconv->socket);
    + bconv->input = NULL;
    + bconv->output = NULL;
    g_free(stream_start);
    return FALSE;
    @@ -551,14 +593,20 @@
    /* This is unlikely to happen */
    if (ret < len) {
    + GSource *source;
    struct _stream_start_data *ss = g_new(struct _stream_start_data, 1);
    ss->msg = g_strdup(stream_start + ret);
    bconv->stream_data = ss;
    /* Finish sending the stream start */
    - bconv->tx_handler = purple_input_add(client_socket,
    - PURPLE_INPUT_WRITE, _start_stream, bconv);
    - } else
    + source = g_pollable_output_stream_create_source(
    + G_POLLABLE_OUTPUT_STREAM(bconv->output),
    + bconv->cancellable);
    + g_source_set_callback(source, (GSourceFunc)_start_stream, bconv,
    + NULL);
    + bconv->tx_handler = g_source_attach(source, NULL);
    + } else {
    bconv->sent_stream_start = FULLY_SENT;
    + }
    g_free(stream_start);
    @@ -567,17 +615,23 @@
    /* This gets called when we've successfully sent our <stream:stream />
    * AND when we've received a <stream:stream /> */
    -void bonjour_jabber_stream_started(BonjourJabberConversation *bconv) {
    +void
    +bonjour_jabber_stream_started(BonjourJabberConversation *bconv)
    +{
    + GError *error = NULL;
    - if (bconv->sent_stream_start == NOT_SENT && !bonjour_jabber_send_stream_init(bconv, bconv->socket)) {
    - const char *err = g_strerror(errno);
    + if (bconv->sent_stream_start == NOT_SENT &&
    + !bonjour_jabber_send_stream_init(bconv, &error)) {
    const char *bname = bconv->buddy_name;
    if (bconv->pb)
    bname = purple_buddy_get_name(bconv->pb);
    - purple_debug_error("bonjour", "Error starting stream with buddy %s at %s error: %s\n",
    - bname ? bname : "(unknown)", bconv->ip, err ? err : "(null)");
    + purple_debug_error(
    + "bonjour",
    + "Error starting stream with buddy %s at %s error: %s",
    + bname ? bname : "(unknown)", bconv->ip,
    + error ? error->message : "(null)");
    if (bconv->pb) {
    PurpleConversation *conv;
    @@ -589,13 +643,18 @@
    }
    /* We don't want to recieve anything else */
    - close(bconv->socket);
    - bconv->socket = -1;
    + purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
    + G_INPUT_STREAM(bconv->input),
    + G_OUTPUT_STREAM(bconv->output));
    + g_clear_object(&bconv->socket);
    + bconv->input = NULL;
    + bconv->output = NULL;
    /* This must be asynchronous because it destroys the parser and we
    * may be in the middle of parsing.
    */
    async_bonjour_jabber_close_conversation(bconv);
    + g_clear_error(&error);
    return;
    }
    @@ -604,12 +663,15 @@
    if (bconv->sent_stream_start == FULLY_SENT && bconv->recv_stream_start
    && bconv->pb && purple_circular_buffer_get_max_read(bconv->tx_buf) > 0) {
    /* Watch for when we can write the buffered messages */
    - bconv->tx_handler = purple_input_add(bconv->socket, PURPLE_INPUT_WRITE,
    - _send_data_write_cb, bconv->pb);
    + GSource *source = g_pollable_output_stream_create_source(
    + G_POLLABLE_OUTPUT_STREAM(bconv->output),
    + bconv->cancellable);
    + g_source_set_callback(source, (GSourceFunc)_send_data_write_cb,
    + bconv->pb, NULL);
    + bconv->tx_handler = g_source_attach(source, NULL);
    /* We can probably write the data right now. */
    - _send_data_write_cb(bconv->pb, bconv->socket, PURPLE_INPUT_WRITE);
    + _send_data_write_cb(G_OBJECT(bconv->output), bconv->pb);
    }
    -
    }
    #ifndef INET6_ADDRSTRLEN
    @@ -617,46 +679,41 @@
    #endif
    static void
    -_server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition)
    +_server_socket_handler(GSocketService *service, GSocketConnection *connection,
    + GObject *source_object, gpointer data)
    {
    BonjourJabber *jdata = data;
    - common_sockaddr_t their_addr; /* connector's address information */
    - socklen_t sin_size = sizeof(common_sockaddr_t);
    - int client_socket;
    -#ifdef HAVE_INET_NTOP
    - char addrstr[INET6_ADDRSTRLEN];
    -#endif
    - const char *address_text;
    - struct _match_buddies_by_address_t *mbba;
    + GSocketAddress *their_addr; /* connector's address information */
    + GInetAddress *their_inet_addr;
    + gchar *address_text;
    + struct _match_buddies_by_address *mbba;
    BonjourJabberConversation *bconv;
    GSList *buddies;
    -
    - /* Check that it is a read condition */
    - if (condition != PURPLE_INPUT_READ)
    - return;
    + GSource *source;
    - memset(&their_addr, 0, sin_size);
    -
    - if ((client_socket = accept(server_socket, &their_addr.sa, &sin_size)) == -1)
    + their_addr = g_socket_connection_get_remote_address(connection, NULL);
    + if (their_addr == NULL) {
    return;
    - _purple_network_set_common_socket_flags(client_socket);
    + }
    + their_inet_addr = g_inet_socket_address_get_address(
    + G_INET_SOCKET_ADDRESS(their_addr));
    /* Look for the buddy that has opened the conversation and fill information */
    -#ifdef HAVE_INET_NTOP
    - if (their_addr.sa.sa_family == AF_INET6) {
    - address_text = inet_ntop(their_addr.sa.sa_family,
    - &their_addr.in6.sin6_addr, addrstr, sizeof(addrstr));
    + address_text = g_inet_address_to_string(their_inet_addr);
    + if (g_inet_address_get_family(their_inet_addr) ==
    + G_SOCKET_FAMILY_IPV6 &&
    + g_inet_address_get_is_link_local(their_inet_addr)) {
    + gchar *tmp = g_strdup_printf(
    + "%s%%%d", address_text,
    + g_inet_socket_address_get_scope_id(
    + G_INET_SOCKET_ADDRESS(their_addr)));
    + g_free(address_text);
    + address_text = tmp;
    + }
    + g_object_unref(their_addr);
    - append_iface_if_linklocal(addrstr, their_addr.in6.sin6_scope_id);
    - } else {
    - address_text = inet_ntop(their_addr.sa.sa_family,
    - &their_addr.in.sin_addr, addrstr, sizeof(addrstr));
    - }
    -#else
    - address_text = inet_ntoa(their_addr.in.sin_addr);
    -#endif
    purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text);
    - mbba = g_new0(struct _match_buddies_by_address_t, 1);
    + mbba = g_new0(struct _match_buddies_by_address, 1);
    mbba->address = address_text;
    buddies = purple_blist_find_buddies(jdata->account, NULL);
    @@ -665,8 +722,8 @@
    if (mbba->matched_buddies == NULL) {
    purple_debug_info("bonjour", "We don't like invisible buddies, this is not a superheroes comic\n");
    + g_free(address_text);
    g_free(mbba);
    - close(client_socket);
    return;
    }
    @@ -679,138 +736,86 @@
    bconv = bonjour_jabber_conv_new(NULL, jdata->account, address_text);
    /* We wait for the stream start before doing anything else */
    - bconv->socket = client_socket;
    - bconv->rx_handler = purple_input_add(client_socket, PURPLE_INPUT_READ, _client_socket_handler, bconv);
    -
    -}
    -
    -static int
    -start_serversocket_listening(int port, int socket, common_sockaddr_t *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback)
    -{
    - int ret_port = port;
    -
    - purple_debug_info("bonjour", "Attempting to bind IPv%d socket to port %d.\n", ip6 ? 6 : 4, port);
    -
    - /* Try to use the specified port - if it isn't available, use a random port */
    - if (bind(socket, &addr->sa, addr_size) != 0) {
    -
    - purple_debug_info("bonjour", "Unable to bind to specified "
    - "port %i: %s\n", port, g_strerror(errno));
    -
    - if (!allow_port_fallback) {
    - purple_debug_warning("bonjour", "Not attempting random port assignment.\n");
    - return -1;
    - }
    -#ifdef PF_INET6
    - if (ip6)
    - addr->in6.sin6_port = 0;
    - else
    -#endif
    - addr->in.sin_port = 0;
    -
    - if (bind(socket, &addr->sa, addr_size) != 0) {
    - purple_debug_error("bonjour", "Unable to bind IPv%d socket to port: %s\n", ip6 ? 6 : 4, g_strerror(errno));
    - return -1;
    - }
    - ret_port = purple_network_get_port_from_fd(socket);
    - }
    -
    - purple_debug_info("bonjour", "Bound IPv%d socket to port %d.\n", ip6 ? 6 : 4, ret_port);
    -
    - /* Attempt to listen on the bound socket */
    - if (listen(socket, 10) != 0) {
    - purple_debug_error("bonjour", "Unable to listen on IPv%d socket: %s\n", ip6 ? 6 : 4, g_strerror(errno));
    - return -1;
    - }
    -
    - return ret_port;
    + bconv->socket = g_object_ref(connection);
    + bconv->input = g_io_stream_get_input_stream(G_IO_STREAM(bconv->socket));
    + bconv->output =
    + g_io_stream_get_output_stream(G_IO_STREAM(bconv->socket));
    + source = g_pollable_input_stream_create_source(
    + G_POLLABLE_INPUT_STREAM(bconv->input), bconv->cancellable);
    + g_source_set_callback(source, (GSourceFunc)_client_socket_handler,
    + bconv, NULL);
    + bconv->rx_handler = g_source_attach(source, NULL);
    + g_free(address_text);
    }
    gint
    bonjour_jabber_start(BonjourJabber *jdata)
    {
    - int ipv6_port = -1, ipv4_port = -1;
    + GError *error = NULL;
    + guint16 port;
    +
    + purple_debug_info("bonjour", "Attempting to bind IP socket to port %d.",
    + jdata->port);
    - /* Open a listening socket for incoming conversations */
    -#ifdef PF_INET6
    - jdata->socket6 = socket(PF_INET6, SOCK_STREAM, 0);
    -#endif
    - jdata->socket = socket(PF_INET, SOCK_STREAM, 0);
    - if (jdata->socket == -1 && jdata->socket6 == -1) {
    - purple_debug_error("bonjour", "Unable to create socket: %s",
    - g_strerror(errno));
    - return -1;
    - }
    -
    -#ifdef PF_INET6
    - if (jdata->socket6 != -1) {
    - common_sockaddr_t addr6;
    -#ifdef IPV6_V6ONLY
    - int on = 1;
    - if (setsockopt(jdata->socket6, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) != 0) {
    - purple_debug_error("bonjour", "couldn't force IPv6\n");
    + /* Open a listening server for incoming conversations */
    + jdata->service = g_socket_service_new();
    + g_socket_listener_set_backlog(G_SOCKET_LISTENER(jdata->service), 10);
    + port = jdata->port;
    + if (!g_socket_listener_add_inet_port(G_SOCKET_LISTENER(jdata->service),
    + port, NULL, &error)) {
    + purple_debug_info("bonjour",
    + "Unable to bind to specified port %i: %s",
    + port, error ? error->message : "(unknown)");
    + g_clear_error(&error);
    + port = g_socket_listener_add_any_inet_port(
    + G_SOCKET_LISTENER(jdata->service), NULL, &error);
    + if (port == 0) {
    + purple_debug_error(
    + "bonjour", "Unable to create socket: %s",
    + error ? error->message : "(unknown)");
    + g_clear_error(&error);
    return -1;
    }
    -#endif
    - memset(&addr6, 0, sizeof(addr6));
    - addr6.in6.sin6_family = AF_INET6;
    - addr6.in6.sin6_port = htons(jdata->port);
    - addr6.in6.sin6_addr = in6addr_any;
    - ipv6_port = start_serversocket_listening(jdata->port,
    - jdata->socket6, &addr6, sizeof(addr6), TRUE, TRUE);
    - /* Open a watcher in the socket we have just opened */
    - if (ipv6_port > 0) {
    - jdata->watcher_id6 = purple_input_add(jdata->socket6, PURPLE_INPUT_READ, _server_socket_handler, jdata);
    - jdata->port = ipv6_port;
    - } else {
    - purple_debug_error("bonjour", "Failed to start listening on IPv6 socket.\n");
    - close(jdata->socket6);
    - jdata->socket6 = -1;
    - }
    }
    -#endif
    - if (jdata->socket != -1) {
    - common_sockaddr_t addr4;
    - memset(&addr4, 0, sizeof(addr4));
    - addr4.in.sin_family = AF_INET;
    - addr4.in.sin_port = htons(jdata->port);
    - ipv4_port = start_serversocket_listening(jdata->port, jdata->socket,
    - &addr4, sizeof(addr4), FALSE, TRUE);
    - /* Open a watcher in the socket we have just opened */
    - if (ipv4_port > 0) {
    - jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata);
    - jdata->port = ipv4_port;
    - } else {
    - purple_debug_error("bonjour", "Failed to start listening on IPv4 socket.\n");
    - close(jdata->socket);
    - jdata->socket = -1;
    - }
    - }
    + purple_debug_info("bonjour", "Bound IP socket to port %u.", port);
    + jdata->port = port;
    - if (!(ipv6_port > 0 || ipv4_port > 0)) {
    - purple_debug_error("bonjour", "Unable to listen on socket: %s",
    - g_strerror(errno));
    - return -1;
    - }
    + g_signal_connect(G_OBJECT(jdata->service), "incoming",
    + G_CALLBACK(_server_socket_handler), jdata);
    return jdata->port;
    }
    static void
    -_connected_to_buddy(gpointer data, gint source, const gchar *error)
    +_connected_to_buddy(GObject *source, GAsyncResult *res, gpointer user_data)
    {
    - PurpleBuddy *pb = data;
    + PurpleBuddy *pb = user_data;
    BonjourBuddy *bb = purple_buddy_get_protocol_data(pb);
    + GSocketConnection *conn;
    + GSource *rx_source;
    + GError *error = NULL;
    - bb->conversation->connect_data = NULL;
    + conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    + res, &error);
    - if (source < 0) {
    + if (conn == NULL) {
    PurpleConversation *conv = NULL;
    PurpleAccount *account = NULL;
    GSList *tmp = bb->ips;
    - purple_debug_error("bonjour", "Error connecting to buddy %s at %s:%d (%s); Trying next IP address\n",
    - purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, error);
    + if (error && error->code == G_IO_ERROR_CANCELLED) {
    + /* This conversation was closed before it started. */
    + g_error_free(error);
    + return;
    + }
    +
    + purple_debug_error("bonjour",
    + "Error connecting to buddy %s at %s:%d "
    + "(%s); Trying next IP address",
    + purple_buddy_get_name(pb),
    + bb->conversation->ip, bb->port_p2pj,
    + error ? error->message : "(unknown)");
    + g_clear_error(&error);
    /* There may be multiple entries for the same IP - one per
    * presence recieved (e.g. multiple interfaces).
    @@ -825,21 +830,23 @@
    if (tmp != NULL) {
    const gchar *ip;
    - PurpleProxyConnectData *connect_data;
    + GSocketClient *client;
    bb->conversation->ip_link = ip = tmp->data;
    purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n",
    purple_buddy_get_name(pb), ip, bb->port_p2pj);
    - connect_data = purple_proxy_connect(purple_account_get_connection(account),
    - account, ip, bb->port_p2pj, _connected_to_buddy, pb);
    -
    - if (connect_data != NULL) {
    + /* Make sure to connect without a proxy. */
    + client = g_socket_client_new();
    + if (client != NULL) {
    g_free(bb->conversation->ip);
    bb->conversation->ip = g_strdup(ip);
    - bb->conversation->connect_data = connect_data;
    -
    + g_socket_client_connect_to_host_async(
    + client, ip, bb->port_p2pj,
    + bb->conversation->cancellable,
    + _connected_to_buddy, pb);
    + g_object_unref(client);
    return;
    }
    }
    @@ -857,13 +864,22 @@
    return;
    }
    - if (!bonjour_jabber_send_stream_init(bb->conversation, source)) {
    - const char *err = g_strerror(errno);
    + bb->conversation->socket = conn;
    + bb->conversation->input =
    + g_io_stream_get_input_stream(G_IO_STREAM(conn));
    + bb->conversation->output =
    + g_io_stream_get_output_stream(G_IO_STREAM(conn));
    +
    + if (!bonjour_jabber_send_stream_init(bb->conversation, &error)) {
    PurpleConversation *conv = NULL;
    PurpleAccount *account = NULL;
    - purple_debug_error("bonjour", "Error starting stream with buddy %s at %s:%d error: %s\n",
    - purple_buddy_get_name(pb), bb->conversation->ip, bb->port_p2pj, err ? err : "(null)");
    + purple_debug_error("bonjour",
    + "Error starting stream with buddy %s at "
    + "%s:%d error: %s",
    + purple_buddy_get_name(pb),
    + bb->conversation->ip, bb->port_p2pj,
    + error ? error->message : "(null)");
    account = purple_buddy_get_account(pb);
    @@ -873,16 +889,19 @@
    _("Unable to send the message, the conversation couldn't be started."),
    PURPLE_MESSAGE_ERROR);
    - close(source);
    bonjour_jabber_close_conversation(bb->conversation);
    bb->conversation = NULL;
    + g_clear_error(&error);
    return;
    }
    /* Start listening for the stream acknowledgement */
    - bb->conversation->socket = source;
    - bb->conversation->rx_handler = purple_input_add(source,
    - PURPLE_INPUT_READ, _client_socket_handler, bb->conversation);
    + rx_source = g_pollable_input_stream_create_source(
    + G_POLLABLE_INPUT_STREAM(bb->conversation->input),
    + bb->conversation->cancellable);
    + g_source_set_callback(rx_source, (GSourceFunc)_client_socket_handler,
    + bb->conversation, NULL);
    + bb->conversation->rx_handler = g_source_attach(rx_source, NULL);
    }
    void
    @@ -943,10 +962,10 @@
    PurpleConnection *pc = purple_account_get_connection(bconv->account);
    BonjourData *bd = purple_connection_get_protocol_data(pc);
    BonjourJabber *jdata = bd->jabber_data;
    - struct _match_buddies_by_address_t *mbba;
    + struct _match_buddies_by_address *mbba;
    GSList *buddies;
    - mbba = g_new0(struct _match_buddies_by_address_t, 1);
    + mbba = g_new0(struct _match_buddies_by_address, 1);
    mbba->address = bconv->ip;
    buddies = purple_blist_find_buddies(jdata->account, NULL);
    @@ -1004,39 +1023,31 @@
    return NULL;
    /* Check if there is a previously open conversation */
    - if (bb->conversation == NULL)
    - {
    - PurpleProxyConnectData *connect_data;
    - PurpleProxyInfo *proxy_info;
    - const char *ip = bb->ips->data; /* Start with the first IP address. */
    -
    - purple_debug_info("bonjour", "Starting conversation with %s at %s:%d\n", to, ip, bb->port_p2pj);
    + if (bb->conversation == NULL) {
    + GSocketClient *client;
    + /* Start with the first IP address. */
    + const gchar *ip = bb->ips->data;
    - /* Make sure that the account always has a proxy of "none".
    - * This is kind of dirty, but proxy_connect_none() isn't exposed. */
    - proxy_info = purple_account_get_proxy_info(jdata->account);
    - if (proxy_info == NULL) {
    - proxy_info = purple_proxy_info_new();
    - purple_account_set_proxy_info(jdata->account, proxy_info);
    - }
    - purple_proxy_info_set_proxy_type(proxy_info, PURPLE_PROXY_NONE);
    + purple_debug_info("bonjour",
    + "Starting conversation with %s at %s:%d", to,
    + ip, bb->port_p2pj);
    - connect_data = purple_proxy_connect(
    - purple_account_get_connection(jdata->account),
    - jdata->account,
    - ip, bb->port_p2pj, _connected_to_buddy, pb);
    -
    - if (connect_data == NULL) {
    - purple_debug_error("bonjour", "Unable to connect to buddy (%s).\n", to);
    + /* Make sure to connect without a proxy. */
    + client = g_socket_client_new();
    + if (client == NULL) {
    + purple_debug_error("bonjour",
    + "Unable to connect to buddy (%s).",
    + to);
    return NULL;
    }
    bb->conversation = bonjour_jabber_conv_new(pb, jdata->account, ip);
    - bb->conversation->connect_data = connect_data;
    bb->conversation->ip_link = ip;
    - /* We don't want _send_data() to register the tx_handler;
    - * that neeeds to wait until we're actually connected. */
    - bb->conversation->tx_handler = 0;
    +
    + g_socket_client_connect_to_host_async(
    + client, ip, bb->port_p2pj,
    + bb->conversation->cancellable, _connected_to_buddy, pb);
    + g_object_unref(client);
    }
    return pb;
    }
    @@ -1121,91 +1132,109 @@
    void
    bonjour_jabber_close_conversation(BonjourJabberConversation *bconv)
    {
    - if (bconv != NULL) {
    - BonjourData *bd = NULL;
    + BonjourData *bd = NULL;
    + PurpleConnection *pc = NULL;
    - PurpleConnection *pc = purple_account_get_connection(bconv->account);
    + if (bconv == NULL) {
    + return;
    + }
    - PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
    + pc = purple_account_get_connection(bconv->account);
    + PURPLE_ASSERT_CONNECTION_IS_VALID(pc);
    - bd = purple_connection_get_protocol_data(pc);
    - if (bd) {
    - bd->jabber_data->pending_conversations = g_slist_remove(
    - bd->jabber_data->pending_conversations, bconv);
    - }
    + bd = purple_connection_get_protocol_data(pc);
    + if (bd) {
    + bd->jabber_data->pending_conversations = g_slist_remove(
    + bd->jabber_data->pending_conversations, bconv);
    + }
    - /* Cancel any file transfers that are waiting to begin */
    - /* There wont be any transfers if it hasn't been attached to a buddy */
    - if (bconv->pb != NULL && bd != NULL) {
    - GSList *xfers, *tmp_next;
    - xfers = bd->xfer_lists;
    - while(xfers != NULL) {
    - PurpleXfer *xfer = xfers->data;
    - tmp_next = xfers->next;
    - /* We only need to cancel this if it hasn't actually started transferring. */
    - /* This will change if we ever support IBB transfers. */
    - if (purple_strequal(purple_xfer_get_remote_user(xfer), purple_buddy_get_name(bconv->pb))
    - && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
    - || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
    - purple_xfer_cancel_remote(xfer);
    - }
    - xfers = tmp_next;
    + /* Cancel any file transfers that are waiting to begin */
    + /* There wont be any transfers if it hasn't been attached to a buddy */
    + if (bconv->pb != NULL && bd != NULL) {
    + GSList *xfers, *tmp_next;
    + xfers = bd->xfer_lists;
    + while (xfers != NULL) {
    + PurpleXfer *xfer = xfers->data;
    + tmp_next = xfers->next;
    + /* We only need to cancel this if it hasn't actually started transferring. */
    + /* This will change if we ever support IBB transfers. */
    + if (purple_strequal(purple_xfer_get_remote_user(xfer), purple_buddy_get_name(bconv->pb))
    + && (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_NOT_STARTED
    + || purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_UNKNOWN)) {
    + purple_xfer_cancel_remote(xfer);
    + }
    + xfers = tmp_next;
    + }
    + }
    +
    + /* Close the socket and remove the watcher */
    + if (bconv->socket != NULL) {
    + /* Send the end of the stream to the other end of the conversation */
    + if (bconv->sent_stream_start == FULLY_SENT) {
    + size_t len = strlen(STREAM_END);
    + if (g_pollable_output_stream_write_nonblocking(
    + G_POLLABLE_OUTPUT_STREAM(bconv->output),
    + STREAM_END, len, bconv->cancellable,
    + NULL) != (gssize)len) {
    + purple_debug_error("bonjour",
    + "bonjour_jabber_close_conversation: "
    + "couldn't send data\n");
    }
    }
    + /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
    + purple_gio_graceful_close(G_IO_STREAM(bconv->socket),
    + G_INPUT_STREAM(bconv->input),
    + G_OUTPUT_STREAM(bconv->output));
    + }
    + if (bconv->rx_handler != 0) {
    + g_source_remove(bconv->rx_handler);
    + bconv->rx_handler = 0;
    + }
    + if (bconv->tx_handler != 0) {
    + g_source_remove(bconv->tx_handler);
    + bconv->tx_handler = 0;
    + }
    - /* Close the socket and remove the watcher */
    - if (bconv->socket >= 0) {
    - /* Send the end of the stream to the other end of the conversation */
    - if (bconv->sent_stream_start == FULLY_SENT) {
    - size_t len = strlen(STREAM_END);
    - if (send(bconv->socket, STREAM_END, len, 0) != (gssize)len) {
    - purple_debug_error("bonjour",
    - "bonjour_jabber_close_conversation: "
    - "couldn't send data\n");
    - }
    - }
    - /* TODO: We're really supposed to wait for "</stream:stream>" before closing the socket */
    - close(bconv->socket);
    - }
    - if (bconv->rx_handler != 0)
    - purple_input_remove(bconv->rx_handler);
    - if (bconv->tx_handler > 0)
    - purple_input_remove(bconv->tx_handler);
    + /* Cancel any pending operations. */
    + if (bconv->cancellable != NULL) {
    + g_cancellable_cancel(bconv->cancellable);
    + g_clear_object(&bconv->cancellable);
    + }
    - /* Free all the data related to the conversation */
    - g_object_unref(G_OBJECT(bconv->tx_buf));
    - if (bconv->connect_data != NULL)
    - purple_proxy_connect_cancel(bconv->connect_data);
    - if (bconv->stream_data != NULL) {
    - struct _stream_start_data *ss = bconv->stream_data;
    - g_free(ss->msg);
    - g_free(ss);
    - }
    + /* Free all the data related to the conversation */
    + g_clear_object(&bconv->socket);
    + bconv->input = NULL;
    + bconv->output = NULL;
    +
    + g_object_unref(G_OBJECT(bconv->tx_buf));
    + if (bconv->stream_data != NULL) {
    + struct _stream_start_data *ss = bconv->stream_data;
    + g_free(ss->msg);
    + g_free(ss);
    + }
    - if (bconv->context != NULL)
    - bonjour_parser_setup(bconv);
    + if (bconv->context != NULL) {
    + bonjour_parser_setup(bconv);
    + }
    - if (bconv->close_timeout != 0)
    - g_source_remove(bconv->close_timeout);
    + if (bconv->close_timeout != 0) {
    + g_source_remove(bconv->close_timeout);
    + }
    - g_free(bconv->buddy_name);
    - g_free(bconv->ip);
    - g_free(bconv);
    - }
    + g_free(bconv->buddy_name);
    + g_free(bconv->ip);
    + g_free(bconv);
    }
    void
    bonjour_jabber_stop(BonjourJabber *jdata)
    {
    /* Close the server socket and remove the watcher */
    - if (jdata->socket >= 0)
    - close(jdata->socket);
    - if (jdata->watcher_id > 0)
    - purple_input_remove(jdata->watcher_id);
    - if (jdata->socket6 >= 0)
    - close(jdata->socket6);
    - if (jdata->watcher_id6 > 0)
    - purple_input_remove(jdata->watcher_id6);
    + if (jdata->service) {
    + g_socket_service_stop(jdata->service);
    + g_socket_listener_close(G_SOCKET_LISTENER(jdata->service));
    + g_clear_object(&jdata->service);
    + }
    /* Close all the conversation sockets and remove all the watchers after sending end streams */
    if (!purple_account_is_disconnected(jdata->account)) {
    @@ -1217,7 +1246,6 @@
    if (bb && bb->conversation) {
    /* Any ongoing connection attempt is cancelled
    * when a connection is destroyed */
    - bb->conversation->connect_data = NULL;
    bonjour_jabber_close_conversation(bb->conversation);
    bb->conversation = NULL;
    }
    @@ -1350,7 +1378,6 @@
    int ret;
    #ifdef HAVE_GETIFADDRS /* This is required for IPv6 */
    - {
    struct ifaddrs *ifap, *ifa;
    common_sockaddr_t addr;
    char addrstr[INET6_ADDRSTRLEN];
    @@ -1392,10 +1419,7 @@
    }
    freeifaddrs(ifap);
    -
    - }
    #else
    - {
    char *tmp;
    struct ifconf ifc;
    struct ifreq *ifr;
    @@ -1430,8 +1454,7 @@
    address_text = inet_ntoa(sinptr->sin_addr);
    ips = g_slist_prepend(ips, g_strdup(address_text));
    }
    - }
    - }
    + }
    }
    #endif
    --- a/libpurple/protocols/bonjour/jabber.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/jabber.h Tue Oct 08 21:48:28 2019 -0500
    @@ -23,37 +23,33 @@
    *
    */
    -#ifndef _BONJOUR_JABBER_H_
    -#define _BONJOUR_JABBER_H_
    +#ifndef PURPLE_BONJOUR_JABBER_H
    +#define PURPLE_BONJOUR_JABBER_H
    #include <libxml/parser.h>
    -#include "xmlnode.h"
    -
    -#include "account.h"
    -#include "circularbuffer.h"
    +#include <purple.h>
    typedef struct
    {
    - gint port;
    - gint socket;
    - gint socket6;
    - gint watcher_id;
    - gint watcher_id6;
    + GSocketService *service;
    + guint16 port;
    PurpleAccount *account;
    GSList *pending_conversations;
    } BonjourJabber;
    typedef struct
    {
    - gint socket;
    + GCancellable *cancellable;
    + GSocketConnection *socket;
    + GInputStream *input;
    + GOutputStream *output;
    guint rx_handler;
    guint tx_handler;
    guint close_timeout;
    PurpleCircularBuffer *tx_buf;
    int sent_stream_start; /* 0 = Unsent, 1 = Partial, 2 = Complete */
    gboolean recv_stream_start;
    - PurpleProxyConnectData *connect_data;
    gpointer stream_data;
    xmlParserCtxt *context;
    PurpleXmlNode *current;
    @@ -113,4 +109,4 @@
    void append_iface_if_linklocal(char *ip, guint32 interface_param);
    -#endif /* _BONJOUR_JABBER_H_ */
    +#endif /* PURPLE_BONJOUR_JABBER_H */
    --- a/libpurple/protocols/bonjour/mdns_avahi.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/mdns_avahi.c Tue Oct 08 21:48:28 2019 -0500
    @@ -15,9 +15,9 @@
    */
    #include "internal.h"
    +#include <purple.h>
    #include "mdns_interface.h"
    -#include "debug.h"
    #include "buddy.h"
    #include "bonjour.h"
    @@ -561,10 +561,8 @@
    if (new_group && (ret = avahi_entry_group_commit(idata->buddy_icon_group)) < 0) {
    purple_debug_error("bonjour",
    "Failed to commit buddy icon group. Error: %s\n", avahi_strerror(ret));
    - if (new_group) {
    - avahi_entry_group_free(idata->buddy_icon_group);
    - idata->buddy_icon_group = NULL;
    - }
    + avahi_entry_group_free(idata->buddy_icon_group);
    + idata->buddy_icon_group = NULL;
    return FALSE;
    }
    } else if (idata->buddy_icon_group != NULL) {
    --- a/libpurple/protocols/bonjour/mdns_common.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/mdns_common.c Tue Oct 08 21:48:28 2019 -0500
    @@ -17,7 +17,7 @@
    #include <string.h>
    #include "internal.h"
    -#include "debug.h"
    +#include <purple.h>
    #include "mdns_common.h"
    #include "mdns_interface.h"
    @@ -70,9 +70,17 @@
    return buffer;
    }
    +static inline GSList *
    +_add_txt_record(GSList *list, const gchar *key, const gchar *value)
    +{
    + PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1);
    + kvp->key = g_strdup(key);
    + kvp->value = g_strdup(get_max_txt_record_value(key, value));
    + return g_slist_prepend(list, kvp);
    +}
    +
    static GSList *generate_presence_txt_records(BonjourDnsSd *data) {
    GSList *ret = NULL;
    - PurpleKeyValuePair *kvp;
    char portstring[6];
    const char *jid, *aim, *email;
    @@ -83,12 +91,6 @@
    aim = purple_account_get_string(data->account, "AIM", NULL);
    email = purple_account_get_string(data->account, "email", NULL);
    -#define _M_ADD_R(k, v) \
    - kvp = g_new0(PurpleKeyValuePair, 1); \
    - kvp->key = g_strdup(k); \
    - kvp->value = g_strdup(get_max_txt_record_value(k, v)); \
    - ret = g_slist_prepend(ret, kvp); \
    -
    /* We should try to follow XEP-0174, but some clients have "issues", so we humor them.
    * See http://telepathy.freedesktop.org/wiki/SalutInteroperability
    */
    @@ -101,34 +103,34 @@
    */
    /* Needed by iChat */
    - _M_ADD_R("txtvers", "1")
    + ret = _add_txt_record(ret, "txtvers", "1");
    /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
    - _M_ADD_R("1st", data->first)
    + ret = _add_txt_record(ret, "1st", data->first);
    /* Needed by Gaim/Pidgin <= 2.0.1 (remove at some point) */
    - _M_ADD_R("last", data->last)
    + ret = _add_txt_record(ret, "last", data->last);
    /* Needed by Adium */
    - _M_ADD_R("port.p2pj", portstring)
    + ret = _add_txt_record(ret, "port.p2pj", portstring);
    /* Needed by iChat, Gaim/Pidgin <= 2.0.1 */
    - _M_ADD_R("status", data->status)
    - _M_ADD_R("node", "libpurple")
    - _M_ADD_R("ver", VERSION)
    + ret = _add_txt_record(ret, "status", data->status);
    + ret = _add_txt_record(ret, "node", "libpurple");
    + ret = _add_txt_record(ret, "ver", VERSION);
    /* Currently always set to "!" since we don't support AV and wont ever be in a conference */
    - _M_ADD_R("vc", data->vc)
    + ret = _add_txt_record(ret, "vc", data->vc);
    if (email != NULL && *email != '\0') {
    - _M_ADD_R("email", email)
    + ret = _add_txt_record(ret, "email", email);
    }
    if (jid != NULL && *jid != '\0') {
    - _M_ADD_R("jid", jid)
    + ret = _add_txt_record(ret, "jid", jid);
    }
    /* Nonstandard, but used by iChat */
    if (aim != NULL && *aim != '\0') {
    - _M_ADD_R("AIM", aim)
    + ret = _add_txt_record(ret, "AIM", aim);
    }
    if (data->msg != NULL && *data->msg != '\0') {
    - _M_ADD_R("msg", data->msg)
    + ret = _add_txt_record(ret, "msg", data->msg);
    }
    if (data->phsh != NULL && *data->phsh != '\0') {
    - _M_ADD_R("phsh", data->phsh)
    + ret = _add_txt_record(ret, "phsh", data->phsh);
    }
    /* TODO: ext, nick */
    --- a/libpurple/protocols/bonjour/mdns_common.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/mdns_common.h Tue Oct 08 21:48:28 2019 -0500
    @@ -14,8 +14,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    */
    -#ifndef _BONJOUR_MDNS_COMMON
    -#define _BONJOUR_MDNS_COMMON
    +#ifndef PURPLE_BONJOUR_MDNS_COMMON_H
    +#define PURPLE_BONJOUR_MDNS_COMMON_H
    #include "mdns_types.h"
    @@ -59,4 +59,4 @@
    void bonjour_dns_sd_set_jid(PurpleAccount *account, const char *hostname);
    -#endif
    +#endif /* PURPLE_BONJOUR_MDNS_COMMON_H */
    --- a/libpurple/protocols/bonjour/mdns_dns_sd.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/mdns_dns_sd.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,5 +1,4 @@
    /*
    - *
    * Purple is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    * source distribution.
    @@ -20,7 +19,7 @@
    */
    #include "internal.h"
    -#include "debug.h"
    +#include <purple.h>
    #include "buddy.h"
    #include "mdns_interface.h"
    --- a/libpurple/protocols/bonjour/mdns_interface.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/mdns_interface.h Tue Oct 08 21:48:28 2019 -0500
    @@ -14,8 +14,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    */
    -#ifndef _BONJOUR_MDNS_INTERFACE
    -#define _BONJOUR_MDNS_INTERFACE
    +#ifndef PURPLE_BONJOUR_MDNS_INTERFACE_H
    +#define PURPLE_BONJOUR_MDNS_INTERFACE_H
    #include "mdns_types.h"
    #include "buddy.h"
    @@ -36,4 +36,4 @@
    void _mdns_retrieve_buddy_icon(BonjourBuddy* buddy);
    -#endif
    +#endif /* PURPLE_BONJOUR_MDNS_INTERFACE_H */
    --- a/libpurple/protocols/bonjour/mdns_types.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/mdns_types.h Tue Oct 08 21:48:28 2019 -0500
    @@ -14,11 +14,12 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    */
    -#ifndef _BONJOUR_MDNS_TYPES
    -#define _BONJOUR_MDNS_TYPES
    +#ifndef PURPLE_BONJOUR_MDNS_TYPES_H
    +#define PURPLE_BONJOUR_MDNS_TYPES_H
    #include <glib.h>
    -#include "account.h"
    +
    +#include <purple.h>
    #define LINK_LOCAL_RECORD_NAME "_presence._tcp."
    @@ -42,4 +43,4 @@
    PUBLISH_UPDATE
    } PublishType;
    -#endif
    +#endif /* PURPLE_BONJOUR_MDNS_TYPES_H */
    --- a/libpurple/protocols/bonjour/parser.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/parser.c Tue Oct 08 21:48:28 2019 -0500
    @@ -21,15 +21,12 @@
    *
    */
    #include "internal.h"
    +#include <purple.h>
    #include <libxml/parser.h>
    -#include "connection.h"
    -#include "debug.h"
    #include "jabber.h"
    #include "parser.h"
    -#include "util.h"
    -#include "xmlnode.h"
    static gboolean
    parse_from_attrib_and_find_buddy(BonjourJabberConversation *bconv, int nb_attributes, const xmlChar **attributes) {
    --- a/libpurple/protocols/bonjour/parser.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/bonjour/parser.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_BONJOUR_PARSER_H_
    -#define _PURPLE_BONJOUR_PARSER_H_
    +
    +#ifndef PURPLE_BONJOUR_PARSER_H
    +#define PURPLE_BONJOUR_PARSER_H
    #include "buddy.h"
    #include "jabber.h"
    @@ -30,4 +31,4 @@
    void bonjour_parser_setup(BonjourJabberConversation *bconv);
    void bonjour_parser_process(BonjourJabberConversation *bconv, const char *buf, int len);
    -#endif /* _PURPLE_BONJOUR_PARSER_H_ */
    +#endif /* PURPLE_BONJOUR_PARSER_H */
    --- a/libpurple/protocols/facebook/api.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/api.c Tue Oct 08 21:48:28 2019 -0500
    @@ -87,6 +87,9 @@
    GDestroyNotify func;
    };
    +static void fb_api_error_literal(FbApi *api, FbApiError error,
    + const gchar *msg);
    +
    static void
    fb_api_attach(FbApi *api, FbId aid, const gchar *msgid, FbApiMessage *msg);
    @@ -597,7 +600,7 @@
    priv = api->priv;
    if (G_UNLIKELY(size == 0)) {
    - fb_api_error(api, FB_API_ERROR_GENERAL, _("Empty JSON data"));
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL, _("Empty JSON data"));
    return FALSE;
    }
    @@ -667,7 +670,7 @@
    }
    if (msg != NULL) {
    - fb_api_error(api, errc, "%s", msg);
    + fb_api_error_literal(api, errc, msg);
    json_node_free(root);
    g_free(msg);
    return FALSE;
    @@ -875,8 +878,8 @@
    hata = purple_http_response_get_data(res, NULL);
    if (!purple_strequal(hata, "true")) {
    - fb_api_error(api, FB_API_ERROR,
    - _("Failed generic API operation"));
    + fb_api_error_literal(api, FB_API_ERROR,
    + _("Failed generic API operation"));
    }
    }
    @@ -1086,8 +1089,8 @@
    priv->unread = fb_json_values_next_int(values, 0);
    if (priv->sid == 0) {
    - fb_api_error(api, FB_API_ERROR_GENERAL,
    - _("Failed to get sync_sequence_id"));
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL,
    + _("Failed to get sync_sequence_id"));
    } else {
    fb_api_connect_queue(api);
    }
    @@ -1162,8 +1165,8 @@
    );
    if (!fb_json_values_next_bool(values, TRUE)) {
    - fb_api_error(api, FB_API_ERROR_GENERAL,
    - _("Failed to mark thread as read"));
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL,
    + _("Failed to mark thread as read"));
    }
    g_object_unref(values);
    @@ -1367,8 +1370,8 @@
    fb_api_message_send(api, msg);
    }
    } else {
    - fb_api_error(api, FB_API_ERROR_GENERAL,
    - "Failed to send message");
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL,
    + "Failed to send message");
    }
    g_object_unref(values);
    @@ -2027,6 +2030,18 @@
    return priv->invisible;
    }
    +static void
    +fb_api_error_literal(FbApi *api, FbApiError error, const gchar *msg)
    +{
    + GError *err;
    +
    + g_return_if_fail(FB_IS_API(api));
    +
    + err = g_error_new_literal(FB_API_ERROR, error, msg);
    +
    + fb_api_error_emit(api, err);
    +}
    +
    void
    fb_api_error(FbApi *api, FbApiError error, const gchar *format, ...)
    {
    @@ -2208,8 +2223,8 @@
    node = fb_json_node_get_nth(root, 0);
    if (node == NULL) {
    - fb_api_error(api, FB_API_ERROR_GENERAL,
    - _("Failed to obtain contact information"));
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL,
    + _("Failed to obtain contact information"));
    json_node_free(root);
    return;
    }
    @@ -2329,7 +2344,10 @@
    split = g_strsplit_set(decoded, ":", 4);
    - g_return_val_if_fail(g_strv_length(split) == 4, users);
    + if (g_strv_length(split) != 4) {
    + g_strfreev(split);
    + g_return_val_if_reached(users);
    + }
    users = g_slist_prepend(users, g_strdup(split[2]));
    @@ -2693,8 +2711,8 @@
    node = fb_json_node_get_nth(root, 0);
    if (node == NULL) {
    - fb_api_error(api, FB_API_ERROR_GENERAL,
    - _("Failed to obtain unread messages"));
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL,
    + _("Failed to obtain unread messages"));
    json_node_free(root);
    return;
    }
    @@ -3026,8 +3044,8 @@
    node = fb_json_node_get_nth(root, 0);
    if (node == NULL) {
    - fb_api_error(api, FB_API_ERROR_GENERAL,
    - _("Failed to obtain thread information"));
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL,
    + _("Failed to obtain thread information"));
    json_node_free(root);
    return;
    }
    @@ -3039,8 +3057,8 @@
    if (thrd.tid) {
    g_signal_emit_by_name(api, "thread-kicked", &thrd);
    } else {
    - fb_api_error(api, FB_API_ERROR_GENERAL,
    - _("Failed to parse thread information"));
    + fb_api_error_literal(api, FB_API_ERROR_GENERAL,
    + _("Failed to parse thread information"));
    }
    } else {
    fb_api_error_emit(api, err);
    --- a/libpurple/protocols/facebook/api.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/api.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_API_H_
    -#define _FACEBOOK_API_H_
    +#ifndef PURPLE_FACEBOOK_API_H
    +#define PURPLE_FACEBOOK_API_H
    /**
    * SECTION:api
    @@ -971,4 +971,4 @@
    void
    fb_api_user_free(FbApiUser *user);
    -#endif /* _FACEBOOK_API_H_ */
    +#endif /* PURPLE_FACEBOOK_API_H */
    --- a/libpurple/protocols/facebook/data.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/data.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_DATA_H_
    -#define _FACEBOOK_DATA_H_
    +#ifndef PURPLE_FACEBOOK_DATA_H
    +#define PURPLE_FACEBOOK_DATA_H
    /**
    * SECTION:data
    @@ -336,4 +336,4 @@
    void
    fb_data_image_queue(FbData *fata);
    -#endif /* _FACEBOOK_DATA_H_ */
    +#endif /* PURPLE_FACEBOOK_DATA_H */
    --- a/libpurple/protocols/facebook/facebook.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/facebook.c Tue Oct 08 21:48:28 2019 -0500
    @@ -83,7 +83,8 @@
    grp = purple_group_new(title);
    node = NULL;
    - for (n = purple_blist_get_root(); n != NULL; n = n->next) {
    + for (n = purple_blist_get_default_root(); n != NULL;
    + n = n->next) {
    node = n;
    }
    @@ -1535,8 +1536,9 @@
    }
    static void
    -facebook_protocol_init(PurpleProtocol *protocol)
    +facebook_protocol_init(FacebookProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    GList *opts = NULL;
    PurpleAccountOption *opt;
    @@ -1572,16 +1574,23 @@
    }
    static void
    -facebook_protocol_class_init(PurpleProtocolClass *klass)
    +facebook_protocol_class_init(FacebookProtocolClass *klass)
    {
    - klass->login = fb_login;
    - klass->close = fb_close;
    - klass->status_types = fb_status_types;
    - klass->list_icon = fb_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = fb_login;
    + protocol_class->close = fb_close;
    + protocol_class->status_types = fb_status_types;
    + protocol_class->list_icon = fb_list_icon;
    }
    static void
    -facebook_protocol_client_iface_init(PurpleProtocolClientIface *iface)
    +facebook_protocol_class_finalize(G_GNUC_UNUSED FacebookProtocolClass *klass)
    +{
    +}
    +
    +static void
    +facebook_protocol_client_iface_init(PurpleProtocolClientInterface *iface)
    {
    iface->tooltip_text = fb_client_tooltip_text;
    iface->blist_node_menu = fb_client_blist_node_menu;
    @@ -1589,20 +1598,20 @@
    }
    static void
    -facebook_protocol_server_iface_init(PurpleProtocolServerIface *iface)
    +facebook_protocol_server_iface_init(PurpleProtocolServerInterface *iface)
    {
    iface->set_status = fb_server_set_status;
    }
    static void
    -facebook_protocol_im_iface_init(PurpleProtocolIMIface *iface)
    +facebook_protocol_im_iface_init(PurpleProtocolIMInterface *iface)
    {
    iface->send = fb_im_send;
    iface->send_typing = fb_im_send_typing;
    }
    static void
    -facebook_protocol_chat_iface_init(PurpleProtocolChatIface *iface)
    +facebook_protocol_chat_iface_init(PurpleProtocolChatInterface *iface)
    {
    iface->info = fb_chat_info;
    iface->info_defaults = fb_chat_info_defaults;
    @@ -1614,26 +1623,25 @@
    }
    static void
    -facebook_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *iface)
    +facebook_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *iface)
    {
    iface->get_list = fb_roomlist_get_list;
    iface->cancel = fb_roomlist_cancel;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - FacebookProtocol, facebook_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + FacebookProtocol, facebook_protocol, PURPLE_TYPE_PROTOCOL, 0,
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - facebook_protocol_client_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - facebook_protocol_server_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - facebook_protocol_im_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - facebook_protocol_chat_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
    - facebook_protocol_roomlist_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + facebook_protocol_client_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + facebook_protocol_server_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + facebook_protocol_im_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + facebook_protocol_chat_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
    + facebook_protocol_roomlist_iface_init));
    static void
    fb_cmds_register(void)
    @@ -1693,7 +1701,7 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - facebook_protocol_register_type(plugin);
    + facebook_protocol_register_type(G_TYPE_MODULE(plugin));
    fb_protocol = purple_protocols_add(FACEBOOK_TYPE_PROTOCOL, error);
    if (fb_protocol == NULL) {
    --- a/libpurple/protocols/facebook/facebook.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/facebook.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_H_
    -#define _FACEBOOK_H_
    +#ifndef PURPLE_FACEBOOK_FACEBOOK_H
    +#define PURPLE_FACEBOOK_FACEBOOK_H
    /**
    * SECTION:facebook
    @@ -81,4 +81,4 @@
    G_MODULE_EXPORT GType
    facebook_protocol_get_type(void);
    -#endif /* _FACEBOOK_H_ */
    +#endif /* PURPLE_FACEBOOK_FACEBOOK_H */
    --- a/libpurple/protocols/facebook/http.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/http.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_HTTP_H_
    -#define _FACEBOOK_HTTP_H_
    +#ifndef PURPLE_FACEBOOK_HTTP_H
    +#define PURPLE_FACEBOOK_HTTP_H
    /**
    * SECTION:http
    @@ -368,4 +368,4 @@
    gboolean
    fb_http_urlcmp(const gchar *url1, const gchar *url2, gboolean protocol);
    -#endif /* _FACEBOOK_HTTP_H_ */
    +#endif /* PURPLE_FACEBOOK_HTTP_H */
    --- a/libpurple/protocols/facebook/id.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/id.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_ID_H_
    -#define _FACEBOOK_ID_H_
    +#ifndef PURPLE_FACEBOOK_ID_H
    +#define PURPLE_FACEBOOK_ID_H
    /**
    * SECTION:id
    @@ -128,4 +128,4 @@
    */
    typedef gint64 FbId;
    -#endif /* _FACEBOOK_ID_H_ */
    +#endif /* PURPLE_FACEBOOK_ID_H */
    --- a/libpurple/protocols/facebook/json.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/json.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_JSON_H_
    -#define _FACEBOOK_JSON_H_
    +#ifndef PURPLE_FACEBOOK_JSON_H
    +#define PURPLE_FACEBOOK_JSON_H
    /**
    * SECTION:json
    @@ -482,4 +482,4 @@
    gchar *
    fb_json_values_next_str_dup(FbJsonValues *values, const gchar *defval);
    -#endif /* _FACEBOOK_JSON_H_ */
    +#endif /* PURPLE_FACEBOOK_JSON_H */
    --- a/libpurple/protocols/facebook/mqtt.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/mqtt.c Tue Oct 08 21:48:28 2019 -0500
    @@ -284,6 +284,19 @@
    g_error_free(err);
    }
    +static void
    +fb_mqtt_error_literal(FbMqtt *mqtt, FbMqttError error, const gchar *msg)
    +{
    + GError *err;
    +
    + g_return_if_fail(FB_IS_MQTT(mqtt));
    +
    + err = g_error_new_literal(FB_MQTT_ERROR, error, msg);
    +
    + g_signal_emit_by_name(mqtt, "error", err);
    + g_error_free(err);
    +}
    +
    void
    fb_mqtt_error(FbMqtt *mqtt, FbMqttError error, const gchar *format, ...)
    {
    @@ -307,7 +320,8 @@
    FbMqttPrivate *priv = mqtt->priv;
    priv->tev = 0;
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL, _("Connection timed out"));
    + fb_mqtt_error_literal(mqtt, FB_MQTT_ERROR_GENERAL,
    + _("Connection timed out"));
    return FALSE;
    }
    @@ -419,8 +433,8 @@
    msg = fb_mqtt_message_new_bytes(priv->rbuf);
    if (G_UNLIKELY(msg == NULL)) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to parse message"));
    + fb_mqtt_error_literal(mqtt, FB_MQTT_ERROR_GENERAL,
    + _("Failed to parse message"));
    return;
    }
    @@ -581,8 +595,8 @@
    }
    /* Since no case returned, there was a parse error. */
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to parse message"));
    + fb_mqtt_error_literal(mqtt, FB_MQTT_ERROR_GENERAL,
    + _("Failed to parse message"));
    }
    static void
    @@ -617,8 +631,8 @@
    bytes = fb_mqtt_message_bytes(msg);
    if (G_UNLIKELY(bytes == NULL)) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to format data"));
    + fb_mqtt_error_literal(mqtt, FB_MQTT_ERROR_GENERAL,
    + _("Failed to format data"));
    return;
    }
    @@ -730,8 +744,7 @@
    connected = (priv->conn != NULL) && priv->connected;
    if (!connected && error) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Not connected"));
    + fb_mqtt_error_literal(mqtt, FB_MQTT_ERROR_GENERAL, _("Not connected"));
    }
    return connected;
    --- a/libpurple/protocols/facebook/mqtt.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/mqtt.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_MQTT_H_
    -#define _FACEBOOK_MQTT_H_
    +#ifndef PURPLE_FACEBOOK_MQTT_H
    +#define PURPLE_FACEBOOK_MQTT_H
    /**
    * SECTION:mqtt
    @@ -541,4 +541,4 @@
    void
    fb_mqtt_message_write_str(FbMqttMessage *msg, const gchar *value);
    -#endif /* _FACEBOOK_MQTT_H_ */
    +#endif /* PURPLE_FACEBOOK_MQTT_H */
    --- a/libpurple/protocols/facebook/thrift.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/thrift.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_THRIFT_H_
    -#define _FACEBOOK_THRIFT_H_
    +#ifndef PURPLE_FACEBOOK_THRIFT_H
    +#define PURPLE_FACEBOOK_THRIFT_H
    /**
    * SECTION:thrift
    @@ -568,4 +568,4 @@
    FbThriftType
    fb_thrift_ct2t(guint8 type);
    -#endif /* _FACEBOOK_THRIFT_H_ */
    +#endif /* PURPLE_FACEBOOK_THRIFT_H */
    --- a/libpurple/protocols/facebook/util.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/facebook/util.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _FACEBOOK_UTIL_H_
    -#define _FACEBOOK_UTIL_H_
    +#ifndef PURPLE_FACEBOOK_UTIL_H
    +#define PURPLE_FACEBOOK_UTIL_H
    /**
    * SECTION:util
    @@ -347,4 +347,4 @@
    GByteArray *
    fb_util_zlib_inflate(const GByteArray *bytes, GError **error);
    -#endif /* _FACEBOOK_UTIL_H_ */
    +#endif /* PURPLE_FACEBOOK_UTIL_H */
    --- a/libpurple/protocols/gg/account.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/account.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_ACCOUNT_H
    -#define _GGP_ACCOUNT_H
    +#ifndef PURPLE_GG_ACCOUNT_H
    +#define PURPLE_GG_ACCOUNT_H
    #error "This file is outdated"
    @@ -59,4 +59,4 @@
    void ggp_account_chpass(PurpleConnection *gc);
    -#endif /* _GGP_ACCOUNT_H */
    +#endif /* PURPLE_GG_ACCOUNT_H */
    --- a/libpurple/protocols/gg/avatar.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/avatar.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_AVATAR_H
    -#define _GGP_AVATAR_H
    +#ifndef PURPLE_GG_AVATAR_H
    +#define PURPLE_GG_AVATAR_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -50,4 +50,4 @@
    void ggp_avatar_own_set(PurpleConnection *gc, PurpleImage *img);
    -#endif /* _GGP_AVATAR_H */
    +#endif /* PURPLE_GG_AVATAR_H */
    --- a/libpurple/protocols/gg/blist.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/blist.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,9 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -
    -#ifndef _PURPLE_GG_BLIST_H
    -#define _PURPLE_GG_BLIST_H
    +#ifndef PURPLE_GG_BLIST_H
    +#define PURPLE_GG_BLIST_H
    #include "connection.h"
    @@ -60,7 +59,6 @@
    */
    const char * ggp_buddylist_get_buddy_name(PurpleConnection *gc, const uin_t uin);
    -#endif /* _PURPLE_GG_BLIST_H */
    -
    +#endif /* PURPLE_GG_BLIST_H */
    /* vim: set ts=8 sts=0 sw=8 noet: */
    --- a/libpurple/protocols/gg/chat.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/chat.h Tue Oct 08 21:48:28 2019 -0500
    @@ -25,8 +25,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_CHAT_H
    -#define _GGP_CHAT_H
    +
    +#ifndef PURPLE_GG_CHAT_H
    +#define PURPLE_GG_CHAT_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -55,4 +56,4 @@
    PurpleRoomlist * ggp_chat_roomlist_get_list(PurpleConnection *gc);
    -#endif /* _GGP_CHAT_H */
    +#endif /* PURPLE_GG_CHAT_H */
    --- a/libpurple/protocols/gg/edisc.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/edisc.c Tue Oct 08 21:48:28 2019 -0500
    @@ -63,8 +63,10 @@
    GList *auth_pending;
    };
    -struct _ggp_edisc_xfer
    +struct _GGPXfer
    {
    + PurpleXfer parent;
    +
    gchar *filename;
    gchar *ticket_id;
    @@ -95,9 +97,7 @@
    static int ggp_edisc_parse_error(const gchar *data);
    /* General xfer functions. */
    -static void ggp_edisc_xfer_free(PurpleXfer *xfer);
    static void ggp_edisc_xfer_error(PurpleXfer *xfer, const gchar *msg);
    -static void ggp_edisc_xfer_cancel(PurpleXfer *xfer);
    static const gchar * ggp_edisc_xfer_ticket_url(const gchar *ticket_id);
    static void ggp_edisc_xfer_progress_watcher(PurpleHttpConnection *hc,
    gboolean reading_state, int processed, int total, gpointer _xfer);
    @@ -214,28 +214,6 @@
    * General xfer functions.
    ******************************************************************************/
    -static void ggp_edisc_xfer_free(PurpleXfer *xfer)
    -{
    - ggp_edisc_session_data *sdata;
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    -
    - if (edisc_xfer == NULL)
    - return;
    -
    - g_free(edisc_xfer->filename);
    - purple_http_conn_cancel(edisc_xfer->hc);
    -
    - sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    - g_return_if_fail(sdata != NULL);
    - if (edisc_xfer->ticket_id != NULL)
    - g_hash_table_remove(sdata->xfers_initialized,
    - edisc_xfer->ticket_id);
    -
    - g_free(edisc_xfer);
    - purple_xfer_set_protocol_data(xfer, NULL);
    -
    -}
    -
    static void ggp_edisc_xfer_error(PurpleXfer *xfer, const gchar *msg)
    {
    if (purple_xfer_is_cancelled(xfer))
    @@ -247,7 +225,6 @@
    purple_xfer_get_account(xfer),
    purple_xfer_get_remote_user(xfer),
    msg);
    - ggp_edisc_xfer_free(xfer);
    purple_xfer_end(xfer);
    }
    @@ -309,13 +286,6 @@
    }
    -static void ggp_edisc_xfer_cancel(PurpleXfer *xfer)
    -{
    - g_return_if_fail(xfer != NULL);
    -
    - ggp_edisc_xfer_free(xfer);
    -}
    -
    static const gchar * ggp_edisc_xfer_ticket_url(const gchar *ticket_id)
    {
    static gchar ticket_url[150];
    @@ -402,7 +372,7 @@
    static void ggp_edisc_xfer_send_init(PurpleXfer *xfer)
    {
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_NOT_STARTED);
    @@ -419,7 +389,7 @@
    ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(gc);
    PurpleHttpRequest *req;
    PurpleXfer *xfer = _xfer;
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    gchar *data;
    if (purple_xfer_is_cancelled(xfer))
    @@ -463,7 +433,7 @@
    ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(
    purple_http_conn_get_purple_connection(hc));
    PurpleXfer *xfer = _xfer;
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    const gchar *data = purple_http_response_get_data(response, NULL);
    ggp_edisc_xfer_ack_status ack_status;
    JsonParser *parser;
    @@ -525,7 +495,7 @@
    void ggp_edisc_xfer_send_ticket_changed(PurpleConnection *gc, PurpleXfer *xfer,
    gboolean is_allowed)
    {
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    if (!edisc_xfer) {
    purple_debug_fatal("gg", "ggp_edisc_event_ticket_changed: "
    "transfer %p already free'd\n", xfer);
    @@ -536,7 +506,6 @@
    purple_debug_info("gg", "ggp_edisc_event_ticket_changed: "
    "transfer %p rejected\n", xfer);
    purple_xfer_cancel_remote(xfer);
    - ggp_edisc_xfer_free(xfer);
    return;
    }
    @@ -555,12 +524,12 @@
    PurpleHttpContentReaderCb cb)
    {
    PurpleXfer *xfer = _xfer;
    - ggp_edisc_xfer *edisc_xfer;
    + GGPXfer *edisc_xfer;
    int stored;
    gboolean success, eof = FALSE;
    g_return_if_fail(xfer != NULL);
    - edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + edisc_xfer = GGP_XFER(xfer);
    g_return_if_fail(edisc_xfer != NULL);
    if (edisc_xfer->already_read != offset) {
    @@ -587,12 +556,12 @@
    static void ggp_edisc_xfer_send_start(PurpleXfer *xfer)
    {
    ggp_edisc_session_data *sdata;
    - ggp_edisc_xfer *edisc_xfer;
    + GGPXfer *edisc_xfer;
    gchar *upload_url, *filename_e;
    PurpleHttpRequest *req;
    g_return_if_fail(xfer != NULL);
    - edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + edisc_xfer = GGP_XFER(xfer);
    g_return_if_fail(edisc_xfer != NULL);
    sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    g_return_if_fail(sdata != NULL);
    @@ -631,7 +600,7 @@
    PurpleHttpResponse *response, gpointer _xfer)
    {
    PurpleXfer *xfer = _xfer;
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    const gchar *data = purple_http_response_get_data(response, NULL);
    JsonParser *parser;
    JsonObject *result;
    @@ -652,38 +621,37 @@
    parser = ggp_json_parse(data);
    result = json_node_get_object(json_parser_get_root(parser));
    result = json_object_get_object_member(result, "result");
    - if (json_object_has_member(result, "status"))
    + if (json_object_has_member(result, "status")) {
    result_status = json_object_get_int_member(result, "status");
    + }
    g_object_unref(parser);
    if (result_status == 0) {
    purple_xfer_set_completed(xfer, TRUE);
    purple_xfer_end(xfer);
    - ggp_edisc_xfer_free(xfer);
    - } else
    + } else {
    ggp_edisc_xfer_error(xfer, _("Error while sending a file"));
    + }
    }
    PurpleXfer * ggp_edisc_xfer_send_new(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
    {
    - PurpleXfer *xfer;
    - ggp_edisc_xfer *edisc_xfer;
    + GGPXfer *xfer;
    g_return_val_if_fail(gc != NULL, NULL);
    g_return_val_if_fail(who != NULL, NULL);
    - xfer = purple_xfer_new(purple_connection_get_account(gc),
    - PURPLE_XFER_TYPE_SEND, who);
    - edisc_xfer = g_new0(ggp_edisc_xfer, 1);
    - purple_xfer_set_protocol_data(xfer, edisc_xfer);
    + xfer = g_object_new(
    + GGP_TYPE_XFER,
    + "account", purple_connection_get_account(gc),
    + "type", PURPLE_XFER_TYPE_SEND,
    + "remote-user", who,
    + NULL
    + );
    - edisc_xfer->gc = gc;
    + xfer->gc = gc;
    - purple_xfer_set_init_fnc(xfer, ggp_edisc_xfer_send_init);
    - purple_xfer_set_start_fnc(xfer, ggp_edisc_xfer_send_start);
    - purple_xfer_set_cancel_send_fnc(xfer, ggp_edisc_xfer_cancel);
    -
    - return xfer;
    + return PURPLE_XFER(xfer);
    }
    void ggp_edisc_xfer_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who,
    @@ -776,7 +744,7 @@
    {
    PurpleConnection *gc = purple_http_conn_get_purple_connection(hc);
    PurpleXfer *xfer;
    - ggp_edisc_xfer *edisc_xfer;
    + GGPXfer *edisc_xfer;
    JsonParser *parser;
    JsonObject *result;
    int status = -1;
    @@ -862,7 +830,7 @@
    purple_xfer_set_filename(xfer, file_name);
    purple_xfer_set_size(xfer, file_size);
    purple_xfer_request(xfer);
    - edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + edisc_xfer = GGP_XFER(xfer);
    edisc_xfer->ticket_id = g_strdup(ticket_id);
    g_hash_table_insert(sdata->xfers_initialized,
    edisc_xfer->ticket_id, xfer);
    @@ -875,25 +843,22 @@
    static PurpleXfer * ggp_edisc_xfer_recv_new(PurpleConnection *gc,
    const char *who)
    {
    - PurpleXfer *xfer;
    - ggp_edisc_xfer *edisc_xfer;
    + GGPXfer *xfer;
    g_return_val_if_fail(gc != NULL, NULL);
    g_return_val_if_fail(who != NULL, NULL);
    - xfer = purple_xfer_new(purple_connection_get_account(gc),
    - PURPLE_XFER_TYPE_RECEIVE, who);
    - edisc_xfer = g_new0(ggp_edisc_xfer, 1);
    - purple_xfer_set_protocol_data(xfer, edisc_xfer);
    -
    - edisc_xfer->gc = gc;
    + xfer = g_object_new(
    + GGP_TYPE_XFER,
    + "account", purple_connection_get_account(gc),
    + "type", PURPLE_XFER_TYPE_RECEIVE,
    + "remote-user", who,
    + NULL
    + );
    - purple_xfer_set_init_fnc(xfer, ggp_edisc_xfer_recv_accept);
    - purple_xfer_set_request_denied_fnc(xfer, ggp_edisc_xfer_recv_reject);
    - purple_xfer_set_start_fnc(xfer, ggp_edisc_xfer_recv_start);
    - purple_xfer_set_cancel_recv_fnc(xfer, ggp_edisc_xfer_cancel);
    + xfer->gc = gc;
    - return xfer;
    + return PURPLE_XFER(xfer);
    }
    static void ggp_edisc_xfer_recv_reject(PurpleXfer *xfer)
    @@ -908,7 +873,7 @@
    static void ggp_edisc_xfer_recv_ack(PurpleXfer *xfer, gboolean accept)
    {
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    ggp_edisc_session_data *sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    PurpleHttpRequest *req;
    @@ -934,7 +899,6 @@
    if (!accept) {
    edisc_xfer->hc = NULL;
    - ggp_edisc_xfer_free(xfer);
    }
    }
    @@ -942,12 +906,12 @@
    PurpleHttpResponse *response, gpointer _xfer)
    {
    PurpleXfer *xfer = _xfer;
    - ggp_edisc_xfer *edisc_xfer;
    + GGPXfer *edisc_xfer;
    if (purple_xfer_is_cancelled(xfer))
    g_return_if_reached();
    - edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + edisc_xfer = GGP_XFER(xfer);
    edisc_xfer->hc = NULL;
    if (!purple_http_response_is_successful(response)) {
    @@ -961,7 +925,7 @@
    static void ggp_edisc_xfer_recv_ticket_completed(PurpleXfer *xfer)
    {
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    if (edisc_xfer->ready)
    return;
    @@ -973,12 +937,12 @@
    static void ggp_edisc_xfer_recv_start(PurpleXfer *xfer)
    {
    ggp_edisc_session_data *sdata;
    - ggp_edisc_xfer *edisc_xfer;
    + GGPXfer *edisc_xfer;
    gchar *upload_url;
    PurpleHttpRequest *req;
    g_return_if_fail(xfer != NULL);
    - edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + edisc_xfer = GGP_XFER(xfer);
    g_return_if_fail(edisc_xfer != NULL);
    sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    g_return_if_fail(sdata != NULL);
    @@ -1012,12 +976,9 @@
    size_t length, gpointer _xfer)
    {
    PurpleXfer *xfer = _xfer;
    - ggp_edisc_xfer *edisc_xfer;
    gssize stored;
    g_return_val_if_fail(xfer != NULL, FALSE);
    - edisc_xfer = purple_xfer_get_protocol_data(xfer);
    - g_return_val_if_fail(edisc_xfer != NULL, FALSE);
    stored = purple_xfer_write_file(xfer, (guchar *)buffer, length) ?
    (gssize)length : -1;
    @@ -1048,13 +1009,11 @@
    PurpleHttpResponse *response, gpointer _xfer)
    {
    PurpleXfer *xfer = _xfer;
    - ggp_edisc_xfer *edisc_xfer = purple_xfer_get_protocol_data(xfer);
    + GGPXfer *edisc_xfer = GGP_XFER(xfer);
    if (purple_xfer_is_cancelled(xfer))
    return;
    - g_return_if_fail(edisc_xfer != NULL);
    -
    edisc_xfer->hc = NULL;
    if (!purple_http_response_is_successful(response)) {
    @@ -1065,7 +1024,6 @@
    if (purple_xfer_get_bytes_remaining(xfer) == 0) {
    purple_xfer_set_completed(xfer, TRUE);
    purple_xfer_end(xfer);
    - ggp_edisc_xfer_free(xfer);
    } else {
    purple_debug_warning("gg", "ggp_edisc_xfer_recv_done: didn't "
    "received everything\n");
    @@ -1223,3 +1181,72 @@
    "security_token=%s\n", sdata->security_token);
    ggp_ggdrive_auth_results(gc, TRUE);
    }
    +
    +G_DEFINE_DYNAMIC_TYPE(GGPXfer, ggp_xfer, PURPLE_TYPE_XFER);
    +
    +static void
    +ggp_xfer_init_xfer(PurpleXfer *xfer) {
    + PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    +
    + if(type == PURPLE_XFER_TYPE_SEND) {
    + ggp_edisc_xfer_send_init(xfer);
    + } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    + ggp_edisc_xfer_recv_accept(xfer);
    + }
    +}
    +
    +static void
    +ggp_xfer_start(PurpleXfer *xfer) {
    + PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    +
    + if(type == PURPLE_XFER_TYPE_SEND) {
    + ggp_edisc_xfer_send_start(xfer);
    + } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    + ggp_edisc_xfer_recv_start(xfer);
    + }
    +}
    +
    +static void
    +ggp_xfer_init(GGPXfer *xfer) {
    +
    +}
    +
    +static void
    +ggp_xfer_finalize(GObject *obj) {
    + GGPXfer *edisc_xfer = GGP_XFER(obj);
    + ggp_edisc_session_data *sdata;
    +
    + g_free(edisc_xfer->filename);
    + purple_http_conn_cancel(edisc_xfer->hc);
    +
    + sdata = ggp_edisc_get_sdata(edisc_xfer->gc);
    +
    + if (edisc_xfer->ticket_id != NULL) {
    + g_hash_table_remove(sdata->xfers_initialized,
    + edisc_xfer->ticket_id);
    + }
    +
    + G_OBJECT_CLASS(ggp_xfer_parent_class)->finalize(obj);
    +}
    +
    +static void
    +ggp_xfer_class_finalize(GGPXferClass *klass) {
    +
    +}
    +
    +static void
    +ggp_xfer_class_init(GGPXferClass *klass) {
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    +
    + obj_class->finalize = ggp_xfer_finalize;
    +
    + xfer_class->init = ggp_xfer_init_xfer;
    + xfer_class->start = ggp_xfer_start;
    + xfer_class->request_denied = ggp_edisc_xfer_recv_reject;
    +}
    +
    +void
    +ggp_xfer_register(GTypeModule *module) {
    + ggp_xfer_register_type(module);
    +}
    --- a/libpurple/protocols/gg/edisc.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/edisc.h Tue Oct 08 21:48:28 2019 -0500
    @@ -25,13 +25,21 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_EDISC_H
    -#define _GGP_EDISC_H
    +
    +#ifndef PURPLE_GG_EDISC_H
    +#define PURPLE_GG_EDISC_H
    #include <internal.h>
    typedef struct _ggp_edisc_session_data ggp_edisc_session_data;
    +G_BEGIN_DECLS
    +
    +#define GGP_TYPE_XFER (ggp_xfer_get_type())
    +G_DECLARE_FINAL_TYPE(GGPXfer, ggp_xfer, GGP, XFER, PurpleXfer);
    +
    +void ggp_xfer_register(GTypeModule *module);
    +
    /* Setting up. */
    void ggp_edisc_setup(PurpleConnection *gc);
    void ggp_edisc_cleanup(PurpleConnection *gc);
    @@ -46,4 +54,6 @@
    const char *filename);
    PurpleXfer * ggp_edisc_xfer_send_new(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    -#endif /* _GGP_EDISC_H */
    +G_END_DECLS
    +
    +#endif /* PURPLE_GG_EDISC_H */
    --- a/libpurple/protocols/gg/gg.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/gg.c Tue Oct 08 21:48:28 2019 -0500
    @@ -594,9 +594,9 @@
    }
    static gint
    -gg_uri_handler_find_account(gconstpointer a, gconstpointer b)
    +gg_uri_handler_find_account(PurpleAccount *account,
    + G_GNUC_UNUSED gconstpointer data)
    {
    - PurpleAccount *account = PURPLE_ACCOUNT(a);
    const gchar *protocol_id;
    protocol_id = purple_account_get_protocol_id(account);
    @@ -630,8 +630,8 @@
    /* Find online Gadu-Gadu account */
    accounts = purple_accounts_get_all();
    - account_node = g_list_find_custom(accounts, NULL,
    - gg_uri_handler_find_account);
    + account_node = g_list_find_custom(
    + accounts, NULL, (GCompareFunc)gg_uri_handler_find_account);
    if (account_node == NULL) {
    return FALSE;
    @@ -1000,8 +1000,9 @@
    }
    static void
    -ggp_protocol_init(PurpleProtocol *protocol)
    +ggp_protocol_init(GGPProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountOption *option;
    GList *encryption_options = NULL;
    GList *protocol_version = NULL;
    @@ -1052,16 +1053,23 @@
    }
    static void
    -ggp_protocol_class_init(PurpleProtocolClass *klass)
    +ggp_protocol_class_init(GGPProtocolClass *klass)
    {
    - klass->login = ggp_login;
    - klass->close = ggp_close;
    - klass->status_types = ggp_status_types;
    - klass->list_icon = ggp_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = ggp_login;
    + protocol_class->close = ggp_close;
    + protocol_class->status_types = ggp_status_types;
    + protocol_class->list_icon = ggp_list_icon;
    }
    static void
    -ggp_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +ggp_protocol_class_finalize(G_GNUC_UNUSED GGPProtocolClass *klass)
    +{
    +}
    +
    +static void
    +ggp_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = ggp_get_actions;
    client_iface->list_emblem = ggp_list_emblem;
    @@ -1075,7 +1083,7 @@
    }
    static void
    -ggp_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +ggp_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->get_info = ggp_pubdir_get_info_protocol;
    server_iface->set_status = ggp_status_set_purplestatus;
    @@ -1089,14 +1097,14 @@
    }
    static void
    -ggp_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +ggp_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = ggp_message_send_im;
    im_iface->send_typing = ggp_send_typing;
    }
    static void
    -ggp_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +ggp_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = ggp_chat_info;
    chat_iface->info_defaults = ggp_chat_info_defaults;
    @@ -1110,13 +1118,13 @@
    }
    static void
    -ggp_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface)
    +ggp_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface)
    {
    roomlist_iface->get_list = ggp_chat_roomlist_get_list;
    }
    static void
    -ggp_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    +ggp_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    {
    privacy_iface->add_deny = ggp_add_deny;
    privacy_iface->rem_deny = ggp_rem_deny;
    @@ -1130,30 +1138,29 @@
    xfer_iface->new_xfer = ggp_edisc_xfer_send_new;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - GGPProtocol, ggp_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + GGPProtocol, ggp_protocol, PURPLE_TYPE_PROTOCOL, 0,
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - ggp_protocol_client_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + ggp_protocol_client_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - ggp_protocol_server_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + ggp_protocol_server_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - ggp_protocol_im_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + ggp_protocol_im_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - ggp_protocol_chat_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + ggp_protocol_chat_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
    - ggp_protocol_roomlist_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
    + ggp_protocol_roomlist_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - ggp_protocol_privacy_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    + ggp_protocol_privacy_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
    - ggp_protocol_xfer_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    + ggp_protocol_xfer_iface_init));
    static gchar *
    plugin_extra(PurplePlugin *plugin)
    @@ -1189,7 +1196,9 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - ggp_protocol_register_type(plugin);
    + ggp_protocol_register_type(G_TYPE_MODULE(plugin));
    +
    + ggp_xfer_register(G_TYPE_MODULE(plugin));
    my_protocol = purple_protocols_add(GGP_TYPE_PROTOCOL, error);
    if (!my_protocol)
    --- a/libpurple/protocols/gg/gg.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/gg.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,9 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -
    -#ifndef _PURPLE_GG_H
    -#define _PURPLE_GG_H
    +#ifndef PURPLE_GG_GG_H
    +#define PURPLE_GG_GG_H
    #define GGP_UIN_LEN_MAX 10
    @@ -92,4 +91,4 @@
    void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond);
    -#endif /* _PURPLE_GG_H */
    +#endif /* PURPLE_GG_GG_H */
    --- a/libpurple/protocols/gg/html.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/html.h Tue Oct 08 21:48:28 2019 -0500
    @@ -25,8 +25,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_HTML_H
    -#define _GGP_HTML_H
    +
    +#ifndef PURPLE_GG_HTML_H
    +#define PURPLE_GG_HTML_H
    #include <internal.h>
    @@ -55,5 +56,4 @@
    int ggp_html_decode_color(const gchar *str);
    ggp_html_tag ggp_html_parse_tag(const gchar *tag_str);
    -
    -#endif /* _GGP_HTML_H */
    +#endif /* PURPLE_GG_HTML_H */
    --- a/libpurple/protocols/gg/image-prpl.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/image-prpl.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_IMAGE_H
    -#define _GGP_IMAGE_H
    +#ifndef PURPLE_GG_IMAGE_PRPL_H
    +#define PURPLE_GG_IMAGE_PRPL_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -65,4 +65,4 @@
    PurpleImage *
    ggp_image_request(PurpleConnection *gc, uin_t uin, uint64_t id);
    -#endif /* _GGP_IMAGE_H */
    +#endif /* PURPLE_GG_IMAGE_PRPL_H */
    --- a/libpurple/protocols/gg/keymapper.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/keymapper.h Tue Oct 08 21:48:28 2019 -0500
    @@ -25,8 +25,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_KEYMAPPER_H
    -#define _GGP_KEYMAPPER_H
    +
    +#ifndef PURPLE_GG_KEYMAPPER_H
    +#define PURPLE_GG_KEYMAPPER_H
    typedef struct _ggp_keymapper ggp_keymapper;
    @@ -45,4 +46,4 @@
    guint64
    ggp_keymapper_from_key(ggp_keymapper *km, gpointer key);
    -#endif /* _GGP_KEYMAPPER_H */
    +#endif /* PURPLE_GG_KEYMAPPER_H */
    --- a/libpurple/protocols/gg/libgadu-events.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/libgadu-events.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_LIBGADU_EVENTS_H
    -#define _GGP_LIBGADU_EVENTS_H
    +#ifndef PURPLE_GG_LIBGADU_EVENTS_H
    +#define PURPLE_GG_LIBGADU_EVENTS_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -40,4 +40,4 @@
    void ggp_events_json(PurpleConnection *gc, struct gg_event_json_event *ev);
    -#endif /* _GGP_LIBGADU_EVENTS_H */
    +#endif /* PURPLE_GG_LIBGADU_EVENTS_H */
    --- a/libpurple/protocols/gg/libgaduw.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/libgaduw.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_LIBGADUW_H
    -#define _GGP_LIBGADUW_H
    +#ifndef PURPLE_GG_LIBGADUW_H
    +#define PURPLE_GG_LIBGADUW_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -59,4 +59,4 @@
    gboolean show_processing);
    void ggp_libgaduw_http_cancel(ggp_libgaduw_http_req *req);
    -#endif /* _GGP_LIBGADUW_H */
    +#endif /* PURPLE_GG_LIBGADUW_H */
    --- a/libpurple/protocols/gg/message-prpl.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/message-prpl.h Tue Oct 08 21:48:28 2019 -0500
    @@ -25,8 +25,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_MESSAGE_PRPL_H
    -#define _GGP_MESSAGE_PRPL_H
    +
    +#ifndef PURPLE_GG_MESSAGE_PRPL_H
    +#define PURPLE_GG_MESSAGE_PRPL_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -45,4 +46,4 @@
    int ggp_message_send_im(PurpleConnection *gc, PurpleMessage *msg);
    gchar * ggp_message_format_to_gg(PurpleConversation *conv, const gchar *text);
    -#endif /* _GGP_MESSAGE_PRPL_H */
    +#endif /* PURPLE_GG_MESSAGE_PRPL_H */
    --- a/libpurple/protocols/gg/multilogon.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/multilogon.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_MULTILOGON_H
    -#define _GGP_MULTILOGON_H
    +#ifndef PURPLE_GG_MULTILOGON_H
    +#define PURPLE_GG_MULTILOGON_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -43,4 +43,4 @@
    void ggp_multilogon_dialog(PurpleConnection *gc);
    -#endif /* _GGP_MULTILOGON_H */
    +#endif /* PURPLE_GG_MULTILOGON_H */
    --- a/libpurple/protocols/gg/oauth/oauth-parameter.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/oauth/oauth-parameter.c Tue Oct 08 21:48:28 2019 -0500
    @@ -51,15 +51,15 @@
    new_value = g_strdup(value);
    if (new_value == NULL) {
    - free(new_key);
    + g_free(new_key);
    return -1;
    }
    - new_p = malloc(sizeof(gg_oauth_parameter_t));
    + new_p = g_new0(gg_oauth_parameter_t, 1);
    if (new_p == NULL) {
    - free(new_key);
    - free(new_value);
    + g_free(new_key);
    + g_free(new_value);
    return -1;
    }
    @@ -150,9 +150,9 @@
    next = list->next;
    - free(list->key);
    - free(list->value);
    - free(list);
    + g_free(list->key);
    + g_free(list->value);
    + g_free(list);
    list = next;
    }
    --- a/libpurple/protocols/gg/oauth/oauth-purple.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/oauth/oauth-purple.c Tue Oct 08 21:48:28 2019 -0500
    @@ -79,8 +79,6 @@
    const char *url = "http://api.gadu-gadu.pl/request_token";
    ggp_oauth_data *data;
    - g_return_if_fail((method == NULL) == (url == NULL));
    -
    purple_debug_misc("gg", "ggp_oauth_request: requesting token...\n");
    auth = gg_oauth_generate_header(method, url,
    --- a/libpurple/protocols/gg/oauth/oauth.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/oauth/oauth.c Tue Oct 08 21:48:28 2019 -0500
    @@ -82,8 +82,8 @@
    res = gg_hmac_sha1(key, text);
    - free(key);
    - free(text);
    + g_free(key);
    + g_free(text);
    return res;
    }
    --- a/libpurple/protocols/gg/pubdir-prpl.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/pubdir-prpl.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_PUBDIR_PROTOCOL_H
    -#define _GGP_PUBDIR_PROTOCOL_H
    +#ifndef PURPLE_GG_PUBDIR_PRPL_H
    +#define PURPLE_GG_PUBDIR_PRPL_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -70,4 +70,4 @@
    void ggp_pubdir_set_info(PurpleConnection *gc);
    -#endif /* _GGP_PUBDIR_PROTOCOL_H */
    +#endif /* PURPLE_GG_PUBDIR_PRPL_H */
    --- a/libpurple/protocols/gg/purplew.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/purplew.c Tue Oct 08 21:48:28 2019 -0500
    @@ -127,7 +127,8 @@
    {
    PurpleBlistNode *bnode;
    GList *groups = NULL;
    - for (bnode = purple_blist_get_root(); bnode; bnode = bnode->next) {
    + for (bnode = purple_blist_get_default_root(); bnode;
    + bnode = bnode->next) {
    PurpleGroup *group;
    GSList *accounts;
    gboolean have_specified = FALSE, have_others = FALSE;
    --- a/libpurple/protocols/gg/purplew.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/purplew.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_PURPLEW_H
    -#define _GGP_PURPLEW_H
    +#ifndef PURPLE_GG_PURPLEW_H
    +#define PURPLE_GG_PURPLEW_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -73,4 +73,4 @@
    /* you must g_free returned list */
    GList * ggp_purplew_account_get_groups(PurpleAccount *account, gboolean exclusive);
    -#endif /* _GGP_PURPLEW_H */
    +#endif /* PURPLE_GG_PURPLEW_H */
    --- a/libpurple/protocols/gg/resolver-purple.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/resolver-purple.c Tue Oct 08 21:48:28 2019 -0500
    @@ -142,7 +142,7 @@
    purple_debug_misc("gg", "ggp_resolver_purple_start(%p, %p, \"%s\")\n",
    fd, private_data, hostname);
    - data = malloc(sizeof(ggp_resolver_purple_data));
    + data = g_new0(ggp_resolver_purple_data, 1);
    *private_data = (void*)data;
    data->cancellable = NULL;
    data->pipes[0] = 0;
    @@ -201,5 +201,5 @@
    if (data->pipes[1])
    close(data->pipes[1]);
    - free(data);
    + g_free(data);
    }
    --- a/libpurple/protocols/gg/resolver-purple.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/resolver-purple.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,12 +27,12 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_RESOLVER_PURPLE_H
    -#define _GGP_RESOLVER_PURPLE_H
    +#ifndef PURPLE_GG_RESOLVER_PURPLE_H
    +#define PURPLE_GG_RESOLVER_PURPLE_H
    /**
    * Registers custom resolver for libgadu, that uses libpurple for DNS queries.
    */
    void ggp_resolver_purple_setup(void);
    -#endif /* _GGP_RESOLVER_PURPLE_H */
    +#endif /* PURPLE_GG_RESOLVER_PURPLE_H */
    --- a/libpurple/protocols/gg/roster.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/roster.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_ROSTER_H
    -#define _GGP_ROSTER_H
    +#ifndef PURPLE_GG_ROSTER_H
    +#define PURPLE_GG_ROSTER_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -70,4 +70,4 @@
    void ggp_roster_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
    PurpleGroup *group);
    -#endif /* _GGP_ROSTER_H */
    +#endif /* PURPLE_GG_ROSTER_H */
    --- a/libpurple/protocols/gg/servconn.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/servconn.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_SERVCONN_H
    -#define _GGP_SERVCONN_H
    +#ifndef PURPLE_GG_SERVCONN_H
    +#define PURPLE_GG_SERVCONN_H
    #include <internal.h>
    #include <accountopt.h>
    @@ -40,4 +40,4 @@
    GSList * ggp_servconn_get_servers(void);
    void ggp_servconn_remote_disconnect(PurpleConnection *gc);
    -#endif /* _GGP_SERVCONN_H */
    +#endif /* PURPLE_GG_SERVCONN_H */
    --- a/libpurple/protocols/gg/status.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/status.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_STATUS_H
    -#define _GGP_STATUS_H
    +#ifndef PURPLE_GG_STATUS_H
    +#define PURPLE_GG_STATUS_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -62,4 +62,4 @@
    void ggp_status_got_others(PurpleConnection *gc, struct gg_event *ev);
    char * ggp_status_buddy_text(PurpleBuddy *buddy);
    -#endif /* _GGP_STATUS_H */
    +#endif /* PURPLE_GG_STATUS_H */
    --- a/libpurple/protocols/gg/tcpsocket.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/tcpsocket.h Tue Oct 08 21:48:28 2019 -0500
    @@ -25,8 +25,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_TCPSOCKET_H
    -#define _GGP_TCPSOCKET_H
    +
    +#ifndef PURPLE_GG_TCPSOCKET_H
    +#define PURPLE_GG_TCPSOCKET_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -37,4 +38,4 @@
    PurpleInputCondition
    ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check);
    -#endif /* _GGP_TCPSOCKET_H */
    +#endif /* PURPLE_GG_TCPSOCKET_H */
    --- a/libpurple/protocols/gg/utils.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/utils.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_UTILS_H
    -#define _GGP_UTILS_H
    +#ifndef PURPLE_GG_UTILS_H
    +#define PURPLE_GG_UTILS_H
    #include <internal.h>
    #include <libgadu.h>
    @@ -108,4 +108,4 @@
    JsonParser * ggp_json_parse(const gchar *data);
    -#endif /* _GGP_UTILS_H */
    +#endif /* PURPLE_GG_UTILS_H */
    --- a/libpurple/protocols/gg/validator.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/validator.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_VALIDATOR_H
    -#define _GGP_VALIDATOR_H
    +#ifndef PURPLE_GG_VALIDATOR_H
    +#define PURPLE_GG_VALIDATOR_H
    #include <internal.h>
    #include <request.h>
    @@ -39,4 +39,4 @@
    gboolean ggp_validator_password_equal(PurpleRequestField *field, gchar **errmsg,
    void *field2);
    -#endif /* _GGP_VALIDATOR_H */
    +#endif /* PURPLE_GG_VALIDATOR_H */
    --- a/libpurple/protocols/gg/xml.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/gg/xml.h Tue Oct 08 21:48:28 2019 -0500
    @@ -27,8 +27,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _GGP_XML_H
    -#define _GGP_XML_H
    +#ifndef PURPLE_GG_XML_H
    +#define PURPLE_GG_XML_H
    #include <internal.h>
    #include <xmlnode.h>
    @@ -45,4 +45,4 @@
    unsigned int ggp_xml_child_count(PurpleXmlNode *xml, const gchar *childName);
    -#endif /* _GGP_XML_H */
    +#endif /* PURPLE_GG_XML_H */
    --- a/libpurple/protocols/irc/cmds.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/irc/cmds.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file cmds.c
    - *
    * purple
    *
    * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
    @@ -21,11 +19,7 @@
    */
    #include "internal.h"
    -
    -#include "conversation.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "util.h"
    +#include <purple.h>
    #include "irc.h"
    --- a/libpurple/protocols/irc/dcc_send.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/irc/dcc_send.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file dcc_send.c Functions used in sending files with DCC SEND
    - *
    * purple
    *
    * Copyright (C) 2004, Timothy T Ringenbach <omarvo@hotmail.com>
    @@ -22,29 +20,31 @@
    */
    #include "internal.h"
    +#include <purple.h>
    +
    #include "irc.h"
    -#include "debug.h"
    -#include "xfer.h"
    -#include "notify.h"
    -#include "network.h"
    +
    +struct _IrcXfer {
    + PurpleXfer parent;
    +
    + /* receive properties */
    + gchar *ip;
    + guint remote_port;
    +
    + /* send properties */
    + PurpleNetworkListenData *listen_data;
    + gint inpa;
    + gint fd;
    + guchar *rxqueue;
    + guint rxlen;
    +};
    +
    +G_DEFINE_DYNAMIC_TYPE(IrcXfer, irc_xfer, PURPLE_TYPE_XFER);
    /***************************************************************************
    * Functions related to receiving files via DCC SEND
    ***************************************************************************/
    -struct irc_xfer_rx_data {
    - gchar *ip;
    - unsigned int remote_port;
    -};
    -
    -static void irc_dccsend_recv_destroy(PurpleXfer *xfer)
    -{
    - struct irc_xfer_rx_data *xd = purple_xfer_get_protocol_data(xfer);
    -
    - g_free(xd->ip);
    - g_free(xd);
    -}
    -
    /*
    * This function is called whenever data is received.
    * It sends the acknowledgement (in the form of a total byte count as an
    @@ -54,6 +54,10 @@
    guint32 l;
    gssize result;
    + if(purple_xfer_get_xfer_type(xfer) != PURPLE_XFER_TYPE_RECEIVE) {
    + return;
    + }
    +
    l = htonl(purple_xfer_get_bytes_sent(xfer));
    result = purple_xfer_write(xfer, (guchar *)&l, sizeof(l));
    if (result != sizeof(l)) {
    @@ -63,17 +67,14 @@
    }
    static void irc_dccsend_recv_init(PurpleXfer *xfer) {
    - struct irc_xfer_rx_data *xd = purple_xfer_get_protocol_data(xfer);
    + IrcXfer *xd = IRC_XFER(xfer);
    purple_xfer_start(xfer, -1, xd->ip, xd->remote_port);
    - g_free(xd->ip);
    - xd->ip = NULL;
    }
    /* This function makes the necessary arrangements for receiving files */
    void irc_dccsend_recv(struct irc_conn *irc, const char *from, const char *msg) {
    - PurpleXfer *xfer;
    - struct irc_xfer_rx_data *xd;
    + IrcXfer *xfer;
    gchar **token;
    struct in_addr addr;
    GString *filename;
    @@ -111,35 +112,32 @@
    }
    i++;
    - xfer = purple_xfer_new(irc->account, PURPLE_XFER_TYPE_RECEIVE, from);
    - if (xfer)
    - {
    - xd = g_new0(struct irc_xfer_rx_data, 1);
    - purple_xfer_set_protocol_data(xfer, xd);
    + xfer = g_object_new(
    + IRC_TYPE_XFER,
    + "account", irc->account,
    + "type", PURPLE_XFER_TYPE_RECEIVE,
    + "remote-user", from,
    + NULL
    + );
    - purple_xfer_set_filename(xfer, filename->str);
    - xd->remote_port = atoi(token[i+1]);
    + purple_xfer_set_filename(PURPLE_XFER(xfer), filename->str);
    +
    + xfer->remote_port = atoi(token[i+1]);
    - nip = strtoul(token[i], NULL, 10);
    - if (nip) {
    - addr.s_addr = htonl(nip);
    - xd->ip = g_strdup(inet_ntoa(addr));
    - } else {
    - xd->ip = g_strdup(token[i]);
    - }
    - purple_debug(PURPLE_DEBUG_INFO, "irc", "Receiving file (%s) from %s\n",
    - filename->str, xd->ip);
    - purple_xfer_set_size(xfer, token[i+2] ? atoi(token[i+2]) : 0);
    + nip = strtoul(token[i], NULL, 10);
    + if (nip) {
    + addr.s_addr = htonl(nip);
    + xfer->ip = g_strdup(inet_ntoa(addr));
    + } else {
    + xfer->ip = g_strdup(token[i]);
    + }
    - purple_xfer_set_init_fnc(xfer, irc_dccsend_recv_init);
    - purple_xfer_set_ack_fnc(xfer, irc_dccsend_recv_ack);
    + purple_debug(PURPLE_DEBUG_INFO, "irc", "Receiving file (%s) from %s\n",
    + filename->str, xfer->ip);
    + purple_xfer_set_size(PURPLE_XFER(xfer), token[i+2] ? atoi(token[i+2]) : 0);
    - purple_xfer_set_end_fnc(xfer, irc_dccsend_recv_destroy);
    - purple_xfer_set_request_denied_fnc(xfer, irc_dccsend_recv_destroy);
    - purple_xfer_set_cancel_recv_fnc(xfer, irc_dccsend_recv_destroy);
    + purple_xfer_request(PURPLE_XFER(xfer));
    - purple_xfer_request(xfer);
    - }
    g_strfreev(token);
    g_string_free(filename, TRUE);
    }
    @@ -148,38 +146,11 @@
    * Functions related to sending files via DCC SEND
    *******************************************************************/
    -struct irc_xfer_send_data {
    - PurpleNetworkListenData *listen_data;
    - gint inpa;
    - int fd;
    - guchar *rxqueue;
    - guint rxlen;
    -};
    -
    -static void irc_dccsend_send_destroy(PurpleXfer *xfer)
    -{
    - struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
    -
    - if (xd == NULL)
    - return;
    -
    - if (xd->listen_data != NULL)
    - purple_network_listen_cancel(xd->listen_data);
    - if (xd->inpa > 0)
    - purple_input_remove(xd->inpa);
    - if (xd->fd != -1)
    - close(xd->fd);
    -
    - g_free(xd->rxqueue);
    -
    - g_free(xd);
    -}
    -
    -/* just in case you were wondering, this is why DCC is gay */
    +/* just in case you were wondering, this is why DCC is crappy */
    static void irc_dccsend_send_read(gpointer data, int source, PurpleInputCondition cond)
    {
    - PurpleXfer *xfer = data;
    - struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
    + PurpleXfer *xfer = PURPLE_XFER(data);
    + IrcXfer *xd = IRC_XFER(xfer);
    char buffer[64];
    int len;
    @@ -228,26 +199,28 @@
    }
    }
    -static gssize irc_dccsend_send_write(const guchar *buffer, size_t size, PurpleXfer *xfer)
    +static gssize irc_dccsend_send_write(PurpleXfer *xfer, const guchar *buffer, size_t size)
    {
    gssize s;
    gssize ret;
    s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)size);
    - if (!s)
    + if (!s) {
    return 0;
    + }
    - ret = purple_xfer_write(xfer, buffer, s);
    + ret = PURPLE_XFER_CLASS(irc_xfer_parent_class)->write(xfer, buffer, s);
    - if (ret < 0 && errno == EAGAIN)
    + if (ret < 0 && errno == EAGAIN) {
    ret = 0;
    + }
    return ret;
    }
    static void irc_dccsend_send_connected(gpointer data, int source, PurpleInputCondition cond) {
    - PurpleXfer *xfer = (PurpleXfer *) data;
    - struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
    + PurpleXfer *xfer = PURPLE_XFER(data);
    + IrcXfer *xd = IRC_XFER(xfer);
    int conn;
    conn = accept(xd->fd, NULL, 0);
    @@ -275,8 +248,8 @@
    static void
    irc_dccsend_network_listen_cb(int sock, gpointer data)
    {
    - PurpleXfer *xfer = data;
    - struct irc_xfer_send_data *xd;
    + PurpleXfer *xfer = PURPLE_XFER(data);
    + IrcXfer *xd = IRC_XFER(xfer);
    PurpleConnection *gc;
    struct irc_conn *irc;
    GSocket *gsock;
    @@ -286,7 +259,7 @@
    struct in_addr addr;
    unsigned short int port;
    - xd = purple_xfer_get_protocol_data(xfer);
    + /* not sure what the deal is here, but it needs to be here.. gk 20190626 */
    xd->listen_data = NULL;
    if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL
    @@ -295,12 +268,9 @@
    return;
    }
    - xd = purple_xfer_get_protocol_data(xfer);
    gc = purple_account_get_connection(purple_xfer_get_account(xfer));
    irc = purple_connection_get_protocol_data(gc);
    - g_object_unref(xfer);
    -
    if (sock < 0) {
    purple_notify_error(gc, NULL, _("File Transfer Failed"),
    _("Unable to open a listening port."),
    @@ -314,8 +284,10 @@
    port = purple_network_get_port_from_fd(sock);
    purple_debug_misc("irc", "port is %hu\n", port);
    /* Monitor the listening socket */
    - purple_xfer_set_watcher(xfer, purple_input_add(sock, PURPLE_INPUT_READ,
    - irc_dccsend_send_connected, xfer));
    + purple_xfer_set_watcher(
    + xfer,
    + purple_input_add(sock, PURPLE_INPUT_READ, irc_dccsend_send_connected, xfer)
    + );
    /* Send the intended recipient the DCC request */
    arg[0] = purple_xfer_get_remote_user(xfer);
    @@ -327,9 +299,13 @@
    }
    inet_aton(purple_network_get_my_ip(fd), &addr);
    - arg[1] = tmp = g_strdup_printf("\001DCC SEND \"%s\" %u %hu %" G_GOFFSET_FORMAT "\001",
    - purple_xfer_get_filename(xfer), ntohl(addr.s_addr),
    - port, purple_xfer_get_size(xfer));
    + arg[1] = tmp = g_strdup_printf(
    + "\001DCC SEND \"%s\" %u %hu %" G_GOFFSET_FORMAT "\001",
    + purple_xfer_get_filename(xfer),
    + ntohl(addr.s_addr),
    + port,
    + purple_xfer_get_size(xfer)
    + );
    irc_cmd_privmsg(purple_connection_get_protocol_data(gc), "msg", NULL, arg);
    g_free(tmp);
    @@ -339,47 +315,37 @@
    * This function is called after the user has selected a file to send.
    */
    static void irc_dccsend_send_init(PurpleXfer *xfer) {
    + IrcXfer *xd = IRC_XFER(xfer);
    PurpleConnection *gc = purple_account_get_connection(purple_xfer_get_account(xfer));
    - struct irc_xfer_send_data *xd = purple_xfer_get_protocol_data(xfer);
    purple_xfer_set_filename(xfer, g_path_get_basename(purple_xfer_get_local_filename(xfer)));
    - g_object_ref(xfer);
    -
    /* Create a listening socket */
    - xd->listen_data = purple_network_listen_range(0, 0, AF_UNSPEC, SOCK_STREAM, TRUE,
    - irc_dccsend_network_listen_cb, xfer);
    + xd->listen_data = purple_network_listen_range(
    + 0,
    + 0,
    + AF_UNSPEC,
    + SOCK_STREAM,
    + TRUE,
    + irc_dccsend_network_listen_cb,
    + xfer
    + );
    if (xd->listen_data == NULL) {
    - g_object_unref(xfer);
    purple_notify_error(gc, NULL, _("File Transfer Failed"),
    _("Unable to open a listening port."),
    purple_request_cpar_from_connection(gc));
    purple_xfer_cancel_local(xfer);
    }
    -
    }
    PurpleXfer *irc_dccsend_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who) {
    - PurpleXfer *xfer;
    - struct irc_xfer_send_data *xd;
    -
    - /* Build the file transfer handle */
    - xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_TYPE_SEND, who);
    - if (xfer)
    - {
    - xd = g_new0(struct irc_xfer_send_data, 1);
    - xd->fd = -1;
    - purple_xfer_set_protocol_data(xfer, xd);
    -
    - /* Setup our I/O op functions */
    - purple_xfer_set_init_fnc(xfer, irc_dccsend_send_init);
    - purple_xfer_set_write_fnc(xfer, irc_dccsend_send_write);
    - purple_xfer_set_end_fnc(xfer, irc_dccsend_send_destroy);
    - purple_xfer_set_request_denied_fnc(xfer, irc_dccsend_send_destroy);
    - purple_xfer_set_cancel_send_fnc(xfer, irc_dccsend_send_destroy);
    - }
    -
    - return xfer;
    + return g_object_new(
    + IRC_TYPE_XFER,
    + "account", purple_connection_get_account(gc),
    + "type", PURPLE_XFER_TYPE_SEND,
    + "remote-user", who,
    + NULL
    + );
    }
    /**
    @@ -396,3 +362,67 @@
    else
    purple_xfer_request(xfer);
    }
    +
    +/******************************************************************************
    + * PurpleXfer Implementation
    + *****************************************************************************/
    +static void
    +irc_dccsend_init(PurpleXfer *xfer) {
    + PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    +
    + if(type == PURPLE_XFER_TYPE_SEND) {
    + irc_dccsend_send_init(xfer);
    + } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    + irc_dccsend_recv_init(xfer);
    + }
    +}
    +
    +/******************************************************************************
    + * GObject Implementation
    + *****************************************************************************/
    +static void
    +irc_xfer_init(IrcXfer *xfer) {
    + xfer->fd = -1;
    +}
    +
    +static void
    +irc_xfer_finalize(GObject *obj) {
    + IrcXfer *xfer = IRC_XFER(obj);
    +
    + /* clean up the receiving proprties */
    + g_free(xfer->ip);
    + g_free(xfer->rxqueue);
    +
    + /* clean up the sending properties */
    + g_clear_pointer(&xfer->listen_data, purple_network_listen_cancel);
    + if(xfer->inpa > 0) {
    + purple_input_remove(xfer->inpa);
    + }
    + if(xfer->fd != -1) {
    + close(xfer->fd);
    + }
    +
    + G_OBJECT_CLASS(irc_xfer_parent_class)->finalize(obj);
    +}
    +
    +static void
    +irc_xfer_class_finalize(IrcXferClass *klass) {
    +
    +}
    +
    +static void
    +irc_xfer_class_init(IrcXferClass *klass) {
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    +
    + obj_class->finalize = irc_xfer_finalize;
    +
    + xfer_class->init = irc_dccsend_init;
    + xfer_class->ack = irc_dccsend_recv_ack;
    + xfer_class->write = irc_dccsend_send_write;
    +}
    +
    +void
    +irc_xfer_register(GTypeModule *module) {
    + irc_xfer_register_type(module);
    +}
    --- a/libpurple/protocols/irc/irc.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/irc/irc.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file irc.c
    - *
    * purple
    *
    * Copyright (C) 2003, Robbert Haarman <purple@inglorion.net>
    @@ -24,19 +22,7 @@
    */
    #include "internal.h"
    -
    -#include "accountopt.h"
    -#include "action.h"
    -#include "buddylist.h"
    -#include "conversation.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "protocol.h"
    -#include "plugins.h"
    -#include "purple-gio.h"
    -#include "util.h"
    -#include "version.h"
    +#include <purple.h>
    #include "irc.h"
    @@ -54,7 +40,7 @@
    static int irc_im_send(PurpleConnection *gc, PurpleMessage *msg);
    static int irc_chat_send(PurpleConnection *gc, int id, PurpleMessage *msg);
    static void irc_chat_join (PurpleConnection *gc, GHashTable *data);
    -static void irc_read_input(struct irc_conn *irc);
    +static void irc_read_input_cb(GObject *source, GAsyncResult *res, gpointer data);
    static guint irc_nick_hash(const char *nick);
    static gboolean irc_nick_equal(const char *nick1, const char *nick2);
    @@ -63,10 +49,8 @@
    PurpleProtocol *_irc_protocol = NULL;
    static gint
    -irc_uri_handler_match_server(gconstpointer a, gconstpointer b)
    +irc_uri_handler_match_server(PurpleAccount *account, const gchar *match_server)
    {
    - PurpleAccount *account = PURPLE_ACCOUNT(a);
    - const gchar *match_server = b;
    const gchar *protocol_id;
    const gchar *username;
    gchar *server;
    @@ -132,8 +116,8 @@
    /* Find account with correct server */
    accounts = purple_accounts_get_all();
    - account_node = g_list_find_custom(accounts, server,
    - irc_uri_handler_match_server);
    + account_node = g_list_find_custom(
    + accounts, server, (GCompareFunc)irc_uri_handler_match_server);
    if (account_node == NULL) {
    purple_debug_warning("irc",
    @@ -258,7 +242,7 @@
    if (!result) {
    purple_queued_output_stream_clear_queue(stream);
    - g_prefix_error(&error, _("Lost connection with server: "));
    + g_prefix_error(&error, "%s", _("Lost connection with server: "));
    purple_connection_take_error(gc, error);
    return;
    }
    @@ -572,7 +556,7 @@
    res, &error);
    if (conn == NULL) {
    - g_prefix_error(&error, _("Unable to connect: "));
    + g_prefix_error(&error, "%s", _("Unable to connect: "));
    purple_connection_take_error(gc, error);
    return;
    }
    @@ -586,7 +570,9 @@
    irc->input = g_data_input_stream_new(
    g_io_stream_get_input_stream(
    G_IO_STREAM(irc->conn)));
    - irc_read_input(irc);
    + g_data_input_stream_read_line_async(irc->input,
    + G_PRIORITY_DEFAULT, irc->cancellable,
    + irc_read_input_cb, gc);
    }
    }
    @@ -742,7 +728,7 @@
    G_DATA_INPUT_STREAM(source), res, &len, &error);
    if (line == NULL && error != NULL) {
    - g_prefix_error(&error, _("Lost connection with server: "));
    + g_prefix_error(&error, "%s", _("Lost connection with server: "));
    purple_connection_take_error(gc, error);
    return;
    } else if (line == NULL) {
    @@ -766,19 +752,12 @@
    while (start < len && line[start] == '\0')
    ++start;
    - if (len - start > 0)
    + if (start < len) {
    irc_parse_msg(irc, line + start);
    + }
    g_free(line);
    - irc_read_input(irc);
    -}
    -
    -static void
    -irc_read_input(struct irc_conn *irc)
    -{
    - PurpleConnection *gc = purple_account_get_connection(irc->account);
    -
    g_data_input_stream_read_line_async(irc->input,
    G_PRIORITY_DEFAULT, irc->cancellable,
    irc_read_input_cb, gc);
    @@ -962,8 +941,9 @@
    }
    static void
    -irc_protocol_init(PurpleProtocol *protocol)
    +irc_protocol_init(IRCProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountUserSplit *split;
    PurpleAccountOption *option;
    @@ -1010,16 +990,23 @@
    }
    static void
    -irc_protocol_class_init(PurpleProtocolClass *klass)
    +irc_protocol_class_init(IRCProtocolClass *klass)
    {
    - klass->login = irc_login;
    - klass->close = irc_close;
    - klass->status_types = irc_status_types;
    - klass->list_icon = irc_blist_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = irc_login;
    + protocol_class->close = irc_close;
    + protocol_class->status_types = irc_status_types;
    + protocol_class->list_icon = irc_blist_icon;
    }
    static void
    -irc_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +irc_protocol_class_finalize(G_GNUC_UNUSED IRCProtocolClass *klass)
    +{
    +}
    +
    +static void
    +irc_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = irc_get_actions;
    client_iface->normalize = purple_normalize_nocase;
    @@ -1027,7 +1014,7 @@
    }
    static void
    -irc_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +irc_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->set_status = irc_set_status;
    server_iface->get_info = irc_get_info;
    @@ -1038,13 +1025,13 @@
    }
    static void
    -irc_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +irc_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = irc_im_send;
    }
    static void
    -irc_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +irc_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = irc_chat_join_info;
    chat_iface->info_defaults = irc_chat_info_defaults;
    @@ -1057,7 +1044,7 @@
    }
    static void
    -irc_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface)
    +irc_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface)
    {
    roomlist_iface->get_list = irc_roomlist_get_list;
    roomlist_iface->cancel = irc_roomlist_cancel;
    @@ -1070,27 +1057,26 @@
    xfer_iface->new_xfer = irc_dccsend_new_xfer;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - IRCProtocol, irc_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + IRCProtocol, irc_protocol, PURPLE_TYPE_PROTOCOL, 0,
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - irc_protocol_client_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + irc_protocol_client_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - irc_protocol_server_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + irc_protocol_server_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - irc_protocol_im_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + irc_protocol_im_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - irc_protocol_chat_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + irc_protocol_chat_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
    - irc_protocol_roomlist_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
    + irc_protocol_roomlist_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
    - irc_protocol_xfer_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    + irc_protocol_xfer_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -1113,7 +1099,9 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - irc_protocol_register_type(plugin);
    + irc_protocol_register_type(G_TYPE_MODULE(plugin));
    +
    + irc_xfer_register(G_TYPE_MODULE(plugin));
    _irc_protocol = purple_protocols_add(IRC_TYPE_PROTOCOL, error);
    if (!_irc_protocol)
    --- a/libpurple/protocols/irc/irc.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/irc/irc.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file irc.h
    - *
    * purple
    *
    * Copyright (C) 2003, 2012 Ethan Blanton <elb@pidgin.im>
    @@ -20,8 +18,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_IRC_H
    -#define _PURPLE_IRC_H
    +#ifndef PURPLE_IRC_IRC_H
    +#define PURPLE_IRC_IRC_H
    #include <glib.h>
    #include <gmodule.h>
    @@ -30,10 +28,7 @@
    #include <sasl/sasl.h>
    #endif
    -#include "circularbuffer.h"
    -#include "xfer.h"
    -#include "queuedoutputstream.h"
    -#include "roomlist.h"
    +#include <purple.h>
    #define IRC_TYPE_PROTOCOL (irc_protocol_get_type())
    #define IRC_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), IRC_TYPE_PROTOCOL, IRCProtocol))
    @@ -238,7 +233,13 @@
    int irc_cmd_whois(struct irc_conn *irc, const char *cmd, const char *target, const char **args);
    int irc_cmd_whowas(struct irc_conn *irc, const char *cmd, const char *target, const char **args);
    +#define IRC_TYPE_XFER (irc_xfer_get_type())
    +G_DECLARE_FINAL_TYPE(IrcXfer, irc_xfer, IRC, XFER, PurpleXfer);
    +
    +void irc_xfer_register(GTypeModule *module);
    +
    PurpleXfer *irc_dccsend_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    void irc_dccsend_send_file(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file);
    void irc_dccsend_recv(struct irc_conn *irc, const char *from, const char *msg);
    -#endif /* _PURPLE_IRC_H */
    +
    +#endif /* PURPLE_IRC_IRC_H */
    --- a/libpurple/protocols/irc/msgs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/irc/msgs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file msgs.c
    - *
    * purple
    *
    * Copyright (C) 2003, 2012 Ethan Blanton <elb@pidgin.im>
    @@ -27,12 +25,8 @@
    */
    #include "internal.h"
    +#include <purple.h>
    -#include "conversation.h"
    -#include "buddylist.h"
    -#include "notify.h"
    -#include "util.h"
    -#include "debug.h"
    #include "irc.h"
    #include <stdio.h>
    --- a/libpurple/protocols/irc/parse.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/irc/parse.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file parse.c
    - *
    * purple
    *
    * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
    @@ -21,13 +19,8 @@
    */
    #include "internal.h"
    +#include <purple.h>
    -#include "accountopt.h"
    -#include "conversation.h"
    -#include "notify.h"
    -#include "debug.h"
    -#include "util.h"
    -#include "cmds.h"
    #include "irc.h"
    #include <stdio.h>
    --- a/libpurple/protocols/jabber/adhoccommands.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/adhoccommands.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_ADHOCCOMMANDS_H_
    -#define PURPLE_JABBER_ADHOCCOMMANDS_H_
    +#ifndef PURPLE_JABBER_ADHOCCOMMANDS_H
    +#define PURPLE_JABBER_ADHOCCOMMANDS_H
    #include "jabber.h"
    @@ -42,4 +42,4 @@
    void jabber_adhoc_init_server_commands(JabberStream *js, GList **m);
    -#endif /* PURPLE_JABBER_ADHOCCOMMANDS_H_ */
    +#endif /* PURPLE_JABBER_ADHOCCOMMANDS_H */
    --- a/libpurple/protocols/jabber/auth.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/auth.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_AUTH_H_
    -#define PURPLE_JABBER_AUTH_H_
    +
    +#ifndef PURPLE_JABBER_AUTH_H
    +#define PURPLE_JABBER_AUTH_H
    typedef struct _JabberSaslMech JabberSaslMech;
    @@ -65,4 +66,4 @@
    void jabber_auth_init(void);
    void jabber_auth_uninit(void);
    -#endif /* PURPLE_JABBER_AUTH_H_ */
    +#endif /* PURPLE_JABBER_AUTH_H */
    --- a/libpurple/protocols/jabber/auth_digest_md5.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/auth_digest_md5.c Tue Oct 08 21:48:28 2019 -0500
    @@ -55,7 +55,7 @@
    gboolean in_quotes = FALSE;
    char *name, *value = NULL;
    token_start = cur;
    - while(*cur != '\0' && (in_quotes || (!in_quotes && *cur != ','))) {
    + while (*cur != '\0' && (in_quotes || *cur != ',')) {
    if (*cur == '"')
    in_quotes = !in_quotes;
    cur++;
    --- a/libpurple/protocols/jabber/auth_digest_md5.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/auth_digest_md5.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_AUTH_DIGEST_MD5_H_
    -#define PURPLE_JABBER_AUTH_DIGEST_MD5_H_
    +
    +#ifndef PURPLE_JABBER_AUTH_DIGEST_MD5_H
    +#define PURPLE_JABBER_AUTH_DIGEST_MD5_H
    #include "internal.h"
    @@ -36,4 +37,4 @@
    */
    GHashTable *jabber_auth_digest_md5_parse(const char *challenge);
    -#endif /* PURPLE_JABBER_AUTH_DIGEST_MD5_H_ */
    +#endif /* PURPLE_JABBER_AUTH_DIGEST_MD5_H */
    --- a/libpurple/protocols/jabber/auth_scram.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/auth_scram.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_AUTH_SCRAM_H_
    -#define PURPLE_JABBER_AUTH_SCRAM_H_
    +
    +#ifndef PURPLE_JABBER_AUTH_SCRAM_H
    +#define PURPLE_JABBER_AUTH_SCRAM_H
    /*
    * Every function in this file is ONLY exposed for tests.
    @@ -91,4 +92,4 @@
    */
    void jabber_scram_data_destroy(JabberScramData *data);
    -#endif /* PURPLE_JABBER_AUTH_SCRAM_H_ */
    +#endif /* PURPLE_JABBER_AUTH_SCRAM_H */
    --- a/libpurple/protocols/jabber/bosh.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/bosh.h Tue Oct 08 21:48:28 2019 -0500
    @@ -22,8 +22,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_BOSH_H_
    -#define PURPLE_JABBER_BOSH_H_
    +
    +#ifndef PURPLE_JABBER_BOSH_H
    +#define PURPLE_JABBER_BOSH_H
    typedef struct _PurpleJabberBOSHConnection PurpleJabberBOSHConnection;
    @@ -51,4 +52,4 @@
    void
    jabber_bosh_connection_send_keepalive(PurpleJabberBOSHConnection *conn);
    -#endif /* PURPLE_JABBER_BOSH_H_ */
    +#endif /* PURPLE_JABBER_BOSH_H */
    --- a/libpurple/protocols/jabber/buddy.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/buddy.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_BUDDY_H_
    -#define PURPLE_JABBER_BUDDY_H_
    +
    +#ifndef PURPLE_JABBER_BUDDY_H
    +#define PURPLE_JABBER_BUDDY_H
    typedef struct _JabberBuddy JabberBuddy;
    @@ -124,4 +125,4 @@
    jabber_resource_get_identity_category_type(const JabberBuddyResource *jbr,
    const gchar *category);
    -#endif /* PURPLE_JABBER_BUDDY_H_ */
    +#endif /* PURPLE_JABBER_BUDDY_H */
    --- a/libpurple/protocols/jabber/caps.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/caps.c Tue Oct 08 21:48:28 2019 -0500
    @@ -503,14 +503,14 @@
    jabber_caps_client_info_destroy(info);
    info = value;
    } else {
    - JabberCapsTuple *n_key = (JabberCapsTuple *)&info->tuple;
    + JabberCapsTuple *n_key = NULL;
    - if (G_UNLIKELY(n_key == NULL)) {
    + if (G_UNLIKELY(info == NULL)) {
    g_warn_if_reached();
    - jabber_caps_client_info_destroy(info);
    return;
    }
    + n_key = (JabberCapsTuple *)&info->tuple;
    n_key->node = userdata->node;
    n_key->ver = userdata->ver;
    n_key->hash = userdata->hash;
    @@ -908,8 +908,7 @@
    field->values);
    }
    } else {
    - g_list_foreach(field->values, (GFunc) g_free, NULL);
    - g_list_free(field->values);
    + g_list_free_full(field->values, g_free);
    }
    g_free(field->var);
    --- a/libpurple/protocols/jabber/caps.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/caps.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_CAPS_H_
    -#define PURPLE_JABBER_CAPS_H_
    +#ifndef PURPLE_JABBER_CAPS_H
    +#define PURPLE_JABBER_CAPS_H
    typedef struct _JabberCapsClientInfo JabberCapsClientInfo;
    @@ -127,4 +127,4 @@
    */
    JabberCapsClientInfo *jabber_caps_parse_client_info(PurpleXmlNode *query);
    -#endif /* PURPLE_JABBER_CAPS_H_ */
    +#endif /* PURPLE_JABBER_CAPS_H */
    --- a/libpurple/protocols/jabber/chat.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/chat.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_CHAT_H_
    -#define PURPLE_JABBER_CHAT_H_
    +
    +#ifndef PURPLE_JABBER_CHAT_H
    +#define PURPLE_JABBER_CHAT_H
    #include "internal.h"
    #include "connection.h"
    @@ -115,4 +116,4 @@
    const gchar *cap);
    guint jabber_chat_get_num_participants(const JabberChat *chat);
    -#endif /* PURPLE_JABBER_CHAT_H_ */
    +#endif /* PURPLE_JABBER_CHAT_H */
    --- a/libpurple/protocols/jabber/disco.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/disco.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_DISCO_H_
    -#define PURPLE_JABBER_DISCO_H_
    +
    +#ifndef PURPLE_JABBER_DISCO_H
    +#define PURPLE_JABBER_DISCO_H
    #include "jabber.h"
    @@ -48,4 +49,4 @@
    void jabber_disco_info_do(JabberStream *js, const char *who,
    JabberDiscoInfoCallback *callback, gpointer data);
    -#endif /* PURPLE_JABBER_DISCO_H_ */
    +#endif /* PURPLE_JABBER_DISCO_H */
    --- a/libpurple/protocols/jabber/gtalk.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/gtalk.c Tue Oct 08 21:48:28 2019 -0500
    @@ -34,8 +34,9 @@
    }
    static void
    -gtalk_protocol_init(PurpleProtocol *protocol)
    +gtalk_protocol_init(GTalkProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountUserSplit *split;
    PurpleAccountOption *option;
    GList *encryption_values = NULL;
    @@ -104,22 +105,36 @@
    }
    static void
    -gtalk_protocol_class_init(PurpleProtocolClass *klass)
    +gtalk_protocol_class_init(GTalkProtocolClass *klass)
    {
    - klass->list_icon = gtalk_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->list_icon = gtalk_list_icon;
    }
    static void
    -gtalk_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +gtalk_protocol_class_finalize(G_GNUC_UNUSED GTalkProtocolClass *klass)
    +{
    +}
    +
    +static void
    +gtalk_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    /* disable xmpp functions not available for gtalk */
    server_iface->register_user = NULL;
    server_iface->unregister_user = NULL;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - GTalkProtocol, gtalk_protocol, JABBER_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + GTalkProtocol, gtalk_protocol, JABBER_TYPE_PROTOCOL, 0,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + gtalk_protocol_server_iface_init));
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - gtalk_protocol_server_iface_init)
    -);
    +/* This exists solely because the above macro makes gtalk_protocol_register_type
    + * static. */
    +void
    +gtalk_protocol_register(PurplePlugin *plugin)
    +{
    + gtalk_protocol_register_type(G_TYPE_MODULE(plugin));
    +}
    --- a/libpurple/protocols/jabber/gtalk.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/gtalk.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _GTALK_H_
    -#define _GTALK_H_
    +
    +#ifndef PURPLE_JABBER_GTALK_H
    +#define PURPLE_JABBER_GTALK_H
    #include "jabber.h"
    @@ -44,11 +45,12 @@
    /**
    * Registers the GTalkProtocol type in the type system.
    */
    -void gtalk_protocol_register_type(PurplePlugin *plugin);
    +G_GNUC_INTERNAL
    +void gtalk_protocol_register(PurplePlugin *plugin);
    /**
    * Returns the GType for the GTalkProtocol object.
    */
    G_MODULE_EXPORT GType gtalk_protocol_get_type(void);
    -#endif /* _GTALK_H_ */
    +#endif /* PURPLE_JABBER_GTALK_H */
    --- a/libpurple/protocols/jabber/ibb.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/ibb.c Tue Oct 08 21:48:28 2019 -0500
    @@ -67,7 +67,6 @@
    if (!sid || !block_size) {
    purple_debug_error("jabber",
    "IBB session open tag requires sid and block-size attributes\n");
    - g_free(sess);
    return NULL;
    }
    --- a/libpurple/protocols/jabber/ibb.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/ibb.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_IBB_H_
    -#define PURPLE_JABBER_IBB_H_
    +#ifndef PURPLE_JABBER_IBB_H
    +#define PURPLE_JABBER_IBB_H
    #include "jabber.h"
    #include "iq.h"
    @@ -127,4 +127,4 @@
    void jabber_ibb_init(void);
    void jabber_ibb_uninit(void);
    -#endif /* PURPLE_JABBER_IBB_H_ */
    +#endif /* PURPLE_JABBER_IBB_H */
    --- a/libpurple/protocols/jabber/iq.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/iq.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_IQ_H_
    -#define PURPLE_JABBER_IQ_H_
    +
    +#ifndef PURPLE_JABBER_IQ_H
    +#define PURPLE_JABBER_IQ_H
    typedef enum {
    JABBER_IQ_SET,
    @@ -115,4 +116,4 @@
    void jabber_iq_signal_register(const gchar *node, const gchar *xmlns);
    void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns);
    -#endif /* PURPLE_JABBER_IQ_H_ */
    +#endif /* PURPLE_JABBER_IQ_H */
    --- a/libpurple/protocols/jabber/jabber.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/jabber.c Tue Oct 08 21:48:28 2019 -0500
    @@ -480,7 +480,7 @@
    g_return_if_fail(data != NULL);
    /* because printing a tab to debug every minute gets old */
    - if (data && !purple_strequal(data, "\t")) {
    + if (!purple_strequal(data, "\t")) {
    const char *username;
    char *text = NULL, *last_part = NULL, *tag_start = NULL;
    @@ -2622,8 +2622,8 @@
    if(!(jid = jabber_id_new(name)))
    return NULL;
    - for(gnode = purple_blist_get_root(); gnode;
    - gnode = purple_blist_node_get_sibling_next(gnode)) {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = purple_blist_node_get_sibling_next(gnode)) {
    for(cnode = purple_blist_node_get_first_child(gnode);
    cnode;
    cnode = purple_blist_node_get_sibling_next(cnode)) {
    @@ -4091,8 +4091,10 @@
    }
    static void
    -jabber_protocol_init(PurpleProtocol *protocol)
    +jabber_protocol_init(JabberProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    +
    protocol->id = "prpl-jabber";
    protocol->name = "XMPP";
    protocol->options = OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME |
    @@ -4109,16 +4111,23 @@
    }
    static void
    -jabber_protocol_class_init(PurpleProtocolClass *klass)
    +jabber_protocol_class_init(JabberProtocolClass *klass)
    {
    - klass->login = jabber_login;
    - klass->close = jabber_close;
    - klass->status_types = jabber_status_types;
    - klass->list_icon = jabber_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = jabber_login;
    + protocol_class->close = jabber_close;
    + protocol_class->status_types = jabber_status_types;
    + protocol_class->list_icon = jabber_list_icon;
    }
    static void
    -jabber_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +jabber_protocol_class_finalize(G_GNUC_UNUSED JabberProtocolClass *klass)
    +{
    +}
    +
    +static void
    +jabber_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = jabber_get_actions;
    client_iface->list_emblem = jabber_list_emblem;
    @@ -4133,7 +4142,7 @@
    }
    static void
    -jabber_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +jabber_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->register_user = jabber_register_account;
    server_iface->unregister_user = jabber_unregister_account;
    @@ -4153,14 +4162,14 @@
    }
    static void
    -jabber_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +jabber_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = jabber_message_send_im;
    im_iface->send_typing = jabber_send_typing;
    }
    static void
    -jabber_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +jabber_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = jabber_chat_info;
    chat_iface->info_defaults = jabber_chat_info_defaults;
    @@ -4174,14 +4183,14 @@
    }
    static void
    -jabber_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    +jabber_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    {
    privacy_iface->add_deny = jabber_add_deny;
    privacy_iface->rem_deny = jabber_rem_deny;
    }
    static void
    -jabber_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface)
    +jabber_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface)
    {
    roomlist_iface->get_list = jabber_roomlist_get_list;
    roomlist_iface->cancel = jabber_roomlist_cancel;
    @@ -4196,7 +4205,7 @@
    }
    static void
    -jabber_protocol_media_iface_init(PurpleProtocolMediaIface *media_iface)
    +jabber_protocol_media_iface_init(PurpleProtocolMediaInterface *media_iface)
    {
    media_iface->initiate_session = jabber_initiate_media;
    media_iface->get_caps = jabber_get_media_caps;
    @@ -4210,36 +4219,36 @@
    xfer_iface->new_xfer = jabber_si_new_xfer;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - JabberProtocol, jabber_protocol, PURPLE_TYPE_PROTOCOL, G_TYPE_FLAG_ABSTRACT,
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - jabber_protocol_client_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - jabber_protocol_server_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - jabber_protocol_im_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - jabber_protocol_chat_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - jabber_protocol_privacy_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
    - jabber_protocol_roomlist_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ATTENTION,
    - jabber_protocol_attention_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_MEDIA_IFACE,
    - jabber_protocol_media_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
    - jabber_protocol_xfer_iface_init)
    -);
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + JabberProtocol, jabber_protocol, PURPLE_TYPE_PROTOCOL,
    + G_TYPE_FLAG_ABSTRACT,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + jabber_protocol_client_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + jabber_protocol_server_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + jabber_protocol_im_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + jabber_protocol_chat_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    + jabber_protocol_privacy_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
    + jabber_protocol_roomlist_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ATTENTION,
    + jabber_protocol_attention_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_MEDIA,
    + jabber_protocol_media_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    + jabber_protocol_xfer_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -4274,10 +4283,12 @@
    jingle_rtp_register(plugin);
    #endif
    - jabber_protocol_register_type(plugin);
    -
    - gtalk_protocol_register_type(plugin);
    - xmpp_protocol_register_type(plugin);
    + jabber_protocol_register_type(G_TYPE_MODULE(plugin));
    +
    + gtalk_protocol_register(plugin);
    + xmpp_protocol_register(plugin);
    +
    + jabber_si_xfer_register(G_TYPE_MODULE(plugin));
    xmpp_protocol = purple_protocols_add(XMPP_TYPE_PROTOCOL, error);
    if (!xmpp_protocol)
    --- a/libpurple/protocols/jabber/jabber.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/jabber.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_H_
    -#define PURPLE_JABBER_H_
    +
    +#ifndef PURPLE_JABBER_JABBER_H
    +#define PURPLE_JABBER_JABBER_H
    typedef enum {
    JABBER_CAP_NONE = 0,
    @@ -429,4 +430,4 @@
    PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who);
    gboolean jabber_can_receive_file(PurpleProtocolXfer *xfer, PurpleConnection *gc, const gchar *who);
    -#endif /* PURPLE_JABBER_H_ */
    +#endif /* PURPLE_JABBER_JABBER_H */
    --- a/libpurple/protocols/jabber/jingle/rtp.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/jingle/rtp.c Tue Oct 08 21:48:28 2019 -0500
    @@ -317,22 +317,17 @@
    return FALSE;
    }
    + media_type = jingle_rtp_get_media_type(content);
    + if (media_type == NULL) {
    + g_object_unref(session);
    + return FALSE;
    + }
    +
    name = jingle_content_get_name(content);
    - media_type = jingle_rtp_get_media_type(content);
    remote_jid = jingle_session_get_remote_jid(session);
    senders = jingle_content_get_senders(content);
    transport = jingle_content_get_transport(content);
    - if (media_type == NULL) {
    - g_free(name);
    - g_free(remote_jid);
    - g_free(senders);
    - g_free(params);
    - g_object_unref(transport);
    - g_object_unref(session);
    - return FALSE;
    - }
    -
    if (JINGLE_IS_RAWUDP(transport))
    transmitter = "rawudp";
    else if (JINGLE_IS_ICEUDP(transport))
    --- a/libpurple/protocols/jabber/jutil.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/jutil.c Tue Oct 08 21:48:28 2019 -0500
    @@ -601,8 +601,9 @@
    static char buf[3072]; /* maximum legal length of a jabber jid */
    JabberID *jid;
    - if (account)
    - gc = purple_account_get_connection(account);
    + if (account) {
    + gc = purple_account_get_connection((PurpleAccount *)account);
    + }
    if (gc)
    js = purple_connection_get_protocol_data(gc);
    --- a/libpurple/protocols/jabber/jutil.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/jutil.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_JUTIL_H_
    -#define PURPLE_JABBER_JUTIL_H_
    +
    +#ifndef PURPLE_JABBER_JUTIL_H
    +#define PURPLE_JABBER_JUTIL_H
    typedef struct {
    char *node;
    @@ -94,4 +95,4 @@
    /* show attr (presence stanza) -> state */
    JabberBuddyState jabber_buddy_show_get_state(const char *id);
    -#endif /* PURPLE_JABBER_JUTIL_H_ */
    +#endif /* PURPLE_JABBER_JUTIL_H */
    --- a/libpurple/protocols/jabber/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -2,11 +2,10 @@
    # Check for Internationalized Domain Name support
    #######################################################################
    -if get_option('idn')
    - idn = dependency('libidn', version : '>= 0.0.0', required : force_deps)
    +idn = dependency('libidn', version : '>= 0.0.0', required : get_option('idn'))
    +if idn.found()
    use_idn = [ '-DUSE_IDN' ]
    else
    - idn = []
    use_idn = []
    endif
    @@ -104,7 +103,7 @@
    'xmpp.h'
    ]
    -if enable_cyrus_sasl
    +if sasl.found()
    JABBERSOURCES += ['auth_cyrus.c']
    endif
    --- a/libpurple/protocols/jabber/message.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/message.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_MESSAGE_H_
    -#define PURPLE_JABBER_MESSAGE_H_
    +
    +#ifndef PURPLE_JABBER_MESSAGE_H
    +#define PURPLE_JABBER_MESSAGE_H
    #include "buddy.h"
    #include "jabber.h"
    @@ -78,4 +79,4 @@
    gboolean jabber_custom_smileys_isenabled(JabberStream *js, const gchar *namespace);
    -#endif /* PURPLE_JABBER_MESSAGE_H_ */
    +#endif /* PURPLE_JABBER_MESSAGE_H */
    --- a/libpurple/protocols/jabber/namespaces.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/namespaces.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_NAMESPACES_H_
    -#define PURPLE_JABBER_NAMESPACES_H_
    +#ifndef PURPLE_JABBER_NAMESPACES_H
    +#define PURPLE_JABBER_NAMESPACES_H
    #define NS_XMPP_BIND "urn:ietf:params:xml:ns:xmpp-bind"
    #define NS_XMPP_CLIENT "jabber:client"
    @@ -114,4 +114,4 @@
    /* Apple extension(s) */
    #define NS_APPLE_IDLE "http://www.apple.com/xmpp/idle"
    -#endif /* PURPLE_JABBER_NAMESPACES_H_ */
    +#endif /* PURPLE_JABBER_NAMESPACES_H */
    --- a/libpurple/protocols/jabber/oob.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/oob.c Tue Oct 08 21:48:28 2019 -0500
    @@ -30,34 +30,23 @@
    #include "iq.h"
    #include "oob.h"
    -typedef struct {
    +struct _JabberOOBXfer {
    JabberStream *js;
    gchar *iq_id;
    gchar *url;
    PurpleHttpConnection *hc;
    -} JabberOOBXfer;
    +};
    -static void jabber_oob_xfer_init(PurpleXfer *xfer)
    +G_DEFINE_DYNAMIC_TYPE(JabberOOBXfer, jabber_oob_xfer, PURPLE_TYPE_XFER);
    +
    +static void jabber_oob_xfer_xfer_init(PurpleXfer *xfer)
    {
    purple_xfer_start(xfer, -1, NULL, 0);
    }
    -static void jabber_oob_xfer_free(PurpleXfer *xfer)
    -{
    - JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
    -
    - purple_xfer_set_protocol_data(xfer, NULL);
    - jox->js->oob_file_transfers = g_list_remove(jox->js->oob_file_transfers,
    - xfer);
    -
    - g_free(jox->iq_id);
    - g_free(jox->url);
    - g_free(jox);
    -}
    -
    static void jabber_oob_xfer_end(PurpleXfer *xfer)
    {
    - JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
    + JabberOOBXfer *jox = JABBER_OOB_XFER(xfer);
    JabberIq *iq;
    iq = jabber_iq_new(jox->js, JABBER_IQ_RESULT);
    @@ -65,8 +54,6 @@
    jabber_iq_set_id(iq, jox->iq_id);
    jabber_iq_send(iq);
    -
    - jabber_oob_xfer_free(xfer);
    }
    static void
    @@ -79,7 +66,7 @@
    if (purple_xfer_is_cancelled(xfer))
    return;
    - jox = purple_xfer_get_protocol_data(xfer);
    + jox = JABBER_OOB_XFER(xfer);
    jox->hc = NULL;
    if (!purple_http_response_is_successful(response) ||
    @@ -119,7 +106,7 @@
    static void jabber_oob_xfer_start(PurpleXfer *xfer)
    {
    PurpleHttpRequest *req;
    - JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
    + JabberOOBXfer *jox = JABBER_OOB_XFER(xfer);
    req = purple_http_request_new(jox->url);
    purple_http_request_set_timeout(req, -1);
    @@ -134,7 +121,7 @@
    }
    static void jabber_oob_xfer_recv_error(PurpleXfer *xfer, const char *code) {
    - JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
    + JabberOOBXfer *jox = JABBER_OOB_XFER(xfer);
    JabberIq *iq;
    PurpleXmlNode *y, *z;
    @@ -153,8 +140,6 @@
    purple_xmlnode_set_namespace(z, NS_XMPP_STANZAS);
    }
    jabber_iq_send(iq);
    -
    - jabber_oob_xfer_free(xfer);
    }
    static void jabber_oob_xfer_recv_denied(PurpleXfer *xfer) {
    @@ -162,7 +147,7 @@
    }
    static void jabber_oob_xfer_recv_cancelled(PurpleXfer *xfer) {
    - JabberOOBXfer *jox = purple_xfer_get_protocol_data(xfer);
    + JabberOOBXfer *jox = JABBER_OOB_XFER(xfer);
    purple_http_conn_cancel(jox->hc);
    jabber_oob_xfer_recv_error(xfer, "404");
    @@ -171,7 +156,6 @@
    void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
    const char *id, PurpleXmlNode *querynode) {
    JabberOOBXfer *jox;
    - PurpleXfer *xfer;
    const gchar *filename, *slash;
    gchar *url;
    PurpleXmlNode *urlnode;
    @@ -189,33 +173,70 @@
    if (!url)
    return;
    - xfer = purple_xfer_new(purple_connection_get_account(js->gc),
    - PURPLE_XFER_TYPE_RECEIVE, from);
    - if (!xfer) {
    - g_free(url);
    - return;
    - }
    + jox = g_object_new(
    + JABBER_TYPE_OOB_XFER,
    + "account", purple_connection_get_account(js->gc),
    + "type", PURPLE_XFER_TYPE_RECEIVE,
    + "remote-user", from,
    + NULL
    + );
    - jox = g_new0(JabberOOBXfer, 1);
    jox->iq_id = g_strdup(id);
    jox->js = js;
    jox->url = url;
    - purple_xfer_set_protocol_data(xfer, jox);
    slash = strrchr(url, '/');
    - if (slash == NULL)
    + if (slash == NULL) {
    filename = url;
    - else
    + } else {
    filename = slash + 1;
    - purple_xfer_set_filename(xfer, filename);
    + }
    +
    + purple_xfer_set_filename(PURPLE_XFER(jox), filename);
    +
    + js->oob_file_transfers = g_list_append(js->oob_file_transfers, jox);
    +
    + purple_xfer_request(PURPLE_XFER(jox));
    +}
    +
    +static void
    +jabber_oob_xfer_init(JabberOOBXfer *xfer) {
    +
    +}
    +
    +static void
    +jabber_oob_xfer_finalize(GObject *obj) {
    + JabberOOBXfer *jox = JABBER_OOB_XFER(obj);
    +
    + jox->js->oob_file_transfers = g_list_remove(jox->js->oob_file_transfers,
    + jox);
    - purple_xfer_set_init_fnc(xfer, jabber_oob_xfer_init);
    - purple_xfer_set_end_fnc(xfer, jabber_oob_xfer_end);
    - purple_xfer_set_request_denied_fnc(xfer, jabber_oob_xfer_recv_denied);
    - purple_xfer_set_cancel_recv_fnc(xfer, jabber_oob_xfer_recv_cancelled);
    - purple_xfer_set_start_fnc(xfer, jabber_oob_xfer_start);
    + g_free(jox->iq_id);
    + g_free(jox->url);
    +
    + G_OBJECT_CLASS(jabber_oob_xfer_parent_class)->finalize(obj);
    +}
    +
    +static void
    +jabber_oob_xfer_class_finalize(JabberOOBXferClass *klass) {
    +
    +}
    - js->oob_file_transfers = g_list_append(js->oob_file_transfers, xfer);
    +static void
    +jabber_oob_xfer_class_init(JabberOOBXferClass *klass) {
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    +
    + obj_class->finalize = jabber_oob_xfer_finalize;
    - purple_xfer_request(xfer);
    + xfer_class->init = jabber_oob_xfer_xfer_init;
    + xfer_class->end = jabber_oob_xfer_end;
    + xfer_class->request_denied = jabber_oob_xfer_recv_denied;
    + xfer_class->cancel_recv = jabber_oob_xfer_recv_cancelled;
    + xfer_class->start = jabber_oob_xfer_start;
    }
    +
    +void
    +jabber_oob_xfer_register(GTypeModule *module) {
    + jabber_oob_xfer_register_type(module);
    +}
    --- a/libpurple/protocols/jabber/oob.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/oob.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,12 +21,22 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_OOB_H_
    -#define PURPLE_JABBER_OOB_H_
    +
    +#ifndef PURPLE_JABBER_OOB_H
    +#define PURPLE_JABBER_OOB_H
    #include "jabber.h"
    +G_BEGIN_DECLS
    +
    +#define JABBER_TYPE_OOB_XFER (jabber_oob_xfer_get_type())
    +G_DECLARE_FINAL_TYPE(JabberOOBXfer, jabber_oob_xfer, JABBER, OOB_XFER, PurpleXfer);
    +
    void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
    const char *id, PurpleXmlNode *querynode);
    -#endif /* PURPLE_JABBER_OOB_H_ */
    +void jabber_oob_xfer_register(GTypeModule *module);
    +
    +G_END_DECLS
    +
    +#endif /* PURPLE_JABBER_OOB_H */
    --- a/libpurple/protocols/jabber/parser.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/parser.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_PARSER_H_
    -#define PURPLE_JABBER_PARSER_H_
    +
    +#ifndef PURPLE_JABBER_PARSER_H
    +#define PURPLE_JABBER_PARSER_H
    #include "jabber.h"
    @@ -30,4 +31,4 @@
    void jabber_parser_free(JabberStream *js);
    void jabber_parser_process(JabberStream *js, const char *buf, int len);
    -#endif /* PURPLE_JABBER_PARSER_H_ */
    +#endif /* PURPLE_JABBER_PARSER_H */
    --- a/libpurple/protocols/jabber/pep.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/pep.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_PEP_H_
    -#define PURPLE_JABBER_PEP_H_
    +#ifndef PURPLE_JABBER_PEP_H
    +#define PURPLE_JABBER_PEP_H
    #include "jabber.h"
    #include "message.h"
    @@ -88,4 +88,4 @@
    */
    void jabber_pep_publish(JabberStream *js, PurpleXmlNode *publish);
    -#endif /* PURPLE_JABBER_PEP_H_ */
    +#endif /* PURPLE_JABBER_PEP_H */
    --- a/libpurple/protocols/jabber/ping.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/ping.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_PING_H_
    -#define PURPLE_JABBER_PING_H_
    +
    +#ifndef PURPLE_JABBER_PING_H
    +#define PURPLE_JABBER_PING_H
    #include "jabber.h"
    #include "iq.h"
    @@ -33,4 +34,4 @@
    gboolean jabber_ping_jid(JabberStream *js, const char *jid);
    void jabber_keepalive_ping(JabberStream *js);
    -#endif /* PURPLE_JABBER_PING_H_ */
    +#endif /* PURPLE_JABBER_PING_H */
    --- a/libpurple/protocols/jabber/presence.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/presence.c Tue Oct 08 21:48:28 2019 -0500
    @@ -460,9 +460,10 @@
    data = g_base64_decode(text, &size);
    g_free(text);
    - if (data)
    - g_compute_checksum_for_data(
    - G_CHECKSUM_SHA1, data, size);
    + if (data) {
    + hash = g_compute_checksum_for_data(G_CHECKSUM_SHA1, data,
    + size);
    + }
    }
    purple_buddy_icons_set_for_user(purple_connection_get_account(js->gc), from, data, size, hash);
    @@ -492,18 +493,12 @@
    if (!jbr) {
    g_free(userdata->from);
    g_free(userdata);
    - if (exts) {
    - g_list_foreach(exts, (GFunc)g_free, NULL);
    - g_list_free(exts);
    - }
    + g_list_free_full(exts, g_free);
    return;
    }
    /* Any old jbr->caps.info is owned by the caps code */
    - if (jbr->caps.exts) {
    - g_list_foreach(jbr->caps.exts, (GFunc)g_free, NULL);
    - g_list_free(jbr->caps.exts);
    - }
    + g_list_free_full(jbr->caps.exts, g_free);
    jbr->caps.info = info;
    jbr->caps.exts = exts;
    --- a/libpurple/protocols/jabber/presence.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/presence.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_PRESENCE_H_
    -#define PURPLE_JABBER_PRESENCE_H_
    +
    +#ifndef PURPLE_JABBER_PRESENCE_H
    +#define PURPLE_JABBER_PRESENCE_H
    typedef enum {
    JABBER_PRESENCE_ERROR = -2,
    @@ -100,4 +101,4 @@
    void jabber_presence_fake_to_self(JabberStream *js, PurpleStatus *status);
    void purple_status_to_jabber(PurpleStatus *status, JabberBuddyState *state, char **msg, int *priority);
    -#endif /* PURPLE_JABBER_PRESENCE_H_ */
    +#endif /* PURPLE_JABBER_PRESENCE_H */
    --- a/libpurple/protocols/jabber/roster.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/roster.c Tue Oct 08 21:48:28 2019 -0500
    @@ -195,8 +195,6 @@
    g_free(groups->data);
    groups = g_slist_delete_link(groups, groups);
    }
    -
    - g_slist_free(buddies);
    }
    void jabber_roster_parse(JabberStream *js, const char *from,
    --- a/libpurple/protocols/jabber/roster.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/roster.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_ROSTER_H_
    -#define PURPLE_JABBER_ROSTER_H_
    +
    +#ifndef PURPLE_JABBER_ROSTER_H
    +#define PURPLE_JABBER_ROSTER_H
    /* it must *not* be localized */
    #define JABBER_ROSTER_DEFAULT_GROUP "Buddies"
    @@ -48,4 +49,4 @@
    const gchar *
    jabber_roster_group_get_global_name(PurpleGroup *group);
    -#endif /* PURPLE_JABBER_ROSTER_H_ */
    +#endif /* PURPLE_JABBER_ROSTER_H */
    --- a/libpurple/protocols/jabber/si.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/si.c Tue Oct 08 21:48:28 2019 -0500
    @@ -42,7 +42,9 @@
    #define STREAMHOST_CONNECT_TIMEOUT 15
    #define ENABLE_FT_THUMBNAILS 0
    -typedef struct {
    +struct _JabberSIXfer {
    + PurpleXfer parent;
    +
    JabberStream *js;
    PurpleProxyConnectData *connect_data;
    @@ -72,7 +74,9 @@
    JabberIBBSession *ibb_session;
    guint ibb_timeout_handle;
    PurpleCircularBuffer *ibb_buffer;
    -} JabberSIXfer;
    +};
    +
    +G_DEFINE_DYNAMIC_TYPE(JabberSIXfer, jabber_si_xfer, PURPLE_TYPE_XFER);
    /* some forward declarations */
    static void jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer);
    @@ -87,7 +91,7 @@
    for(xfers = js->file_transfers; xfers; xfers = xfers->next) {
    PurpleXfer *xfer = xfers->data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    if(jsx->stream_id && purple_xfer_get_remote_user(xfer) &&
    purple_strequal(jsx->stream_id, sid) && purple_strequal(purple_xfer_get_remote_user(xfer), from))
    return xfer;
    @@ -97,8 +101,7 @@
    }
    static void
    -jabber_si_free_streamhost(gpointer data, gpointer user_data)
    -{
    +jabber_si_free_streamhost(gpointer data) {
    JabberBytestreamsStreamhost *sh = data;
    if(!data)
    @@ -118,7 +121,7 @@
    jabber_si_bytestreams_connect_cb(gpointer data, gint source, const gchar *error_message)
    {
    PurpleXfer *xfer = data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    JabberIq *iq;
    PurpleXmlNode *query, *su;
    JabberBytestreamsStreamhost *streamhost = jsx->streamhosts->data;
    @@ -137,7 +140,7 @@
    streamhost->jid, streamhost->host,
    error_message ? error_message : "(null)");
    jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
    - jabber_si_free_streamhost(streamhost, NULL);
    + jabber_si_free_streamhost(streamhost);
    jabber_si_bytestreams_attempt_connect(xfer);
    return;
    }
    @@ -174,7 +177,7 @@
    connect_timeout_cb(gpointer data)
    {
    PurpleXfer *xfer = data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    purple_debug_info("jabber", "Streamhost connection timeout of %d seconds exceeded.\n", STREAMHOST_CONNECT_TIMEOUT);
    @@ -203,7 +206,7 @@
    jabber_si_bytestreams_ibb_timeout_cb(gpointer data)
    {
    PurpleXfer *xfer = (PurpleXfer *) data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    if (jsx && !jsx->ibb_session) {
    purple_debug_info("jabber",
    @@ -218,7 +221,7 @@
    static void jabber_si_bytestreams_attempt_connect(PurpleXfer *xfer)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    JabberBytestreamsStreamhost *streamhost;
    JabberID *dstjid;
    @@ -244,6 +247,9 @@
    purple_debug_info("jabber",
    "jabber_si_bytestreams_attempt_connect: "
    "no streamhosts found, trying IBB\n");
    + if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
    + jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS;
    + }
    /* if we are the sender, open an IBB session, but not if we already
    did it, since we could have received the error <iq/> from the
    receiver already... */
    @@ -322,7 +328,7 @@
    if (jsx->connect_data == NULL)
    {
    jsx->streamhosts = g_list_remove(jsx->streamhosts, streamhost);
    - jabber_si_free_streamhost(streamhost, NULL);
    + jabber_si_free_streamhost(streamhost);
    jabber_si_bytestreams_attempt_connect(xfer);
    }
    }
    @@ -347,7 +353,7 @@
    if(!(xfer = jabber_si_xfer_find(js, sid, from)))
    return;
    - jsx = purple_xfer_get_protocol_data(xfer);
    + jsx = JABBER_SI_XFER(xfer);
    if(!jsx->accepted)
    return;
    @@ -384,7 +390,7 @@
    PurpleInputCondition cond)
    {
    PurpleXfer *xfer = data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    int len;
    len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
    @@ -419,7 +425,7 @@
    PurpleInputCondition cond)
    {
    PurpleXfer *xfer = data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    char buffer[42]; /* 40 for DST.ADDR + 2 bytes for port number*/
    int len;
    char *dstaddr, *hash;
    @@ -528,7 +534,7 @@
    PurpleInputCondition cond)
    {
    PurpleXfer *xfer = data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    int len;
    len = write(source, jsx->rxqueue + jsx->rxlen, jsx->rxmaxlen - jsx->rxlen);
    @@ -565,7 +571,7 @@
    PurpleInputCondition cond)
    {
    PurpleXfer *xfer = data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    int i;
    int len;
    char buffer[256];
    @@ -669,7 +675,7 @@
    PurpleInputCondition cond)
    {
    PurpleXfer *xfer = data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    int acceptfd;
    purple_debug_info("jabber", "in jabber_si_xfer_bytestreams_send_connected_cb\n");
    @@ -712,7 +718,7 @@
    return;
    }
    - jsx = purple_xfer_get_protocol_data(xfer);
    + jsx = JABBER_SI_XFER(xfer);
    /* In the case of a direct file transfer, this is expected to return */
    if(!jsx)
    @@ -726,6 +732,11 @@
    "jabber_si_xfer_connect_proxy_cb: got error, method: %d\n",
    jsx->stream_method);
    if (jsx->stream_method & STREAM_METHOD_IBB) {
    + /* if we previously tried bytestreams, we need to disble it. */
    + if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
    + jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS;
    + }
    +
    purple_debug_info("jabber", "IBB is possible, try it\n");
    /* if we are the sender and haven't already opened an IBB
    session, do so now (we might already have failed to open
    @@ -770,6 +781,10 @@
    } else {
    /* if available, try to revert to IBB... */
    if (jsx->stream_method & STREAM_METHOD_IBB) {
    + if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
    + jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS;
    + }
    +
    purple_debug_info("jabber",
    "jabber_si_connect_proxy_cb: trying to revert to IBB\n");
    if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
    @@ -800,8 +815,7 @@
    }
    jsx->streamhosts = g_list_remove_link(jsx->streamhosts, matched);
    - g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
    - g_list_free(jsx->streamhosts);
    + g_list_free_full(jsx->streamhosts, jabber_si_free_streamhost);
    jsx->streamhosts = matched;
    @@ -820,7 +834,7 @@
    JabberBytestreamsStreamhost *sh, *sh2;
    int streamhost_count = 0;
    - jsx = purple_xfer_get_protocol_data(xfer);
    + jsx = JABBER_SI_XFER(xfer);
    jsx->listen_data = NULL;
    /* I'm not sure under which conditions this can happen
    @@ -920,6 +934,9 @@
    /* if available, revert to IBB */
    if (jsx->stream_method & STREAM_METHOD_IBB) {
    + if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
    + jsx->stream_method &= ~STREAM_METHOD_BYTESTREAMS;
    + }
    purple_debug_info("jabber",
    "jabber_si_xfer_bytestreams_listen_cb: trying to revert to IBB\n");
    if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
    @@ -954,7 +971,7 @@
    g_object_ref(xfer);
    - jsx = purple_xfer_get_protocol_data(xfer);
    + jsx = JABBER_SI_XFER(xfer);
    /* TODO: This should probably be done with an account option instead of
    * piggy-backing on the TOR proxy type. */
    @@ -1002,7 +1019,7 @@
    gsize size)
    {
    PurpleXfer *xfer = (PurpleXfer *) jabber_ibb_session_get_user_data(sess);
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    if ((goffset)size <= purple_xfer_get_bytes_remaining(xfer)) {
    purple_debug_info("jabber", "about to write %" G_GSIZE_FORMAT " bytes from IBB stream\n",
    @@ -1020,13 +1037,19 @@
    }
    static gssize
    -jabber_si_xfer_ibb_read(guchar **out_buffer, size_t buf_size, PurpleXfer *xfer)
    +jabber_si_xfer_ibb_read(PurpleXfer *xfer, guchar **out_buffer, size_t buf_size)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    guchar *buffer;
    - gsize size = purple_circular_buffer_get_used(jsx->ibb_buffer);
    + gsize size;
    gsize tmp;
    + if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
    + return PURPLE_XFER_CLASS(jabber_si_xfer_parent_class)->read(xfer, out_buffer, buf_size);
    + }
    +
    + size = purple_circular_buffer_get_used(jsx->ibb_buffer);
    +
    *out_buffer = buffer = g_malloc(size);
    while ((tmp = purple_circular_buffer_get_max_read(jsx->ibb_buffer))) {
    const gchar *output = purple_circular_buffer_get_output(jsx->ibb_buffer);
    @@ -1044,8 +1067,9 @@
    {
    const gchar *sid = purple_xmlnode_get_attrib(open, "sid");
    PurpleXfer *xfer = jabber_si_xfer_find(js, sid, who);
    +
    if (xfer) {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    JabberIBBSession *sess =
    jabber_ibb_session_create_from_xmlnode(js, who, id, open, xfer);
    @@ -1067,9 +1091,6 @@
    jsx->ibb_buffer =
    purple_circular_buffer_new(jabber_ibb_session_get_block_size(sess));
    - /* set up read function */
    - purple_xfer_set_read_fnc(xfer, jabber_si_xfer_ibb_read);
    -
    /* start the transfer */
    purple_xfer_start(xfer, -1, NULL, 0);
    return TRUE;
    @@ -1088,12 +1109,18 @@
    }
    static gssize
    -jabber_si_xfer_ibb_write(const guchar *buffer, size_t len, PurpleXfer *xfer)
    +jabber_si_xfer_ibb_write(PurpleXfer *xfer, const guchar *buffer, size_t len)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    JabberIBBSession *sess = jsx->ibb_session;
    - gsize packet_size = len < jabber_ibb_session_get_max_data_size(sess) ?
    - len : jabber_ibb_session_get_max_data_size(sess);
    + gsize packet_size;
    +
    + if(jsx->stream_method & STREAM_METHOD_BYTESTREAMS) {
    + purple_debug_error("jabber", "falling back to raw socket\n");
    + return PURPLE_XFER_CLASS(jabber_si_xfer_parent_class)->write(xfer, buffer, len);
    + }
    +
    + packet_size = MIN(len, jabber_ibb_session_get_max_data_size(sess));
    jabber_ibb_session_send_data(sess, buffer, packet_size);
    @@ -1134,7 +1161,7 @@
    static void
    jabber_si_xfer_ibb_send_init(JabberStream *js, PurpleXfer *xfer)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    jsx->ibb_session = jabber_ibb_session_create(js, jsx->stream_id,
    purple_xfer_get_remote_user(xfer), xfer);
    @@ -1150,8 +1177,6 @@
    jabber_ibb_session_set_error_callback(jsx->ibb_session,
    jabber_si_xfer_ibb_error_cb);
    - purple_xfer_set_write_fnc(xfer, jabber_si_xfer_ibb_write);
    -
    jsx->ibb_buffer =
    purple_circular_buffer_new(jabber_ibb_session_get_max_data_size(jsx->ibb_session));
    @@ -1191,7 +1216,7 @@
    for(field = purple_xmlnode_get_child(x, "field"); field; field = purple_xmlnode_get_next_twin(field)) {
    const char *var = purple_xmlnode_get_attrib(field, "var");
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    if(purple_strequal(var, "stream-method")) {
    if((value = purple_xmlnode_get_child(field, "value"))) {
    @@ -1222,7 +1247,7 @@
    static void jabber_si_xfer_send_request(PurpleXfer *xfer)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    JabberIq *iq;
    PurpleXmlNode *si, *file, *feature, *x, *field, *option, *value;
    char buf[32];
    @@ -1292,58 +1317,6 @@
    jabber_iq_send(iq);
    }
    -static void jabber_si_xfer_free(PurpleXfer *xfer)
    -{
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    -
    - if (jsx) {
    - JabberStream *js = jsx->js;
    -
    - js->file_transfers = g_list_remove(js->file_transfers, xfer);
    -
    - if (jsx->connect_data != NULL)
    - purple_proxy_connect_cancel(jsx->connect_data);
    - if (jsx->listen_data != NULL)
    - purple_network_listen_cancel(jsx->listen_data);
    - if (jsx->iq_id != NULL)
    - jabber_iq_remove_callback_by_id(js, jsx->iq_id);
    - if (jsx->local_streamhost_fd >= 0)
    - close(jsx->local_streamhost_fd);
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND && purple_xfer_get_fd(xfer) >= 0) {
    - purple_debug_info("jabber", "remove port mapping\n");
    - purple_network_remove_port_mapping(purple_xfer_get_fd(xfer));
    - }
    - if (jsx->connect_timeout > 0)
    - g_source_remove(jsx->connect_timeout);
    - if (jsx->ibb_timeout_handle > 0)
    - g_source_remove(jsx->ibb_timeout_handle);
    -
    - if (jsx->streamhosts) {
    - g_list_foreach(jsx->streamhosts, jabber_si_free_streamhost, NULL);
    - g_list_free(jsx->streamhosts);
    - }
    -
    - if (jsx->ibb_session) {
    - purple_debug_info("jabber",
    - "jabber_si_xfer_free: destroying IBB session\n");
    - jabber_ibb_session_destroy(jsx->ibb_session);
    - }
    -
    - if (jsx->ibb_buffer) {
    - g_object_unref(G_OBJECT(jsx->ibb_buffer));
    - }
    -
    - purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx);
    -
    - g_free(jsx->stream_id);
    - g_free(jsx->iq_id);
    - /* XXX: free other stuff */
    - g_free(jsx->rxqueue);
    - g_free(jsx);
    - purple_xfer_set_protocol_data(xfer, NULL);
    - }
    -}
    -
    /*
    * These four functions should only be called from the PurpleXfer functions
    * (typically purple_xfer_cancel_(remote|local), purple_xfer_end, or
    @@ -1351,20 +1324,19 @@
    */
    static void jabber_si_xfer_cancel_send(PurpleXfer *xfer)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    /* if there is an IBB session active, send close on that */
    if (jsx->ibb_session) {
    jabber_ibb_session_close(jsx->ibb_session);
    }
    - jabber_si_xfer_free(xfer);
    purple_debug_info("jabber", "in jabber_si_xfer_cancel_send\n");
    }
    static void jabber_si_xfer_request_denied(PurpleXfer *xfer)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    JabberStream *js = jsx->js;
    /*
    @@ -1389,34 +1361,26 @@
    jabber_iq_send(iq);
    }
    - jabber_si_xfer_free(xfer);
    purple_debug_info("jabber", "in jabber_si_xfer_request_denied\n");
    }
    static void jabber_si_xfer_cancel_recv(PurpleXfer *xfer)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    /* if there is an IBB session active, send close */
    if (jsx->ibb_session) {
    jabber_ibb_session_close(jsx->ibb_session);
    }
    - jabber_si_xfer_free(xfer);
    purple_debug_info("jabber", "in jabber_si_xfer_cancel_recv\n");
    }
    -static void jabber_si_xfer_end(PurpleXfer *xfer)
    -{
    - jabber_si_xfer_free(xfer);
    -}
    -
    -
    static void jabber_si_xfer_send_disco_cb(JabberStream *js, const char *who,
    JabberCapabilities capabilities, gpointer data)
    {
    - PurpleXfer *xfer = (PurpleXfer *) data;
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + PurpleXfer *xfer = PURPLE_XFER(data);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    if (capabilities & JABBER_CAP_IBB) {
    purple_debug_info("jabber",
    @@ -1443,7 +1407,7 @@
    static void do_transfer_send(PurpleXfer *xfer, const char *resource)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    char **who_v = g_strsplit(purple_xfer_get_remote_user(xfer), "/", 2);
    char *who;
    JabberBuddy *jb;
    @@ -1491,9 +1455,9 @@
    do_transfer_send(xfer, selected_label);
    }
    -static void jabber_si_xfer_init(PurpleXfer *xfer)
    +static void jabber_si_xfer_xfer_init(PurpleXfer *xfer)
    {
    - JabberSIXfer *jsx = purple_xfer_get_protocol_data(xfer);
    + JabberSIXfer *jsx = JABBER_SI_XFER(xfer);
    JabberIq *iq;
    if(purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND) {
    JabberBuddy *jb;
    @@ -1621,30 +1585,22 @@
    PurpleXfer *jabber_si_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
    {
    JabberStream *js;
    -
    - PurpleXfer *xfer;
    JabberSIXfer *jsx;
    js = purple_connection_get_protocol_data(gc);
    - xfer = purple_xfer_new(purple_connection_get_account(gc), PURPLE_XFER_TYPE_SEND, who);
    - if (xfer)
    - {
    - jsx = g_new0(JabberSIXfer, 1);
    - purple_xfer_set_protocol_data(xfer, jsx);
    - jsx->js = js;
    - jsx->local_streamhost_fd = -1;
    + jsx = g_object_new(
    + JABBER_TYPE_SI_XFER,
    + "account", purple_connection_get_account(gc),
    + "type", PURPLE_XFER_TYPE_SEND,
    + "remote-user", who,
    + NULL
    + );
    - jsx->ibb_session = NULL;
    + jsx->js = js;
    + js->file_transfers = g_list_append(js->file_transfers, jsx);
    - purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
    - purple_xfer_set_cancel_send_fnc(xfer, jabber_si_xfer_cancel_send);
    - purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
    -
    - js->file_transfers = g_list_append(js->file_transfers, xfer);
    - }
    -
    - return xfer;
    + return PURPLE_XFER(jsx);
    }
    void jabber_si_xfer_send(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file)
    @@ -1680,7 +1636,6 @@
    const char *id, PurpleXmlNode *si)
    {
    JabberSIXfer *jsx;
    - PurpleXfer *xfer;
    PurpleXmlNode *file, *feature, *x, *field, *option, *value;
    #if ENABLE_FT_THUMBNAILS
    PurpleXmlNode *thumbnail;
    @@ -1719,10 +1674,13 @@
    if(jabber_si_xfer_find(js, stream_id, from) != NULL)
    return;
    - jsx = g_new0(JabberSIXfer, 1);
    - jsx->local_streamhost_fd = -1;
    -
    - jsx->ibb_session = NULL;
    + jsx = g_object_new(
    + JABBER_TYPE_SI_XFER,
    + "account", purple_connection_get_account(js->gc),
    + "type", PURPLE_XFER_TYPE_RECEIVE,
    + "remote-user", from,
    + NULL
    + );
    for(field = purple_xmlnode_get_child(x, "field"); field; field = purple_xmlnode_get_next_twin(field)) {
    const char *var = purple_xmlnode_get_attrib(field, "var");
    @@ -1745,7 +1703,7 @@
    }
    if(jsx->stream_method == STREAM_METHOD_UNKNOWN) {
    - g_free(jsx);
    + g_object_unref(G_OBJECT(jsx));
    return;
    }
    @@ -1753,21 +1711,12 @@
    jsx->stream_id = g_strdup(stream_id);
    jsx->iq_id = g_strdup(id);
    - xfer = purple_xfer_new(purple_connection_get_account(js->gc), PURPLE_XFER_TYPE_RECEIVE, from);
    - g_return_if_fail(xfer != NULL);
    -
    - purple_xfer_set_protocol_data(xfer, jsx);
    + purple_xfer_set_filename(PURPLE_XFER(jsx), filename);
    + if(filesize > 0) {
    + purple_xfer_set_size(PURPLE_XFER(jsx), filesize);
    + }
    - purple_xfer_set_filename(xfer, filename);
    - if(filesize > 0)
    - purple_xfer_set_size(xfer, filesize);
    -
    - purple_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
    - purple_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
    - purple_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv);
    - purple_xfer_set_end_fnc(xfer, jabber_si_xfer_end);
    -
    - js->file_transfers = g_list_append(js->file_transfers, xfer);
    + js->file_transfers = g_list_append(js->file_transfers, jsx);
    #if ENABLE_FT_THUMBNAILS
    /* if there is a thumbnail, we should request it... */
    @@ -1775,27 +1724,119 @@
    NS_THUMBS))) {
    const char *cid = purple_xmlnode_get_attrib(thumbnail, "cid");
    if (cid) {
    - jabber_data_request(js, cid, purple_xfer_get_remote_user(xfer),
    - NULL, TRUE, jabber_si_thumbnail_cb, xfer);
    + jabber_data_request(js, cid, purple_xfer_get_remote_user(PURPLE_XFER(jsx)),
    + NULL, TRUE, jabber_si_thumbnail_cb, jsx);
    return;
    }
    }
    #endif
    - purple_xfer_request(xfer);
    + purple_xfer_request(PURPLE_XFER(jsx));
    +}
    +
    +/******************************************************************************
    + * GObject Implementation
    + *****************************************************************************/
    +static void
    +jabber_si_xfer_init(JabberSIXfer *xfer) {
    + xfer->local_streamhost_fd = -1;
    + xfer->ibb_session = NULL;
    +}
    +
    +static void
    +jabber_si_xfer_finalize(GObject *obj) {
    + JabberSIXfer *jsx = JABBER_SI_XFER(obj);
    + JabberStream *js = jsx->js;
    +
    + js->file_transfers = g_list_remove(js->file_transfers, jsx);
    +
    + if (jsx->connect_data != NULL) {
    + purple_proxy_connect_cancel(jsx->connect_data);
    + }
    +
    + if (jsx->listen_data != NULL) {
    + purple_network_listen_cancel(jsx->listen_data);
    + }
    +
    + if (jsx->iq_id != NULL) {
    + jabber_iq_remove_callback_by_id(js, jsx->iq_id);
    + }
    +
    + if (jsx->local_streamhost_fd >= 0) {
    + close(jsx->local_streamhost_fd);
    + }
    +
    + if (purple_xfer_get_xfer_type(PURPLE_XFER(jsx)) == PURPLE_XFER_TYPE_SEND && purple_xfer_get_fd(PURPLE_XFER(jsx)) >= 0) {
    + purple_debug_info("jabber", "remove port mapping\n");
    + purple_network_remove_port_mapping(purple_xfer_get_fd(PURPLE_XFER(jsx)));
    + }
    +
    + if (jsx->connect_timeout > 0) {
    + g_source_remove(jsx->connect_timeout);
    + }
    +
    + if (jsx->ibb_timeout_handle > 0) {
    + g_source_remove(jsx->ibb_timeout_handle);
    + }
    +
    + if (jsx->streamhosts) {
    + g_list_free_full(jsx->streamhosts, jabber_si_free_streamhost);
    + }
    +
    + if (jsx->ibb_session) {
    + purple_debug_info("jabber",
    + "jabber_si_xfer_free: destroying IBB session\n");
    + jabber_ibb_session_destroy(jsx->ibb_session);
    + }
    +
    + if (jsx->ibb_buffer) {
    + g_object_unref(G_OBJECT(jsx->ibb_buffer));
    + }
    +
    + purple_debug_info("jabber", "jabber_si_xfer_free(): freeing jsx %p\n", jsx);
    +
    + g_free(jsx->stream_id);
    + g_free(jsx->iq_id);
    + g_free(jsx->rxqueue);
    +
    + G_OBJECT_CLASS(jabber_si_xfer_parent_class)->finalize(obj);
    +}
    +
    +static void
    +jabber_si_xfer_class_finalize(JabberSIXferClass *klass) {
    +}
    +
    +static void
    +jabber_si_xfer_class_init(JabberSIXferClass *klass) {
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    +
    + obj_class->finalize = jabber_si_xfer_finalize;
    +
    + xfer_class->init = jabber_si_xfer_xfer_init;
    + xfer_class->request_denied = jabber_si_xfer_request_denied;
    + xfer_class->cancel_send = jabber_si_xfer_cancel_send;
    + xfer_class->cancel_recv = jabber_si_xfer_cancel_recv;
    + xfer_class->read = jabber_si_xfer_ibb_read;
    + xfer_class->write = jabber_si_xfer_ibb_write;
    +}
    +
    +/******************************************************************************
    + * Public API
    + *****************************************************************************/
    +void
    +jabber_si_xfer_register(GTypeModule *module) {
    + jabber_si_xfer_register_type(module);
    }
    void
    -jabber_si_init(void)
    -{
    +jabber_si_init(void) {
    jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse);
    jabber_ibb_register_open_handler(jabber_si_xfer_ibb_open_cb);
    }
    void
    -jabber_si_uninit(void)
    -{
    +jabber_si_uninit(void) {
    jabber_ibb_unregister_open_handler(jabber_si_xfer_ibb_open_cb);
    }
    -
    --- a/libpurple/protocols/jabber/si.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/si.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,20 +21,31 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_SI_H_
    -#define PURPLE_JABBER_SI_H_
    +
    +#ifndef PURPLE_JABBER_SI_H
    +#define PURPLE_JABBER_SI_H
    #include "xfer.h"
    #include "jabber.h"
    +G_BEGIN_DECLS
    +
    +#define JABBER_TYPE_SI_XFER (jabber_si_xfer_get_type())
    +G_DECLARE_FINAL_TYPE(JabberSIXfer, jabber_si_xfer, JABBER, SI_XFER, PurpleXfer);
    +
    void jabber_bytestreams_parse(JabberStream *js, const char *from,
    JabberIqType type, const char *id, PurpleXmlNode *query);
    void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
    const char *id, PurpleXmlNode *si);
    PurpleXfer *jabber_si_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who);
    void jabber_si_xfer_send(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who, const char *file);
    +
    +void jabber_si_xfer_register(GTypeModule *module);
    +
    void jabber_si_init(void);
    void jabber_si_uninit(void);
    -#endif /* PURPLE_JABBER_SI_H_ */
    +G_END_DECLS
    +
    +#endif /* PURPLE_JABBER_SI_H */
    --- a/libpurple/protocols/jabber/useravatar.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/useravatar.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef _PURPLE_JABBER_USERAVATAR_H_
    -#define _PURPLE_JABBER_USERAVATAR_H_
    +#ifndef PURPLE_JABBER_USERAVATAR_H
    +#define PURPLE_JABBER_USERAVATAR_H
    #include "jabber.h"
    #include "image.h"
    @@ -34,4 +34,4 @@
    void jabber_avatar_fetch_mine(JabberStream *js);
    -#endif /* _PURPLE_JABBER_USERAVATAR_H_ */
    +#endif /* PURPLE_JABBER_USERAVATAR_H */
    --- a/libpurple/protocols/jabber/usermood.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/usermood.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_USERMOOD_H_
    -#define PURPLE_JABBER_USERMOOD_H_
    +#ifndef PURPLE_JABBER_USERMOOD_H
    +#define PURPLE_JABBER_USERMOOD_H
    #include "jabber.h"
    @@ -47,4 +47,4 @@
    PurpleMood *jabber_get_moods(PurpleAccount *account);
    -#endif /* PURPLE_JABBER_USERMOOD_H_ */
    +#endif /* PURPLE_JABBER_USERMOOD_H */
    --- a/libpurple/protocols/jabber/usernick.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/usernick.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_USERNICK_H_
    -#define PURPLE_JABBER_USERNICK_H_
    +#ifndef PURPLE_JABBER_USERNICK_H
    +#define PURPLE_JABBER_USERNICK_H
    #include "jabber.h"
    @@ -31,4 +31,4 @@
    void jabber_nick_init(void);
    void jabber_nick_init_action(GList **m);
    -#endif /* PURPLE_JABBER_USERNICK_H_ */
    +#endif /* PURPLE_JABBER_USERNICK_H */
    --- a/libpurple/protocols/jabber/usertune.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/usertune.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef PURPLE_JABBER_USERTUNE_H_
    -#define PURPLE_JABBER_USERTUNE_H_
    +#ifndef PURPLE_JABBER_USERTUNE_H
    +#define PURPLE_JABBER_USERTUNE_H
    #include "jabber.h"
    @@ -42,4 +42,4 @@
    void jabber_tune_set(PurpleConnection *gc, const PurpleJabberTuneInfo *tuneinfo);
    -#endif /* PURPLE_JABBER_USERTUNE_H_ */
    +#endif /* PURPLE_JABBER_USERTUNE_H */
    --- a/libpurple/protocols/jabber/xdata.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/xdata.c Tue Oct 08 21:48:28 2019 -0500
    @@ -88,8 +88,7 @@
    fieldnode = purple_xmlnode_new_child(result, "field");
    purple_xmlnode_set_attrib(fieldnode, "var", id);
    valuenode = purple_xmlnode_new_child(fieldnode, "value");
    - if(value)
    - purple_xmlnode_insert_data(valuenode, value, -1);
    + purple_xmlnode_insert_data(valuenode, value, -1);
    break;
    }
    case JABBER_X_DATA_TEXT_MULTI:
    --- a/libpurple/protocols/jabber/xdata.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/xdata.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,9 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef PURPLE_JABBER_XDATA_H_
    -#define PURPLE_JABBER_XDATA_H_
    +
    +#ifndef PURPLE_JABBER_XDATA_H
    +#define PURPLE_JABBER_XDATA_H
    #include "jabber.h"
    #include "xmlnode.h"
    @@ -52,4 +53,4 @@
    */
    gchar *jabber_x_data_get_formtype(const PurpleXmlNode *form);
    -#endif /* PURPLE_JABBER_XDATA_H_ */
    +#endif /* PURPLE_JABBER_XDATA_H */
    --- a/libpurple/protocols/jabber/xmpp.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/xmpp.c Tue Oct 08 21:48:28 2019 -0500
    @@ -28,8 +28,9 @@
    #include "xmpp.h"
    static void
    -xmpp_protocol_init(PurpleProtocol *protocol)
    +xmpp_protocol_init(XMPPProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountUserSplit *split;
    PurpleAccountOption *option;
    GList *encryption_values = NULL;
    @@ -98,8 +99,21 @@
    }
    static void
    -xmpp_protocol_class_init(PurpleProtocolClass *klass)
    +xmpp_protocol_class_init(G_GNUC_UNUSED XMPPProtocolClass *klass)
    +{
    +}
    +
    +static void
    +xmpp_protocol_class_finalize(G_GNUC_UNUSED XMPPProtocolClass *klass)
    {
    }
    -PURPLE_DEFINE_TYPE(XMPPProtocol, xmpp_protocol, JABBER_TYPE_PROTOCOL);
    +G_DEFINE_DYNAMIC_TYPE(XMPPProtocol, xmpp_protocol, JABBER_TYPE_PROTOCOL);
    +
    +/* This exists solely because the above macro makes xmpp_protocol_register_type
    + * static. */
    +void
    +xmpp_protocol_register(PurplePlugin *plugin)
    +{
    + xmpp_protocol_register_type(G_TYPE_MODULE(plugin));
    +}
    --- a/libpurple/protocols/jabber/xmpp.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/jabber/xmpp.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _XMPP_H_
    -#define _XMPP_H_
    +
    +#ifndef PURPLE_JABBER_XMPP_H
    +#define PURPLE_JABBER_XMPP_H
    #include "jabber.h"
    @@ -44,11 +45,12 @@
    /**
    * Registers the XMPPProtocol type in the type system.
    */
    -void xmpp_protocol_register_type(PurplePlugin *plugin);
    +G_GNUC_INTERNAL
    +void xmpp_protocol_register(PurplePlugin *plugin);
    /**
    * Returns the GType for the XMPPProtocol object.
    */
    G_MODULE_EXPORT GType xmpp_protocol_get_type(void);
    -#endif /* _XMPP_H_ */
    +#endif /* PURPLE_JABBER_XMPP_H */
    --- a/libpurple/protocols/novell/nmconference.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmconference.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_CONFERENCE_H__
    -#define __NM_CONFERENCE_H__
    +#ifndef PURPLE_NOVELL_NMCONFERENCE_H
    +#define PURPLE_NOVELL_NMCONFERENCE_H
    typedef struct _NMConference NMConference;
    @@ -164,4 +164,4 @@
    */
    gpointer nm_conference_get_data(NMConference * conference);
    -#endif
    +#endif /* PURPLE_NOVELL_NMCONFERENCE_H */
    --- a/libpurple/protocols/novell/nmconn.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmconn.c Tue Oct 08 21:48:28 2019 -0500
    @@ -628,8 +628,7 @@
    0, val, type);
    }
    - } while ((type != 0) && (count != 0));
    -
    + } while (count != 0);
    g_free(str);
    --- a/libpurple/protocols/novell/nmconn.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmconn.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_CONN_H__
    -#define __NM_CONN_H__
    +#ifndef PURPLE_NOVELL_NMCONN_H
    +#define PURPLE_NOVELL_NMCONN_H
    typedef struct _NMConn NMConn;
    typedef struct _NMSSLConn NMSSLConn;
    @@ -243,4 +243,4 @@
    */
    int nm_conn_get_port(NMConn * conn);
    -#endif
    +#endif /* PURPLE_NOVELL_NMCONN_H */
    --- a/libpurple/protocols/novell/nmcontact.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmcontact.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_CONTACT_H__
    -#define __NM_CONTACT_H__
    +#ifndef PURPLE_NOVELL_NMCONTACT_H
    +#define PURPLE_NOVELL_NMCONTACT_H
    #include <glib.h>
    @@ -422,4 +422,4 @@
    */
    NMField *nm_folder_to_fields(NMFolder * folder);
    -#endif
    +#endif /* PURPLE_NOVELL_NMCONTACT_H */
    --- a/libpurple/protocols/novell/nmevent.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmevent.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_EVENT_H__
    -#define __NM_EVENT_H__
    +#ifndef PURPLE_NOVELL_NMEVENT_H
    +#define PURPLE_NOVELL_NMEVENT_H
    typedef struct _NMEvent NMEvent;
    @@ -175,4 +175,4 @@
    */
    time_t nm_event_get_gmt(NMEvent * event);
    -#endif
    +#endif /* PURPLE_NOVELL_NMEVENT_H */
    --- a/libpurple/protocols/novell/nmfield.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmfield.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,13 +18,12 @@
    *
    */
    -#ifndef NMFIELD_H
    -#define NMFIELD_H
    +#ifndef PURPLE_NOVELL_NMFIELD_H
    +#define PURPLE_NOVELL_NMFIELD_H
    #include <glib.h>
    -typedef struct NMField_t
    -{
    +typedef struct {
    char *tag; /* Field tag */
    guint8 method; /* Method of the field */
    guint8 flags; /* Flags */
    @@ -222,4 +221,4 @@
    /* Print a field array (for debugging purposes) */
    void nm_print_fields(NMField * fields);
    -#endif
    +#endif /* PURPLE_NOVELL_NMFIELD_H */
    --- a/libpurple/protocols/novell/nmmessage.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmmessage.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_MESSAGE_H__
    -#define __NM_MESSAGE_H__
    +#ifndef PURPLE_NOVELL_NMMESSAGE_H
    +#define PURPLE_NOVELL_NMMESSAGE_H
    typedef struct _NMMessage NMMessage;
    @@ -80,4 +80,4 @@
    */
    NMConference *nm_message_get_conference(NMMessage * msg);
    -#endif
    +#endif /* PURPLE_NOVELL_NMMESSAGE_H */
    --- a/libpurple/protocols/novell/nmrequest.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmrequest.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_REQUEST_H__
    -#define __NM_REQUEST_H__
    +#ifndef PURPLE_NOVELL_NMREQUEST_H
    +#define PURPLE_NOVELL_NMREQUEST_H
    typedef struct _NMRequest NMRequest;
    @@ -147,4 +147,4 @@
    */
    NMERR_T nm_request_get_ret_code(NMRequest * req);
    -#endif
    +#endif /* PURPLE_NOVELL_NMREQUEST_H */
    --- a/libpurple/protocols/novell/nmrtf.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmrtf.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NMRTF_H__
    -#define __NMRTF_H__
    +#ifndef PURPLE_NOVELL_NMRTF_H
    +#define PURPLE_NOVELL_NMRTF_H
    typedef struct _NMRtfContext NMRtfContext;
    @@ -27,4 +27,4 @@
    char *nm_rtf_strip_formatting(NMRtfContext *ctx, const char *input);
    void nm_rtf_deinit(NMRtfContext *ctx);
    -#endif
    +#endif /* PURPLE_NOVELL_NMRTF_H */
    --- a/libpurple/protocols/novell/nmuser.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmuser.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1614,7 +1614,7 @@
    field = (NMField *) locate->ptr_value;
    while ((field = nm_locate_field(NM_A_SZ_DN, field))) {
    - if (field && field->ptr_value != 0) {
    + if (field->ptr_value != 0) {
    if (nm_utf8_str_equal
    (nm_user_record_get_dn(user->user_record),
    @@ -1813,7 +1813,7 @@
    if (typed == NULL)
    return NULL;
    - dotted = g_new0(char, strlen(typed));
    + dotted = g_new0(char, strlen(typed) + 1);
    do {
    @@ -2022,22 +2022,15 @@
    /* Not found, so we need to add it */
    if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) {
    - const char *dn = NULL;
    -
    locate =
    nm_locate_field(NM_A_SZ_DN,
    (NMField *) cursor->ptr_value);
    - if (locate != NULL && locate->ptr_value != 0) {
    - dn = (const char *) locate->ptr_value;
    - if (dn != NULL) {
    - contact =
    - nm_create_contact_from_fields(cursor);
    - if (contact) {
    - nm_folder_add_contact_to_list(user->
    - root_folder,
    - contact);
    - nm_release_contact(contact);
    - }
    + if (locate != NULL && locate->ptr_value != NULL) {
    + contact = nm_create_contact_from_fields(cursor);
    + if (contact) {
    + nm_folder_add_contact_to_list(
    + user->root_folder, contact);
    + nm_release_contact(contact);
    }
    }
    } else if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER)
    --- a/libpurple/protocols/novell/nmuser.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmuser.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_USER_H__
    -#define __NM_USER_H__
    +#ifndef PURPLE_NOVELL_NMUSER_H
    +#define PURPLE_NOVELL_NMUSER_H
    #include <glib.h>
    #include <stdio.h>
    @@ -677,4 +677,4 @@
    */
    const char *nm_error_to_string (NMERR_T err);
    -#endif
    +#endif /* PURPLE_NOVELL_NMUSER_H */
    --- a/libpurple/protocols/novell/nmuserrecord.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmuserrecord.c Tue Oct 08 21:48:28 2019 -0500
    @@ -81,12 +81,8 @@
    * just pick the first value and return it
    */
    NMField *tmp = (NMField *)field->ptr_value;
    - if ((tmp != NULL) &&
    - ((tmp->type == NMFIELD_TYPE_UTF8) ||
    - (tmp->type == NMFIELD_TYPE_DN))) {
    -
    + if (tmp->type == NMFIELD_TYPE_UTF8 || tmp->type == NMFIELD_TYPE_DN) {
    value = (char *)tmp->ptr_value;
    -
    } else {
    return NULL;
    }
    @@ -474,14 +470,12 @@
    int max = nm_count_fields(fields);
    if (index < max) {
    - if (user_record) {
    field = &fields[index];
    if (field && field->tag && field->ptr_value) {
    property = g_new0(NMProperty, 1);
    property->tag = g_strdup(field->tag);
    property->value = _get_attribute_value(field);
    }
    - }
    }
    }
    }
    --- a/libpurple/protocols/novell/nmuserrecord.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/nmuserrecord.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    *
    */
    -#ifndef __NM_USER_RECORD_H__
    -#define __NM_USER_RECORD_H__
    +#ifndef PURPLE_NOVELL_NMUSERRECORD_H
    +#define PURPLE_NOVELL_NMUSERRECORD_H
    #include <glib.h>
    @@ -270,4 +270,4 @@
    */
    void nm_user_record_copy(NMUserRecord * dest, NMUserRecord * src);
    -#endif
    +#endif /* PURPLE_NOVELL_NMUSERRECORD_H */
    --- a/libpurple/protocols/novell/novell.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/novell.c Tue Oct 08 21:48:28 2019 -0500
    @@ -262,8 +262,7 @@
    g_free(err);
    }
    - if (msg)
    - nm_release_message(msg);
    + nm_release_message(msg);
    }
    }
    @@ -316,8 +315,7 @@
    }
    - if (contact)
    - nm_release_contact(contact);
    + nm_release_contact(contact);
    }
    /* Add the new contact into the PurpleBuddy list */
    @@ -353,8 +351,8 @@
    folder_name = NM_ROOT_FOLDER_NAME;
    /* Re-add the buddy now that we got the okay from the server */
    - if (folder_name && (group = purple_blist_find_group(folder_name))) {
    -
    + group = purple_blist_find_group(folder_name);
    + if (group) {
    const char *alias = nm_contact_get_display_name(tmp_contact);
    const char *display_id = nm_contact_get_display_id(new_contact);
    @@ -394,7 +392,6 @@
    rc = nm_send_get_details(user, nm_contact_get_dn(new_contact),
    _get_details_resp_setup_buddy, new_contact);
    _check_for_disconnect(user, rc);
    -
    }
    } else {
    @@ -556,8 +553,7 @@
    g_free(err);
    }
    - if (msg)
    - nm_release_message(msg);
    + nm_release_message(msg);
    }
    }
    @@ -807,8 +803,8 @@
    } else {
    rc = nm_send_get_details(user, who,
    - _get_details_resp_add_privacy_item,
    - (gpointer)FALSE);
    + _get_details_resp_add_privacy_item,
    + GINT_TO_POINTER(FALSE));
    _check_for_disconnect(user, rc);
    }
    } else {
    @@ -861,8 +857,8 @@
    } else {
    rc = nm_send_get_details(user, who,
    - _get_details_resp_add_privacy_item,
    - (gpointer)TRUE);
    + _get_details_resp_add_privacy_item,
    + GINT_TO_POINTER(TRUE));
    _check_for_disconnect(user, rc);
    }
    @@ -1250,8 +1246,8 @@
    NMFolder *folder = NULL;
    const char *gname = NULL;
    - for (gnode = purple_blist_get_root(); gnode;
    - gnode = purple_blist_node_get_sibling_next(gnode)) {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = purple_blist_node_get_sibling_next(gnode)) {
    if (!PURPLE_IS_GROUP(gnode))
    continue;
    group = (PurpleGroup *) gnode;
    @@ -3106,8 +3102,9 @@
    if (strchr(who, '.')) {
    const char *dn = nm_lookup_dn(user, who);
    if (dn == NULL) {
    - rc = nm_send_get_details(user, who, _get_details_send_privacy_create,
    - (gpointer)TRUE);
    + rc = nm_send_get_details(user, who,
    + _get_details_send_privacy_create,
    + GINT_TO_POINTER(TRUE));
    _check_for_disconnect(user, rc);
    return;
    } else {
    @@ -3150,8 +3147,9 @@
    if (strchr(who, '.')) {
    const char *dn = nm_lookup_dn(user, who);
    if (dn == NULL) {
    - rc = nm_send_get_details(user, who, _get_details_send_privacy_create,
    - (gpointer)FALSE);
    + rc = nm_send_get_details(user, who,
    + _get_details_send_privacy_create,
    + GINT_TO_POINTER(FALSE));
    _check_for_disconnect(user, rc);
    return;
    } else {
    @@ -3486,8 +3484,9 @@
    }
    static void
    -novell_protocol_init(PurpleProtocol *protocol)
    +novell_protocol_init(NovellProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountOption *option;
    protocol->id = "prpl-novell";
    @@ -3503,16 +3502,23 @@
    }
    static void
    -novell_protocol_class_init(PurpleProtocolClass *klass)
    +novell_protocol_class_init(NovellProtocolClass *klass)
    {
    - klass->login = novell_login;
    - klass->close = novell_close;
    - klass->status_types = novell_status_types;
    - klass->list_icon = novell_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = novell_login;
    + protocol_class->close = novell_close;
    + protocol_class->status_types = novell_status_types;
    + protocol_class->list_icon = novell_list_icon;
    }
    static void
    -novell_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +novell_protocol_class_finalize(G_GNUC_UNUSED NovellProtocolClass *klass)
    +{
    +}
    +
    +static void
    +novell_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->status_text = novell_status_text;
    client_iface->tooltip_text = novell_tooltip_text;
    @@ -3523,7 +3529,7 @@
    }
    static void
    -novell_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +novell_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->get_info = novell_get_info;
    server_iface->set_status = novell_set_status;
    @@ -3538,14 +3544,14 @@
    }
    static void
    -novell_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +novell_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = novell_send_im;
    im_iface->send_typing = novell_send_typing;
    }
    static void
    -novell_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +novell_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->invite = novell_chat_invite;
    chat_iface->leave = novell_chat_leave;
    @@ -3553,7 +3559,7 @@
    }
    static void
    -novell_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    +novell_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    {
    privacy_iface->add_permit = novell_add_permit;
    privacy_iface->add_deny = novell_add_deny;
    @@ -3562,24 +3568,23 @@
    privacy_iface->set_permit_deny = novell_set_permit_deny;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - NovellProtocol, novell_protocol, PURPLE_TYPE_PROTOCOL, 0,
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - novell_protocol_client_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - novell_protocol_server_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - novell_protocol_im_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - novell_protocol_chat_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - novell_protocol_privacy_iface_init)
    -);
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + NovellProtocol, novell_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + novell_protocol_client_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + novell_protocol_server_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + novell_protocol_im_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + novell_protocol_chat_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    + novell_protocol_privacy_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -3602,7 +3607,7 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - novell_protocol_register_type(plugin);
    + novell_protocol_register_type(G_TYPE_MODULE(plugin));
    my_protocol = purple_protocols_add(NOVELL_TYPE_PROTOCOL, error);
    if (!my_protocol)
    --- a/libpurple/protocols/novell/novell.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/novell/novell.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _NOVELL_H_
    -#define _NOVELL_H_
    +
    +#ifndef PURPLE_NOVELL_NOVELL_H
    +#define PURPLE_NOVELL_NOVELL_H
    #include <gmodule.h>
    @@ -48,4 +49,4 @@
    */
    G_MODULE_EXPORT GType novell_protocol_get_type(void);
    -#endif /* _NOVELL_H_ */
    +#endif /* PURPLE_NOVELL_NOVELL_H */
    --- a/libpurple/protocols/null/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/null/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -7,5 +7,5 @@
    if DYNAMIC_NULL
    null_prpl = shared_library('null', NULLSOURCES,
    dependencies : [libpurple_dep, glib],
    - install : true, install_dir : PURPLE_PLUGINDIR)
    + install : false, install_dir : PURPLE_PLUGINDIR)
    endif
    --- a/libpurple/protocols/null/nullprpl.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/null/nullprpl.c Tue Oct 08 21:48:28 2019 -0500
    @@ -51,23 +51,10 @@
    * it with code to include your own config.h or similar. If you're going to
    * provide for translation, you'll also need to setup the gettext macros. */
    #include "internal.h"
    +#include <purple.h>
    #include "nullprpl.h"
    -#include "account.h"
    -#include "accountopt.h"
    -#include "buddylist.h"
    -#include "cmds.h"
    -#include "conversation.h"
    -#include "connection.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "plugins.h"
    -#include "roomlist.h"
    -#include "status.h"
    -#include "util.h"
    -#include "version.h"
    -
    /*
    * reference to the protocol instance, used for registering signals, prefs,
    * etc. it is set when the protocol is added in plugin_load and is required
    @@ -367,6 +354,7 @@
    {
    PurpleConnection *gc = purple_account_get_connection(acct);
    GList *offline_messages;
    + GList *all_offline_messages;
    purple_debug_info("nullprpl", "logging in %s\n", purple_account_get_username(acct));
    @@ -390,7 +378,8 @@
    /* fetch stored offline messages */
    purple_debug_info("nullprpl", "checking for offline messages for %s\n",
    purple_account_get_username(acct));
    - offline_messages = g_hash_table_lookup(goffline_messages, purple_account_get_username(acct));
    + all_offline_messages = offline_messages = g_hash_table_lookup(
    + goffline_messages, purple_account_get_username(acct));
    while (offline_messages) {
    GOfflineMessage *message = (GOfflineMessage *)offline_messages->data;
    purple_debug_info("nullprpl", "delivering offline message to %s: %s\n",
    @@ -404,8 +393,8 @@
    g_free(message);
    }
    - g_list_free(offline_messages);
    g_hash_table_remove(goffline_messages, purple_account_get_username(acct));
    + g_list_free(all_offline_messages);
    }
    static void null_close(PurpleConnection *gc)
    @@ -855,10 +844,10 @@
    }
    static void null_set_buddy_icon(PurpleConnection *gc,
    - PurpleStoredImage *img) {
    + PurpleImage *img) {
    purple_debug_info("nullprpl", "setting %s's buddy icon to %s\n",
    purple_account_get_username(purple_connection_get_account(gc)),
    - img ? purple_imgstore_get_filename(img) : "(null)");
    + img ? purple_image_get_path(img) : "(null)");
    }
    static void null_remove_group(PurpleConnection *gc, PurpleGroup *group) {
    @@ -975,10 +964,13 @@
    purple_roomlist_room_get_name(category));
    }
    +/* PurpleClientIface->offline_message takes a const PurpleBuddy *, this needs
    + * to change, but didn't want to do that in this pull request.
    + */
    static gboolean null_offline_message(const PurpleBuddy *buddy) {
    purple_debug_info("nullprpl",
    "reporting that offline messages are supported for %s\n",
    - purple_buddy_get_name(buddy));
    + purple_buddy_get_name((PurpleBuddy *)buddy));
    return TRUE;
    }
    @@ -986,36 +978,38 @@
    * Initialize the protocol instance. see protocol.h for more information.
    */
    static void
    -null_protocol_init(PurpleProtocol *protocol)
    +null_protocol_init(NullProtocol *self)
    {
    - PurpleAccountUserSplit *split;
    - PurpleAccountOption *option;
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    + PurpleAccountUserSplit *split;
    + PurpleAccountOption *option;
    - protocol->id = "prpl-null";
    - protocol->name = "Null - Testing Protocol";
    - protocol->options = OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC;
    - protocol->icon_spec = purple_buddy_icon_spec_new(
    - "png,jpg,gif", /* format */
    - 0, /* min_width */
    - 0, /* min_height */
    - 128, /* max_width */
    - 128, /* max_height */
    - 10000, /* max_filesize */
    - PURPLE_ICON_SCALE_DISPLAY /* scale_rules */
    - );
    + protocol->id = "prpl-null";
    + protocol->name = "Null - Testing Protocol";
    + protocol->options = OPT_PROTO_NO_PASSWORD | OPT_PROTO_CHAT_TOPIC;
    + protocol->icon_spec = purple_buddy_icon_spec_new(
    + "png,jpg,gif", /* format */
    + 0, /* min_width */
    + 0, /* min_height */
    + 128, /* max_width */
    + 128, /* max_height */
    + 10000, /* max_filesize */
    + PURPLE_ICON_SCALE_DISPLAY /* scale_rules */
    + );
    - /* see accountopt.h for information about user splits and protocol options */
    - split = purple_account_user_split_new(
    - _("Example user split"), /* text shown to user */
    - "default", /* default value */
    - '@'); /* field separator */
    - option = purple_account_option_string_new(
    - _("Example option"), /* text shown to user */
    - "example", /* pref name */
    - "default"); /* default value */
    + /* see accountopt.h for information about user splits and protocol
    + * options */
    + split = purple_account_user_split_new(
    + _("Example user split"), /* text shown to user */
    + "default", /* default value */
    + '@'); /* field separator */
    + option = purple_account_option_string_new(
    + _("Example option"), /* text shown to user */
    + "example", /* pref name */
    + "default"); /* default value */
    - protocol->user_splits = g_list_append(NULL, split);
    - protocol->account_options = g_list_append(NULL, option);
    + protocol->user_splits = g_list_append(NULL, split);
    + protocol->account_options = g_list_append(NULL, option);
    }
    /*
    @@ -1024,16 +1018,23 @@
    */
    static void
    -null_protocol_class_init(PurpleProtocolClass *klass)
    +null_protocol_class_init(NullProtocolClass *klass)
    {
    - klass->login = null_login;
    - klass->close = null_close;
    - klass->status_types = null_status_types;
    - klass->list_icon = null_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = null_login;
    + protocol_class->close = null_close;
    + protocol_class->status_types = null_status_types;
    + protocol_class->list_icon = null_list_icon;
    }
    static void
    -null_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +null_protocol_class_finalize(G_GNUC_UNUSED NullProtocolClass *klass)
    +{
    +}
    +
    +static void
    +null_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = null_get_actions;
    client_iface->status_text = null_status_text;
    @@ -1045,7 +1046,7 @@
    }
    static void
    -null_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +null_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->register_user = null_register_user;
    server_iface->set_info = null_set_info;
    @@ -1065,14 +1066,14 @@
    }
    static void
    -null_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +null_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = null_send_im;
    im_iface->send_typing = null_send_typing;
    }
    static void
    -null_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +null_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = null_chat_info;
    chat_iface->info_defaults = null_chat_info_defaults;
    @@ -1086,7 +1087,7 @@
    }
    static void
    -null_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    +null_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    {
    privacy_iface->add_permit = null_add_permit;
    privacy_iface->add_deny = null_add_deny;
    @@ -1096,7 +1097,7 @@
    }
    static void
    -null_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface)
    +null_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface)
    {
    roomlist_iface->get_list = null_roomlist_get_list;
    roomlist_iface->cancel = null_roomlist_cancel;
    @@ -1109,27 +1110,26 @@
    * to register this type with the type system, and null_protocol_get_type()
    * which returns the registered GType.
    */
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - NullProtocol, null_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + NullProtocol, null_protocol, PURPLE_TYPE_PROTOCOL, 0,
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - null_protocol_client_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + null_protocol_client_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - null_protocol_server_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + null_protocol_server_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - null_protocol_im_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + null_protocol_im_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - null_protocol_chat_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + null_protocol_chat_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - null_protocol_privacy_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    + null_protocol_privacy_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
    - null_protocol_roomlist_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
    + null_protocol_roomlist_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -1155,11 +1155,9 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - PurpleCmdId id;
    -
    /* register the NULL_TYPE_PROTOCOL type in the type system. this function
    - * is defined by PURPLE_DEFINE_TYPE_EXTENDED. */
    - null_protocol_register_type(plugin);
    + * is defined by G_DEFINE_DYNAMIC_TYPE_EXTENDED. */
    + null_protocol_register_type(G_TYPE_MODULE(plugin));
    /* add the protocol to the core */
    my_protocol = purple_protocols_add(NULL_TYPE_PROTOCOL, error);
    --- a/libpurple/protocols/null/nullprpl.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/null/nullprpl.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* purple
    +/*
    + * purple
    *
    * Purple is the legal property of its developers, whose names are too numerous
    * to list here. Please refer to the COPYRIGHT file distributed with this
    @@ -19,10 +20,11 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _NULL_H_
    -#define _NULL_H_
    -#include "protocol.h"
    +#ifndef PURPLE_NULL_NULLPRPL_H
    +#define PURPLE_NULL_NULLPRPL_H
    +
    +#include <purple.h>
    #define NULL_TYPE_PROTOCOL (null_protocol_get_type())
    #define NULL_PROTOCOL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NULL_TYPE_PROTOCOL, NullProtocol))
    @@ -46,4 +48,4 @@
    */
    G_MODULE_EXPORT GType null_protocol_get_type(void);
    -#endif /* _NULL_H_ */
    +#endif /* PURPLE_NULL_NULLPRPL_H */
    --- a/libpurple/protocols/oscar/aim.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/aim.c Tue Oct 08 21:48:28 2019 -0500
    @@ -29,8 +29,10 @@
    #include "oscarcommon.h"
    static void
    -aim_protocol_init(PurpleProtocol *protocol)
    +aim_protocol_init(AIMProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    +
    protocol->id = "prpl-aim";
    protocol->name = "AIM";
    @@ -38,31 +40,45 @@
    }
    static void
    -aim_protocol_class_init(PurpleProtocolClass *klass)
    +aim_protocol_class_init(AIMProtocolClass *klass)
    {
    - klass->list_icon = oscar_list_icon_aim;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->list_icon = oscar_list_icon_aim;
    }
    static void
    -aim_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +aim_protocol_class_finalize(G_GNUC_UNUSED AIMProtocolClass *klass)
    +{
    +}
    +
    +static void
    +aim_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_max_message_size = oscar_get_max_message_size;
    }
    static void
    -aim_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    +aim_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    {
    privacy_iface->add_permit = oscar_add_permit;
    privacy_iface->rem_permit = oscar_rem_permit;
    privacy_iface->set_permit_deny = oscar_set_aim_permdeny;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - AIMProtocol, aim_protocol, OSCAR_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + AIMProtocol, aim_protocol, OSCAR_TYPE_PROTOCOL, 0,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + aim_protocol_client_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - aim_protocol_client_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    + aim_protocol_privacy_iface_init));
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - aim_protocol_privacy_iface_init)
    -);
    +/* This exists solely because the above macro makes aim_protocol_register_type
    + * static. */
    +void
    +aim_protocol_register(PurplePlugin *plugin)
    +{
    + aim_protocol_register_type(G_TYPE_MODULE(plugin));
    +}
    --- a/libpurple/protocols/oscar/aim.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/aim.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _AIM_H_
    -#define _AIM_H_
    +
    +#ifndef PURPLE_OSCAR_AIM_H
    +#define PURPLE_OSCAR_AIM_H
    #include "oscar.h"
    @@ -44,11 +45,12 @@
    /**
    * Registers the AIMProtocol type in the type system.
    */
    -void aim_protocol_register_type(PurplePlugin *plugin);
    +G_GNUC_INTERNAL
    +void aim_protocol_register(PurplePlugin *plugin);
    /**
    * Returns the GType for the AIMProtocol object.
    */
    G_MODULE_EXPORT GType aim_protocol_get_type(void);
    -#endif /* _AIM_H_ */
    +#endif /* PURPLE_OSCAR_AIM_H */
    --- a/libpurple/protocols/oscar/encoding.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/encoding.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _ENCODING_H_
    -#define _ENCODING_H_
    +#ifndef PURPLE_OSCAR_ENCODING_H
    +#define PURPLE_OSCAR_ENCODING_H
    #include "oscar.h"
    #include "oscarcommon.h"
    @@ -43,4 +43,4 @@
    */
    gchar * oscar_encode_im(const gchar *msg, gsize *result_len, guint16 *charset, gchar **charsetstr);
    -#endif
    \ No newline at end of file
    +#endif /* PURPLE_OSCAR_ENCODING_H */
    --- a/libpurple/protocols/oscar/family_chatnav.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/family_chatnav.c Tue Oct 08 21:48:28 2019 -0500
    @@ -414,8 +414,7 @@
    else
    purple_debug_misc("oscar", "chatnav_parse_info: unknown request subtype (%04x)\n", snac2->type);
    - if (snac2)
    - g_free(snac2->data);
    + g_free(snac2->data);
    g_free(snac2);
    return ret;
    --- a/libpurple/protocols/oscar/family_feedbag.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/family_feedbag.c Tue Oct 08 21:48:28 2019 -0500
    @@ -567,7 +567,7 @@
    struct aim_ssi_item *cur1, *cur2;
    struct aim_ssi_tmp *cur, *new;
    int n = 0;
    - GString *debugstr = g_string_new("");
    + GString *debugstr;
    /*
    * The variable "n" is used to limit the number of addmoddel's that
    @@ -590,6 +590,7 @@
    * list should be in ascending numerical order for the group ID#s and the
    * buddy ID#s, which makes things more efficient. I think.
    */
    + debugstr = g_string_new("");
    /* Deletions */
    if (!od->ssi.pending) {
    --- a/libpurple/protocols/oscar/family_icbm.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/family_icbm.c Tue Oct 08 21:48:28 2019 -0500
    @@ -172,13 +172,13 @@
    if (!purple_conversation_present_error(bn, purple_connection_get_account(gc), buf)) {
    g_free(buf);
    - if (errcode != 0 && errcode < errcodereasonlen)
    + if (errcode != 0 && errcode < errcodereasonlen) {
    buf = g_strdup_printf(_("Unable to send message to %s: %s (%s)"),
    - bn ? bn : "(unknown)", reason_str,
    - _(errcodereason[errcode]));
    - else
    - buf = g_strdup_printf(_("Unable to send message to %s: %s"),
    - bn ? bn : "(unknown)", reason_str);
    + bn, reason_str, _(errcodereason[errcode]));
    + } else {
    + buf = g_strdup_printf(_("Unable to send message to %s: %s"), bn,
    + reason_str);
    + }
    purple_notify_error(od->gc, NULL, buf, reason_str,
    purple_request_cpar_from_connection(od->gc));
    }
    @@ -424,7 +424,7 @@
    snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, bn, strlen(bn)+1);
    /* XXX should be uncached by an unwritten 'invite accept' handler */
    - priv = g_malloc(sizeof(struct aim_invite_priv));
    + priv = g_new0(struct aim_invite_priv, 1);
    priv->bn = g_strdup(bn);
    priv->roomname = g_strdup(roomname);
    priv->exchange = exchange;
    --- a/libpurple/protocols/oscar/family_locate.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/family_locate.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1291,9 +1291,7 @@
    /* Build the packet first to get real length */
    if (profile) {
    - /* no + 1 here because of %s */
    - encoding = g_malloc(strlen(defencoding) + strlen(profile_encoding));
    - snprintf(encoding, strlen(defencoding) + strlen(profile_encoding), defencoding, profile_encoding);
    + encoding = g_strdup_printf(defencoding, profile_encoding);
    aim_tlvlist_add_str(&tlvlist, 0x0001, encoding);
    aim_tlvlist_add_raw(&tlvlist, 0x0002, profile_len, (const guchar *)profile);
    g_free(encoding);
    @@ -1309,8 +1307,7 @@
    */
    if (awaymsg) {
    if (awaymsg_len) {
    - encoding = g_malloc(strlen(defencoding) + strlen(awaymsg_encoding));
    - snprintf(encoding, strlen(defencoding) + strlen(awaymsg_encoding), defencoding, awaymsg_encoding);
    + encoding = g_strdup_printf(defencoding, awaymsg_encoding);
    aim_tlvlist_add_str(&tlvlist, 0x0003, encoding);
    aim_tlvlist_add_raw(&tlvlist, 0x0004, awaymsg_len, (const guchar *)awaymsg);
    g_free(encoding);
    @@ -1375,7 +1372,7 @@
    GSList *tlvlist;
    aim_tlv_t *tlv = NULL;
    - userinfo = (aim_userinfo_t *)g_malloc(sizeof(aim_userinfo_t));
    + userinfo = g_new0(aim_userinfo_t, 1);
    aim_info_extract(od, bs, userinfo);
    tlvlist = aim_tlvlist_read(bs);
    --- a/libpurple/protocols/oscar/family_userlookup.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/family_userlookup.c Tue Oct 08 21:48:28 2019 -0500
    @@ -48,8 +48,7 @@
    ret = userfunc(od, conn, frame, snac2->data /* address */);
    /* XXX freesnac()? */
    - if (snac2)
    - g_free(snac2->data);
    + g_free(snac2->data);
    g_free(snac2);
    return ret;
    --- a/libpurple/protocols/oscar/icq.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/icq.c Tue Oct 08 21:48:28 2019 -0500
    @@ -45,8 +45,10 @@
    }
    static void
    -icq_protocol_init(PurpleProtocol *protocol)
    +icq_protocol_init(ICQProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    +
    protocol->id = "prpl-icq";
    protocol->name = "ICQ";
    @@ -54,22 +56,36 @@
    }
    static void
    -icq_protocol_class_init(PurpleProtocolClass *klass)
    +icq_protocol_class_init(ICQProtocolClass *klass)
    {
    - klass->list_icon = oscar_list_icon_icq;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->list_icon = oscar_list_icon_icq;
    }
    static void
    -icq_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +icq_protocol_class_finalize(G_GNUC_UNUSED ICQProtocolClass *klass)
    +{
    +}
    +
    +static void
    +icq_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_account_text_table = icq_get_account_text_table;
    client_iface->get_moods = oscar_get_purple_moods;
    client_iface->get_max_message_size = icq_get_max_message_size;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - ICQProtocol, icq_protocol, OSCAR_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + ICQProtocol, icq_protocol, OSCAR_TYPE_PROTOCOL, 0,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + icq_protocol_client_iface_init));
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - icq_protocol_client_iface_init)
    -);
    +/* This exists solely because the above macro makes icq_protocol_register_type
    + * static. */
    +void
    +icq_protocol_register(PurplePlugin *plugin)
    +{
    + icq_protocol_register_type(G_TYPE_MODULE(plugin));
    +}
    --- a/libpurple/protocols/oscar/icq.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/icq.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _ICQ_H_
    -#define _ICQ_H_
    +
    +#ifndef PURPLE_OSCAR_ICQ_H
    +#define PURPLE_OSCAR_ICQ_H
    #include "oscar.h"
    @@ -44,11 +45,12 @@
    /**
    * Registers the ICQProtocol type in the type system.
    */
    -void icq_protocol_register_type(PurplePlugin *plugin);
    +G_GNUC_INTERNAL
    +void icq_protocol_register(PurplePlugin *plugin);
    /**
    * Returns the GType for the ICQProtocol object.
    */
    G_MODULE_EXPORT GType icq_protocol_get_type(void);
    -#endif /* _ICQ_H_ */
    +#endif /* PURPLE_OSCAR_ICQ_H */
    --- a/libpurple/protocols/oscar/odc.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/odc.c Tue Oct 08 21:48:28 2019 -0500
    @@ -595,8 +595,8 @@
    PurpleAccount *account;
    PurpleIMConversation *im;
    - size1 = purple_str_size_to_units(frame->payload.len);
    - size2 = purple_str_size_to_units(DIRECTIM_MAX_FILESIZE);
    + size1 = g_format_size(frame->payload.len);
    + size2 = g_format_size(DIRECTIM_MAX_FILESIZE);
    tmp = g_strdup_printf(_("%s tried to send you a %s file, but we only allow files up to %s over Direct IM. Try using file transfer instead.\n"), conn->bn, size1, size2);
    g_free(size1);
    g_free(size2);
    --- a/libpurple/protocols/oscar/oft.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/oft.c Tue Oct 08 21:48:28 2019 -0500
    @@ -589,7 +589,7 @@
    {
    PeerConnection *conn;
    - conn = purple_xfer_get_protocol_data(xfer);
    + conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    peer_connection_trynext(conn);
    }
    @@ -599,7 +599,7 @@
    {
    PeerConnection *conn;
    - conn = purple_xfer_get_protocol_data(xfer);
    + conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    /* Tell the other person that we've received everything */
    conn->fd = purple_xfer_get_fd(conn->xfer);
    @@ -617,7 +617,7 @@
    PeerConnection *conn;
    /* Update our rolling checksum. Like Walmart, yo. */
    - conn = purple_xfer_get_protocol_data(xfer);
    + conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    conn->xferdata.recvcsum = peer_oft_checksum_chunk(buffer,
    size, conn->xferdata.recvcsum, purple_xfer_get_bytes_sent(xfer) & 1);
    }
    @@ -653,7 +653,7 @@
    PeerConnection *conn;
    goffset size;
    - conn = purple_xfer_get_protocol_data(xfer);
    + conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    /* Make sure the file size can be represented in 32 bits */
    @@ -661,8 +661,8 @@
    if (size > G_MAXUINT32)
    {
    gchar *tmp, *size1, *size2;
    - size1 = purple_str_size_to_units(size);
    - size2 = purple_str_size_to_units(G_MAXUINT32);
    + size1 = g_format_size(size);
    + size2 = g_format_size(G_MAXUINT32);
    tmp = g_strdup_printf(_("File %s is %s, which is larger than "
    "the maximum size of %s."),
    purple_xfer_get_local_filename(xfer), size1, size2);
    @@ -713,7 +713,7 @@
    {
    PeerConnection *conn;
    - conn = purple_xfer_get_protocol_data(xfer);
    + conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    /*
    * If we're done sending, intercept the socket from the core ft code
    @@ -742,7 +742,7 @@
    {
    PeerConnection *conn;
    - conn = purple_xfer_get_protocol_data(xfer);
    + conn = oscar_xfer_get_peer_connection(OSCAR_XFER(xfer));
    if (conn == NULL)
    return;
    --- a/libpurple/protocols/oscar/oscar.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/oscar.c Tue Oct 08 21:48:28 2019 -0500
    @@ -990,12 +990,12 @@
    return 1;
    }
    - purple_debug_misc("oscar", "Reg status: %hu\n"
    - "Email: %s\n"
    - "BOSIP: %s\n",
    - info->regstatus,
    - info->email ? info->email : "null",
    - info->bosip ? info->bosip : "null");
    + purple_debug_misc("oscar",
    + "Reg status: %hu\n"
    + "Email: %s\n"
    + "BOSIP: %s\n",
    + info->regstatus, info->email ? info->email : "null",
    + info->bosip);
    purple_debug_info("oscar", "Closing auth connection...\n");
    flap_connection_schedule_destroy(conn, OSCAR_DISCONNECT_DONE, NULL);
    @@ -1903,8 +1903,7 @@
    smsmsg = byte_stream_getstr(&qbs, smslen);
    /* Check if this is an SMS being sent from server */
    - if ((smstype == 0) && (purple_strequal(tagstr, "ICQSMS")) && (smsmsg != NULL))
    - {
    + if (purple_strequal(tagstr, "ICQSMS") && smsmsg != NULL) {
    xmlroot = purple_xmlnode_from_str(smsmsg, -1);
    if (xmlroot != NULL)
    {
    @@ -2096,34 +2095,9 @@
    PurpleConnection *gc = od->gc;
    switch(reason) {
    - case 0x0003: { /* Reply from an ICQ status message request */
    - char *statusmsg, **splitmsg;
    - PurpleNotifyUserInfo *user_info;
    -
    - statusmsg = oscar_icqstatus(state);
    -
    - /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
    - /* TODO: Don't we need to escape each piece? */
    - splitmsg = g_strsplit(msg, "\r\n", 0);
    -
    - user_info = purple_notify_user_info_new();
    -
    - purple_notify_user_info_add_pair_plaintext(user_info, _("UIN"), who);
    - /* TODO: Check whether it's correct to call add_pair_html,
    - or if we should be using add_pair_plaintext */
    - purple_notify_user_info_add_pair_html(user_info, _("Status"), statusmsg);
    - purple_notify_user_info_add_section_break(user_info);
    - purple_notify_user_info_add_pair_html(user_info, NULL, g_strjoinv("<BR>", splitmsg));
    -
    - g_free(statusmsg);
    - g_strfreev(splitmsg);
    -
    - purple_notify_userinfo(gc, who, user_info, NULL, NULL);
    - purple_notify_user_info_destroy(user_info);
    -
    - } break;
    -
    - case 0x0006: { /* Reply from an ICQ status message request */
    + /* Reply from an ICQ status message request */
    + case 0x0003:
    + case 0x0006: {
    char *statusmsg, **splitmsg;
    PurpleNotifyUserInfo *user_info;
    @@ -2215,27 +2189,24 @@
    va_end(ap);
    switch (event) {
    - case 0x0000: { /* Text has been cleared */
    + case 0x0000: /* Text has been cleared */
    + case 0x000f: /* Closed IM window */
    purple_serv_got_typing_stopped(gc, bn);
    - } break;
    -
    - case 0x0001: { /* Paused typing */
    + break;
    +
    + case 0x0001: /* Paused typing */
    purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPED);
    - } break;
    -
    - case 0x0002: { /* Typing */
    + break;
    +
    + case 0x0002: /* Typing */
    purple_serv_got_typing(gc, bn, 0, PURPLE_IM_TYPING);
    - } break;
    -
    - case 0x000f: { /* Closed IM window */
    - purple_serv_got_typing_stopped(gc, bn);
    - } break;
    -
    - default: {
    + break;
    +
    + default:
    purple_debug_info("oscar", "Received unknown typing "
    "notification message from %s. Channel is 0x%04x "
    "and event is 0x%04hx.\n", bn, channel, event);
    - } break;
    + break;
    }
    return 1;
    @@ -5273,7 +5244,7 @@
    PurpleXfer *
    oscar_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who)
    {
    - PurpleXfer *xfer;
    + OscarXfer *xfer;
    OscarData *od;
    PurpleAccount *account;
    PeerConnection *conn;
    @@ -5281,23 +5252,23 @@
    od = purple_connection_get_protocol_data(gc);
    account = purple_connection_get_account(gc);
    - xfer = purple_xfer_new(account, PURPLE_XFER_TYPE_SEND, who);
    - if (xfer)
    - {
    - purple_xfer_set_init_fnc(xfer, peer_oft_sendcb_init);
    - purple_xfer_set_cancel_send_fnc(xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_request_denied_fnc(xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_ack_fnc(xfer, peer_oft_sendcb_ack);
    -
    - conn = peer_connection_new(od, OSCAR_CAPABILITY_SENDFILE, who);
    - conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
    - conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    - aim_icbm_makecookie(conn->cookie);
    - conn->xfer = xfer;
    - purple_xfer_set_protocol_data(xfer, conn);
    - }
    -
    - return xfer;
    + conn = peer_connection_new(od, OSCAR_CAPABILITY_SENDFILE, who);
    + conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
    + conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
    +
    + xfer = g_object_new(
    + OSCAR_TYPE_XFER,
    + "account", account,
    + "type", PURPLE_XFER_TYPE_SEND,
    + "remote-user", who,
    + "conn", conn,
    + NULL
    + );
    +
    + aim_icbm_makecookie(conn->cookie);
    + conn->xfer = PURPLE_XFER(xfer);
    +
    + return PURPLE_XFER(xfer);
    }
    /*
    @@ -5677,8 +5648,10 @@
    }
    static void
    -oscar_protocol_init(PurpleProtocol *protocol)
    +oscar_protocol_init(OscarProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    +
    protocol->options = OPT_PROTO_MAIL_CHECK | OPT_PROTO_INVITE_MESSAGE |
    OPT_PROTO_AUTHORIZATION_DENIED_MESSAGE;
    protocol->icon_spec = purple_buddy_icon_spec_new("gif,jpeg,bmp,ico",
    @@ -5688,15 +5661,22 @@
    }
    static void
    -oscar_protocol_class_init(PurpleProtocolClass *klass)
    +oscar_protocol_class_init(OscarProtocolClass *klass)
    {
    - klass->login = oscar_login;
    - klass->close = oscar_close;
    - klass->status_types = oscar_status_types;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = oscar_login;
    + protocol_class->close = oscar_close;
    + protocol_class->status_types = oscar_status_types;
    }
    static void
    -oscar_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +oscar_protocol_class_finalize(G_GNUC_UNUSED OscarProtocolClass *klass)
    +{
    +}
    +
    +static void
    +oscar_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = oscar_get_actions;
    client_iface->list_emblem = oscar_list_emblem;
    @@ -5709,7 +5689,7 @@
    }
    static void
    -oscar_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +oscar_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->set_info = oscar_set_info;
    server_iface->get_info = oscar_get_info;
    @@ -5727,14 +5707,14 @@
    }
    static void
    -oscar_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +oscar_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = oscar_send_im;
    im_iface->send_typing = oscar_send_typing;
    }
    static void
    -oscar_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +oscar_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = oscar_chat_info;
    chat_iface->info_defaults = oscar_chat_info_defaults;
    @@ -5746,7 +5726,7 @@
    }
    static void
    -oscar_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    +oscar_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    {
    privacy_iface->add_deny = oscar_add_deny;
    privacy_iface->rem_deny = oscar_rem_deny;
    @@ -5760,27 +5740,27 @@
    xfer_iface->new_xfer = oscar_new_xfer;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - OscarProtocol, oscar_protocol, PURPLE_TYPE_PROTOCOL, G_TYPE_FLAG_ABSTRACT,
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - oscar_protocol_client_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - oscar_protocol_server_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - oscar_protocol_im_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - oscar_protocol_chat_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - oscar_protocol_privacy_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
    - oscar_protocol_xfer_iface_init)
    -);
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + OscarProtocol, oscar_protocol, PURPLE_TYPE_PROTOCOL,
    + G_TYPE_FLAG_ABSTRACT,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + oscar_protocol_client_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + oscar_protocol_server_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + oscar_protocol_im_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + oscar_protocol_chat_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    + oscar_protocol_privacy_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    + oscar_protocol_xfer_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -5803,10 +5783,12 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - oscar_protocol_register_type(plugin);
    -
    - aim_protocol_register_type(plugin);
    - icq_protocol_register_type(plugin);
    + oscar_protocol_register_type(G_TYPE_MODULE(plugin));
    +
    + aim_protocol_register(plugin);
    + icq_protocol_register(plugin);
    +
    + oscar_xfer_register(G_TYPE_MODULE(plugin));
    aim_protocol = purple_protocols_add(AIM_TYPE_PROTOCOL, error);
    if (!aim_protocol)
    --- a/libpurple/protocols/oscar/oscar.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/oscar.h Tue Oct 08 21:48:28 2019 -0500
    @@ -26,8 +26,8 @@
    *
    */
    -#ifndef _OSCAR_H_
    -#define _OSCAR_H_
    +#ifndef PURPLE_OSCAR_OSCAR_H
    +#define PURPLE_OSCAR_OSCAR_H
    #include "internal.h"
    #include "circularbuffer.h"
    @@ -1375,4 +1375,4 @@
    G_END_DECLS
    -#endif /* _OSCAR_H_ */
    +#endif /* PURPLE_OSCAR_OSCAR_H */
    --- a/libpurple/protocols/oscar/oscarcommon.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/oscarcommon.h Tue Oct 08 21:48:28 2019 -0500
    @@ -24,6 +24,9 @@
    * and libicq.c
    */
    +#ifndef PURPLE_OSCAR_OSCARCOMMON_H
    +#define PURPLE_OSCAR_OSCARCOMMON_H
    +
    #include "internal.h"
    #include "accountopt.h"
    @@ -124,3 +127,5 @@
    GList *oscar_get_actions(PurpleConnection *gc);
    const gchar *oscar_get_login_server(gboolean is_icq, gboolean use_ssl);
    gboolean oscar_uri_handler(const char *proto, const char *cmd, GHashTable *params);
    +
    +#endif /* PURPLE_OSCAR_OSCARCOMMON_H */
    --- a/libpurple/protocols/oscar/peer.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/peer.c Tue Oct 08 21:48:28 2019 -0500
    @@ -208,7 +208,6 @@
    if (conn->xfer != NULL)
    {
    PurpleXferStatus status;
    - purple_xfer_set_protocol_data(conn->xfer, NULL);
    status = purple_xfer_get_status(conn->xfer);
    if ((status != PURPLE_XFER_STATUS_DONE) &&
    (status != PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
    @@ -1101,58 +1100,113 @@
    {
    gchar *filename;
    - conn->xfer = purple_xfer_new(account, PURPLE_XFER_TYPE_RECEIVE, bn);
    - if (conn->xfer)
    - {
    - purple_xfer_set_protocol_data(conn->xfer, conn);
    - purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
    + conn->xfer = PURPLE_XFER(g_object_new(
    + OSCAR_TYPE_XFER,
    + "account", account,
    + "type", PURPLE_XFER_TYPE_RECEIVE,
    + "remote-user", bn,
    + "conn", conn,
    + NULL
    + ));
    + purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
    - /* Set the file name */
    - if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
    - filename = g_strdup(args->info.sendfile.filename);
    - else
    - filename = purple_utf8_salvage(args->info.sendfile.filename);
    + /* Set the file name */
    + if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
    + filename = g_strdup(args->info.sendfile.filename);
    + else
    + filename = purple_utf8_salvage(args->info.sendfile.filename);
    - if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
    - {
    - /*
    - * If they are sending us a directory then the last character
    - * of the file name will be an asterisk. We don't want to
    - * save stuff to a directory named "*" so we remove the
    - * asterisk from the file name.
    - */
    - char *tmp = strrchr(filename, '\\');
    - if ((tmp != NULL) && (tmp[1] == '*'))
    - tmp[0] = '\0';
    - }
    - purple_xfer_set_filename(conn->xfer, filename);
    - g_free(filename);
    -
    + if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
    + {
    /*
    - * Set the message, unless this is the dummy message from an
    - * ICQ client or an empty message from an AIM client.
    - * TODO: Maybe we should strip HTML and then see if strlen>0?
    + * If they are sending us a directory then the last character
    + * of the file name will be an asterisk. We don't want to
    + * save stuff to a directory named "*" so we remove the
    + * asterisk from the file name.
    */
    - if ((message != NULL) &&
    - (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
    - (g_ascii_strcasecmp(message, "<HTML>") != 0))
    - {
    - purple_xfer_set_message(conn->xfer, message);
    - }
    + char *tmp = strrchr(filename, '\\');
    + if ((tmp != NULL) && (tmp[1] == '*'))
    + tmp[0] = '\0';
    + }
    + purple_xfer_set_filename(conn->xfer, filename);
    + g_free(filename);
    - /* Setup our I/O op functions */
    - purple_xfer_set_init_fnc(conn->xfer, peer_oft_recvcb_init);
    - purple_xfer_set_end_fnc(conn->xfer, peer_oft_recvcb_end);
    - purple_xfer_set_request_denied_fnc(conn->xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_cancel_recv_fnc(conn->xfer, peer_oft_cb_generic_cancel);
    - purple_xfer_set_ack_fnc(conn->xfer, peer_oft_recvcb_ack_recv);
    + /*
    + * Set the message, unless this is the dummy message from an
    + * ICQ client or an empty message from an AIM client.
    + * TODO: Maybe we should strip HTML and then see if strlen>0?
    + */
    + if ((message != NULL) &&
    + (g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
    + (g_ascii_strcasecmp(message, "<HTML>") != 0))
    + {
    + purple_xfer_set_message(conn->xfer, message);
    + }
    - /* Now perform the request */
    - purple_xfer_request(conn->xfer);
    - }
    + /* Now perform the request */
    + purple_xfer_request(conn->xfer);
    }
    }
    /*******************************************************************/
    /* End code for establishing a peer connection */
    /*******************************************************************/
    +
    +G_DEFINE_DYNAMIC_TYPE(OscarXfer, oscar_xfer, PURPLE_TYPE_XFER);
    +
    +static void
    +oscar_xfer_init_xfer(PurpleXfer *xfer) {
    + PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    +
    + if(type == PURPLE_XFER_TYPE_SEND) {
    + peer_oft_sendcb_init(xfer);
    + } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    + peer_oft_recvcb_init(xfer);
    + }
    +}
    +
    +static void
    +oscar_xfer_ack(PurpleXfer *xfer, const guchar *buffer, size_t size) {
    + PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    +
    + if(type == PURPLE_XFER_TYPE_SEND) {
    + peer_oft_sendcb_ack(xfer, buffer, size);
    + } else if(type == PURPLE_XFER_TYPE_RECEIVE) {
    + peer_oft_recvcb_ack_recv(xfer, buffer, size);
    + }
    +
    +}
    +
    +static void
    +oscar_xfer_init(OscarXfer *xfer) {
    +
    +}
    +
    +static void
    +oscar_xfer_class_finalize(OscarXferClass *klass) {
    +
    +}
    +
    +static void
    +oscar_xfer_class_init(OscarXferClass *klass) {
    + PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    +
    + xfer_class->init = oscar_xfer_init_xfer;
    + xfer_class->end = peer_oft_recvcb_end;
    + xfer_class->cancel_send = peer_oft_cb_generic_cancel;
    + xfer_class->cancel_recv = peer_oft_cb_generic_cancel;
    + xfer_class->request_denied = peer_oft_cb_generic_cancel;
    + xfer_class->ack = oscar_xfer_ack;
    +}
    +
    +void
    +oscar_xfer_register(GTypeModule *module) {
    + oscar_xfer_register_type(module);
    +}
    +
    +PeerConnection *
    +oscar_xfer_get_peer_connection(OscarXfer *xfer) {
    + g_return_val_if_fail(OSCAR_IS_XFER(xfer), NULL);
    +
    + return xfer->conn;
    +}
    --- a/libpurple/protocols/oscar/peer.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/peer.h Tue Oct 08 21:48:28 2019 -0500
    @@ -22,8 +22,8 @@
    * OFT and ODC Services
    */
    -#ifndef _PEER_H_
    -#define _PEER_H_
    +#ifndef PURPLE_OSCAR_PEER_H
    +#define PURPLE_OSCAR_PEER_H
    #include "xfer.h"
    #include "network.h"
    @@ -279,4 +279,17 @@
    */
    void peer_proxy_connection_established_cb(gpointer data, gint source, const gchar *error_message);
    -#endif /* _PEER_H_ */
    +/* File transfers */
    +#define OSCAR_TYPE_XFER (oscar_xfer_get_type())
    +G_DECLARE_FINAL_TYPE(OscarXfer, oscar_xfer, OSCAR, XFER, PurpleXfer);
    +
    +struct _OscarXfer {
    + PurpleXfer parent;
    +
    + PeerConnection *conn;
    +};
    +
    +void oscar_xfer_register(GTypeModule *module);
    +PeerConnection *oscar_xfer_get_peer_connection(OscarXfer *xfer);
    +
    +#endif /* PURPLE_OSCAR_PEER_H */
    --- a/libpurple/protocols/oscar/peer_proxy.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/peer_proxy.c Tue Oct 08 21:48:28 2019 -0500
    @@ -270,8 +270,7 @@
    }
    /* If this frame has a payload then attempt to read it */
    - if (frame->payload.len - frame->payload.offset > 0)
    - {
    + if (frame->payload.offset < frame->payload.len) {
    /* Read data into the temporary buffer until it is complete */
    read = recv(conn->fd,
    &frame->payload.data[frame->payload.offset],
    --- a/libpurple/protocols/oscar/snactypes.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/snactypes.h Tue Oct 08 21:48:28 2019 -0500
    @@ -22,8 +22,9 @@
    * AIM Callback Types
    *
    */
    -#ifndef _SNACTYPES_H_
    -#define _SNACTYPES_H_
    +
    +#ifndef PURPLE_OSCAR_SNACTYPES_H
    +#define PURPLE_OSCAR_SNACTYPES_H
    /*
    * SNAC Families.
    @@ -284,4 +285,4 @@
    /* SNAC flags */
    #define AIM_SNACFLAGS_DESTRUCTOR 0x0001
    -#endif /* _SNACTYPES_H_ */
    +#endif /* PURPLE_OSCAR_SNACTYPES_H */
    --- a/libpurple/protocols/oscar/userinfo.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/userinfo.c Tue Oct 08 21:48:28 2019 -0500
    @@ -32,11 +32,12 @@
    const gchar *tmp;
    guint64 bit = 1;
    - str = g_string_new("");
    -
    if (!caps) {
    return NULL;
    - } else while (bit <= OSCAR_CAPABILITY_LAST) {
    + }
    +
    + str = g_string_new("");
    + while (bit <= OSCAR_CAPABILITY_LAST) {
    if (bit & caps) {
    switch (bit) {
    case OSCAR_CAPABILITY_BUDDYICON:
    @@ -300,6 +301,7 @@
    purple_notify_user_info_add_pair_html(user_info, _("Status"), message);
    g_free(message);
    + g_free(itmsurl);
    }
    void
    --- a/libpurple/protocols/oscar/visibility.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/oscar/visibility.h Tue Oct 08 21:48:28 2019 -0500
    @@ -18,8 +18,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _VISIBILITY_H_
    -#define _VISIBILITY_H_
    +#ifndef PURPLE_OSCAR_VISIBILITY_H
    +#define PURPLE_OSCAR_VISIBILITY_H
    #include "oscar.h"
    #include "plugins.h"
    @@ -29,4 +29,4 @@
    void oscar_show_visible_list(PurpleProtocolAction *action);
    void oscar_show_invisible_list(PurpleProtocolAction *action);
    -#endif
    +#endif /* PURPLE_OSCAR_VISIBILITY_H */
    --- a/libpurple/protocols/sametime/im_mime.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/sametime/im_mime.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    *
    */
    -#ifndef SAMETIME_IM_MIME_H
    -#define SAMETIME_IM_MIME_H
    +#ifndef PURPLE_SAMETIME_IM_MIME_H
    +#define PURPLE_SAMETIME_IM_MIME_H
    #include <glib.h>
    @@ -36,4 +36,4 @@
    */
    gchar *im_mime_generate(const char *message);
    -#endif /* SAMETIME_IM_MIME_H */
    +#endif /* PURPLE_SAMETIME_IM_MIME_H */
    --- a/libpurple/protocols/sametime/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/sametime/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -10,6 +10,6 @@
    c_args : ['-DG_LOG_DOMAIN="sametime"'],
    dependencies : [meanwhile, gmime, libpurple_dep, glib],
    install : true, install_dir : PURPLE_PLUGINDIR)
    -
    +
    subdir('tests')
    endif
    --- a/libpurple/protocols/sametime/sametime.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/sametime/sametime.c Tue Oct 08 21:48:28 2019 -0500
    @@ -185,12 +185,6 @@
    #define DEBUG_WARN(...) purple_debug_warning(G_LOG_DOMAIN, __VA_ARGS__)
    -/** ensure non-null strings */
    -#ifndef NSTR
    -# define NSTR(str) ((str)? (str): "(null)")
    -#endif
    -
    -
    /** calibrates distinct secure channel nomenclature */
    static const unsigned char no_secret[] = {
    0x2d, 0x2d, 0x20, 0x73, 0x69, 0x65, 0x67, 0x65,
    @@ -509,7 +503,7 @@
    guint32 idle_len; /*< how long a client has been idle */
    guint32 ugly_idle_len; /*< how long a broken client has been idle */
    - DEBUG_INFO("%s has idle value 0x%x\n", NSTR(id), idle);
    + DEBUG_INFO("%s has idle value 0x%x\n", id, idle);
    idle_len = time(NULL) - idle;
    ugly_idle_len = ((time(NULL) * 1000) - idle) / 1000;
    @@ -540,7 +534,7 @@
    #else
    if(idle < 0 || idle > time(NULL)) {
    - DEBUG_INFO("hiding a messy idle value 0x%x\n", NSTR(id), idle);
    + DEBUG_INFO("hiding a messy idle value 0x%x\n", id, idle);
    idle = -1;
    }
    #endif
    @@ -673,8 +667,8 @@
    acct = purple_connection_get_account(gc);
    g_return_if_fail(acct != NULL);
    - for(gn = purple_blist_get_root(); gn;
    - gn = purple_blist_node_get_sibling_next(gn)) {
    + for (gn = purple_blist_get_default_root(); gn;
    + gn = purple_blist_node_get_sibling_next(gn)) {
    const char *owner;
    const char *gname;
    enum mwSametimeGroupType gtype;
    @@ -916,7 +910,7 @@
    acct = purple_connection_get_account(gc);
    owner = purple_account_get_username(acct);
    - blist = purple_blist_get_buddy_list();
    + blist = purple_blist_get_default();
    g_return_val_if_fail(blist != NULL, NULL);
    name = mwSametimeGroup_getName(stgroup);
    @@ -928,23 +922,17 @@
    return NULL;
    }
    - if (!name) {
    - DEBUG_WARN("Can't ensure a null group\n");
    - return NULL;
    - }
    -
    - DEBUG_INFO("attempting to ensure group %s, called %s\n",
    - NSTR(name), NSTR(alias));
    + DEBUG_INFO("attempting to ensure group %s, called %s\n", name, alias);
    /* first attempt at finding the group, by the name key */
    - for(gn = purple_blist_get_root(); gn;
    - gn = purple_blist_node_get_sibling_next(gn)) {
    + for (gn = purple_blist_get_default_root(); gn;
    + gn = purple_blist_node_get_sibling_next(gn)) {
    const char *n, *o;
    if(! PURPLE_IS_GROUP(gn)) continue;
    n = purple_blist_node_get_string(gn, GROUP_KEY_NAME);
    o = purple_blist_node_get_string(gn, GROUP_KEY_OWNER);
    - DEBUG_INFO("found group named %s, owned by %s\n", NSTR(n), NSTR(o));
    + DEBUG_INFO("found group named %s, owned by %s\n", n, o);
    if(n && purple_strequal(n, name)) {
    if(!o || purple_strequal(o, owner)) {
    @@ -957,7 +945,7 @@
    /* try again, by alias */
    if(! group) {
    - DEBUG_INFO("searching for group by alias %s\n", NSTR(alias));
    + DEBUG_INFO("searching for group by alias %s\n", alias);
    group = purple_blist_find_group(alias);
    }
    @@ -1017,7 +1005,7 @@
    g_return_if_fail(group != NULL);
    - DEBUG_INFO("clearing members from pruned group %s\n", NSTR(purple_group_get_name(group)));
    + DEBUG_INFO("clearing members from pruned group %s\n", purple_group_get_name(group));
    gc = purple_account_get_connection(acct);
    g_return_if_fail(gc != NULL);
    @@ -1037,7 +1025,7 @@
    if(! PURPLE_IS_BUDDY(bn)) continue;
    if(purple_buddy_get_account(gb) == acct) {
    - DEBUG_INFO("clearing %s from group\n", NSTR(purple_buddy_get_name(gb)));
    + DEBUG_INFO("clearing %s from group\n", purple_buddy_get_name(gb));
    prune = g_list_prepend(prune, gb);
    }
    }
    @@ -1074,7 +1062,7 @@
    g_return_if_fail(group != NULL);
    - DEBUG_INFO("pruning membership of group %s\n", NSTR(purple_group_get_name(group)));
    + DEBUG_INFO("pruning membership of group %s\n", purple_group_get_name(group));
    acct = purple_connection_get_account(gc);
    g_return_if_fail(acct != NULL);
    @@ -1087,7 +1075,7 @@
    for(ul = utl; ul; ul = ul->next) {
    const char *id = mwSametimeUser_getUser(ul->data);
    g_hash_table_insert(stusers, (char *) id, ul->data);
    - DEBUG_INFO("server copy has %s\n", NSTR(id));
    + DEBUG_INFO("server copy has %s\n", id);
    }
    g_list_free(utl);
    @@ -1108,7 +1096,7 @@
    /* if the account is correct and they're not in our table, mark
    them for pruning */
    if(purple_buddy_get_account(gb) == acct && !g_hash_table_lookup(stusers, purple_buddy_get_name(gb))) {
    - DEBUG_INFO("marking %s for pruning\n", NSTR(purple_buddy_get_name(gb)));
    + DEBUG_INFO("marking %s for pruning\n", purple_buddy_get_name(gb));
    prune = g_list_prepend(prune, gb);
    }
    }
    @@ -1149,7 +1137,7 @@
    acct_n = purple_account_get_username(acct);
    - blist = purple_blist_get_buddy_list();
    + blist = purple_blist_get_default();
    g_return_if_fail(blist != NULL);
    /* build a hash table for quick lookup while pruning the local
    @@ -1164,8 +1152,8 @@
    g_list_free(gtl);
    /* find all groups which should be pruned from the local list */
    - for(gn = purple_blist_get_root(); gn;
    - gn = purple_blist_node_get_sibling_next(gn)) {
    + for (gn = purple_blist_get_default_root(); gn;
    + gn = purple_blist_node_get_sibling_next(gn)) {
    PurpleGroup *grp = (PurpleGroup *) gn;
    const char *gname, *owner;
    struct mwSametimeGroup *stgrp;
    @@ -1365,8 +1353,8 @@
    PurpleBlistNode *gnode, *cnode, *bnode;
    GList *add_buds = NULL;
    - for(gnode = purple_blist_get_root(); gnode;
    - gnode = purple_blist_node_get_sibling_next(gnode)) {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = purple_blist_node_get_sibling_next(gnode)) {
    if(! PURPLE_IS_GROUP(gnode)) continue;
    for(cnode = purple_blist_node_get_first_child(gnode);
    @@ -1412,8 +1400,8 @@
    mwServiceStorage_load(pd->srvc_store, unit, fetch_blist_cb, pd, NULL);
    /* find all the NAB groups and subscribe to them */
    - for(l = purple_blist_get_root(); l;
    - l = purple_blist_node_get_sibling_next(l)) {
    + for (l = purple_blist_get_default_root(); l;
    + l = purple_blist_node_get_sibling_next(l)) {
    PurpleGroup *group = (PurpleGroup *) l;
    enum mwSametimeGroupType gt;
    const char *owner;
    @@ -1705,7 +1693,7 @@
    msg = _("A Sametime administrator has issued the following announcement"
    " on server %s");
    - prim = g_strdup_printf(msg, NSTR(host));
    + prim = g_strdup_printf(msg, host);
    purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO,
    _("Sametime Administrator Announcement"),
    @@ -1925,8 +1913,8 @@
    g_hash_table_insert(ht, CHAT_KEY_INVITE, c_invitation);
    DEBUG_INFO("received invitation from '%s' to join ('%s','%s'): '%s'\n",
    - NSTR(c_inviter), NSTR(c_name),
    - NSTR(c_topic), NSTR(c_invitation));
    + c_inviter, c_name,
    + c_topic, c_invitation);
    if(! c_topic) c_topic = "(no title)";
    if(! c_invitation) c_invitation = "(no message)";
    @@ -1980,8 +1968,7 @@
    const char *n = mwConference_getName(conf);
    const char *t = mwConference_getTitle(conf);
    - DEBUG_INFO("conf %s opened, %u initial members\n",
    - NSTR(n), g_list_length(members));
    + DEBUG_INFO("conf %s opened, %u initial members\n", n, g_list_length(members));
    srvc = mwConference_getService(conf);
    session = mwService_getSession(MW_SERVICE(srvc));
    @@ -2010,7 +1997,7 @@
    const char *n = mwConference_getName(conf);
    char *msg = mwError(reason);
    - DEBUG_INFO("conf %s closed, 0x%08x\n", NSTR(n), reason);
    + DEBUG_INFO("conf %s closed, 0x%08x\n", n, reason);
    srvc = mwConference_getService(conf);
    session = mwService_getSession(MW_SERVICE(srvc));
    @@ -2032,7 +2019,7 @@
    const char *n = mwConference_getName(conf);
    - DEBUG_INFO("%s joined conf %s\n", NSTR(peer->user_id), NSTR(n));
    + DEBUG_INFO("%s joined conf %s\n", peer->user_id, n);
    g_conf = mwConference_getClientData(conf);
    g_return_if_fail(g_conf != NULL);
    @@ -2049,7 +2036,7 @@
    const char *n = mwConference_getName(conf);
    - DEBUG_INFO("%s left conf %s\n", NSTR(peer->user_id), NSTR(n));
    + DEBUG_INFO("%s left conf %s\n", peer->user_id, n);
    g_conf = mwConference_getClientData(conf);
    g_return_if_fail(g_conf != NULL);
    @@ -2089,10 +2076,10 @@
    const char *w = who->user_id;
    if(typing) {
    - DEBUG_INFO("%s in conf %s: <typing>\n", NSTR(w), NSTR(n));
    + DEBUG_INFO("%s in conf %s: <typing>\n", w, n);
    } else {
    - DEBUG_INFO("%s in conf %s: <stopped typing>\n", NSTR(w), NSTR(n));
    + DEBUG_INFO("%s in conf %s: <stopped typing>\n", w, n);
    }
    }
    @@ -2124,11 +2111,22 @@
    /** size of an outgoing file transfer chunk */
    #define MW_FT_LEN (BUF_LONG * 2)
    +#define MW_TYPE_XFER (mw_xfer_get_type())
    +G_DECLARE_FINAL_TYPE(mwXfer, mw_xfer, MW, XFER, PurpleXfer)
    +
    +struct _mwXfer {
    + PurpleXfer parent;
    +
    + struct mwFileTransfer *ft;
    +};
    +
    +G_DEFINE_DYNAMIC_TYPE(mwXfer, mw_xfer, PURPLE_TYPE_XFER);
    +
    static void ft_incoming_cancel(PurpleXfer *xfer) {
    /* incoming transfer rejected or cancelled in-progress */
    - struct mwFileTransfer *ft = purple_xfer_get_protocol_data(xfer);
    - if(ft) mwFileTransfer_reject(ft);
    + mwXfer *mw_xfer = MW_XFER(xfer);
    + if(mw_xfer->ft) mwFileTransfer_reject(mw_xfer->ft);
    }
    @@ -2140,14 +2138,84 @@
    - stick the FILE's fp in xfer->dest_fp
    */
    - struct mwFileTransfer *ft;
    -
    - ft = purple_xfer_get_protocol_data(xfer);
    + mwXfer *mw_xfer = MW_XFER(xfer);
    purple_xfer_start(xfer, -1, NULL, 0);
    - mwFileTransfer_accept(ft);
    -}
    -
    + mwFileTransfer_accept(mw_xfer->ft);
    +}
    +
    +
    +static void ft_outgoing_init(PurpleXfer *xfer) {
    + PurpleAccount *acct;
    + PurpleConnection *gc;
    +
    + struct mwPurpleProtocolData *pd;
    + struct mwServiceFileTransfer *srvc;
    + struct mwFileTransfer *ft;
    +
    + const char *filename;
    + gsize filesize;
    + FILE *fp;
    + char *remote_user = NULL;
    +
    + struct mwIdBlock idb = { NULL, NULL };
    +
    + DEBUG_INFO("ft_outgoing_init\n");
    +
    + acct = purple_xfer_get_account(xfer);
    + gc = purple_account_get_connection(acct);
    + pd = purple_connection_get_protocol_data(gc);
    + srvc = pd->srvc_ft;
    +
    + remote_user = g_strdup(purple_xfer_get_remote_user(xfer));
    +
    + filename = purple_xfer_get_local_filename(xfer);
    + filesize = purple_xfer_get_size(xfer);
    + idb.user = remote_user;
    +
    + purple_xfer_update_progress(xfer);
    +
    + /* test that we can actually send the file */
    + fp = g_fopen(filename, "rb");
    + if(! fp) {
    + char *msg = g_strdup_printf(_("Error reading file %s: \n%s\n"),
    + filename, g_strerror(errno));
    + purple_xfer_error(purple_xfer_get_xfer_type(xfer), acct, purple_xfer_get_remote_user(xfer), msg);
    + g_free(msg);
    + g_free(remote_user);
    + return;
    + }
    + fclose(fp);
    +
    + {
    + char *tmp = strrchr(filename, G_DIR_SEPARATOR);
    + if(tmp++) filename = tmp;
    + }
    +
    + ft = mwFileTransfer_new(srvc, &idb, NULL, filename, filesize);
    +
    + g_object_ref(xfer);
    + mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) g_object_unref);
    +
    + mwFileTransfer_offer(ft);
    + g_free(remote_user);
    +}
    +
    +
    +static void ft_init(PurpleXfer *xfer) {
    + switch(purple_xfer_get_xfer_type(xfer)) {
    + case PURPLE_XFER_TYPE_SEND:
    + ft_outgoing_init(xfer);
    + break;
    + case PURPLE_XFER_TYPE_RECEIVE:
    + ft_incoming_init(xfer);
    + break;
    + case PURPLE_XFER_TYPE_UNKNOWN:
    + default:
    + g_return_if_reached();
    + break;
    + }
    +}
    static void mw_ft_offered(struct mwFileTransfer *ft) {
    /*
    @@ -2173,21 +2241,16 @@
    who = mwFileTransfer_getUser(ft)->user;
    DEBUG_INFO("file transfer %p offered\n", ft);
    - DEBUG_INFO(" from: %s\n", NSTR(who));
    - DEBUG_INFO(" file: %s\n", NSTR(mwFileTransfer_getFileName(ft)));
    + DEBUG_INFO(" from: %s\n", who);
    + DEBUG_INFO(" file: %s\n", mwFileTransfer_getFileName(ft));
    DEBUG_INFO(" size: %u\n", mwFileTransfer_getFileSize(ft));
    - DEBUG_INFO(" text: %s\n", NSTR(mwFileTransfer_getMessage(ft)));
    + DEBUG_INFO(" text: %s\n", mwFileTransfer_getMessage(ft));
    xfer = purple_xfer_new(acct, PURPLE_XFER_TYPE_RECEIVE, who);
    if (xfer)
    {
    g_object_ref(xfer);
    mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) g_object_unref);
    - purple_xfer_set_protocol_data(xfer, ft);
    -
    - purple_xfer_set_init_fnc(xfer, ft_incoming_init);
    - purple_xfer_set_cancel_recv_fnc(xfer, ft_incoming_cancel);
    - purple_xfer_set_request_denied_fnc(xfer, ft_incoming_cancel);
    purple_xfer_set_filename(xfer, mwFileTransfer_getFileName(ft));
    purple_xfer_set_size(xfer, mwFileTransfer_getFileSize(ft));
    @@ -2220,7 +2283,7 @@
    } else {
    int err = errno;
    DEBUG_WARN("problem reading from file %s: %s\n",
    - NSTR(mwFileTransfer_getFileName(ft)), g_strerror(err));
    + mwFileTransfer_getFileName(ft), g_strerror(err));
    mwFileTransfer_cancel(ft);
    }
    @@ -2261,8 +2324,6 @@
    xfer = mwFileTransfer_getClientData(ft);
    if(xfer) {
    - purple_xfer_set_protocol_data(xfer, NULL);
    -
    if(! mwFileTransfer_getRemaining(ft)) {
    purple_xfer_set_completed(xfer, TRUE);
    purple_xfer_end(xfer);
    @@ -2362,6 +2423,52 @@
    }
    +static void ft_outgoing_cancel(PurpleXfer *xfer) {
    + mwXfer *mw_xfer = MW_XFER(xfer);
    +
    + DEBUG_INFO("ft_outgoing_cancel called\n");
    +
    + if(mw_xfer->ft) mwFileTransfer_cancel(mw_xfer->ft);
    +}
    +
    +
    +static void
    +mw_xfer_init(mwXfer *xfer) {
    +
    +}
    +
    +static void
    +mw_xfer_class_finalize(mwXferClass *klass) {
    +
    +}
    +
    +static void
    +mw_xfer_class_init(mwXferClass *klass) {
    + PurpleXferClass *xfer_class = PURPLE_XFER_CLASS(klass);
    +
    + xfer_class->init = ft_init;
    + xfer_class->cancel_send = ft_outgoing_cancel;
    + xfer_class->cancel_recv = ft_incoming_cancel;
    + xfer_class->request_denied = ft_incoming_cancel;
    +}
    +
    +
    +static PurpleXfer *mw_protocol_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who) {
    + PurpleAccount *acct;
    +
    + acct = purple_connection_get_account(gc);
    +
    + return g_object_new(
    + MW_TYPE_XFER,
    + "account", acct,
    + "type", PURPLE_XFER_TYPE_SEND,
    + "remote-user", who,
    + NULL
    + );
    +
    +}
    +
    +
    static void convo_data_free(struct convo_data *cd) {
    GList *l;
    @@ -2828,7 +2935,7 @@
    members = mwPlace_getMembers(place);
    DEBUG_INFO("place %s opened, %u initial members\n",
    - NSTR(n), g_list_length(members));
    + n, g_list_length(members));
    if(! t) t = "(no title)";
    gconf = purple_serv_got_joined_chat(gc, PLACE_TO_ID(place), t);
    @@ -2853,7 +2960,7 @@
    const char *n = mwPlace_getName(place);
    char *msg = mwError(code);
    - DEBUG_INFO("place %s closed, 0x%08x\n", NSTR(n), code);
    + DEBUG_INFO("place %s closed, 0x%08x\n", n, code);
    srvc = mwPlace_getService(place);
    session = mwService_getSession(MW_SERVICE(srvc));
    @@ -2874,7 +2981,7 @@
    const char *n = mwPlace_getName(place);
    - DEBUG_INFO("%s joined place %s\n", NSTR(peer->user), NSTR(n));
    + DEBUG_INFO("%s joined place %s\n", peer->user, n);
    gconf = mwPlace_getClientData(place);
    g_return_if_fail(gconf != NULL);
    @@ -2890,7 +2997,7 @@
    const char *n = mwPlace_getName(place);
    - DEBUG_INFO("%s left place %s\n", NSTR(peer->user), NSTR(n));
    + DEBUG_INFO("%s left place %s\n", peer->user, n);
    gconf = mwPlace_getClientData(place);
    g_return_if_fail(gconf != NULL);
    @@ -3960,7 +4067,7 @@
    GList *row = NULL;
    DEBUG_INFO("multi resolve: %s, %s\n",
    - NSTR(match->id), NSTR(match->name));
    + match->id, match->name);
    if(!match->id || !match->name)
    continue;
    @@ -4578,89 +4685,6 @@
    }
    -static void ft_outgoing_init(PurpleXfer *xfer) {
    - PurpleAccount *acct;
    - PurpleConnection *gc;
    -
    - struct mwPurpleProtocolData *pd;
    - struct mwServiceFileTransfer *srvc;
    - struct mwFileTransfer *ft;
    -
    - const char *filename;
    - gsize filesize;
    - FILE *fp;
    - char *remote_user = NULL;
    -
    - struct mwIdBlock idb = { NULL, NULL };
    -
    - DEBUG_INFO("ft_outgoing_init\n");
    -
    - acct = purple_xfer_get_account(xfer);
    - gc = purple_account_get_connection(acct);
    - pd = purple_connection_get_protocol_data(gc);
    - srvc = pd->srvc_ft;
    -
    - remote_user = g_strdup(purple_xfer_get_remote_user(xfer));
    -
    - filename = purple_xfer_get_local_filename(xfer);
    - filesize = purple_xfer_get_size(xfer);
    - idb.user = remote_user;
    -
    - purple_xfer_update_progress(xfer);
    -
    - /* test that we can actually send the file */
    - fp = g_fopen(filename, "rb");
    - if(! fp) {
    - char *msg = g_strdup_printf(_("Error reading file %s: \n%s\n"),
    - filename, g_strerror(errno));
    - purple_xfer_error(purple_xfer_get_xfer_type(xfer), acct, purple_xfer_get_remote_user(xfer), msg);
    - g_free(msg);
    - g_free(remote_user);
    - return;
    - }
    - fclose(fp);
    -
    - {
    - char *tmp = strrchr(filename, G_DIR_SEPARATOR);
    - if(tmp++) filename = tmp;
    - }
    -
    - ft = mwFileTransfer_new(srvc, &idb, NULL, filename, filesize);
    -
    - g_object_ref(xfer);
    - mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) g_object_unref);
    - purple_xfer_set_protocol_data(xfer, ft);
    -
    - mwFileTransfer_offer(ft);
    - g_free(remote_user);
    -}
    -
    -
    -static void ft_outgoing_cancel(PurpleXfer *xfer) {
    - struct mwFileTransfer *ft = purple_xfer_get_protocol_data(xfer);
    -
    - DEBUG_INFO("ft_outgoing_cancel called\n");
    -
    - if(ft) mwFileTransfer_cancel(ft);
    -}
    -
    -
    -static PurpleXfer *mw_protocol_new_xfer(PurpleProtocolXfer *prplxfer, PurpleConnection *gc, const char *who) {
    - PurpleAccount *acct;
    - PurpleXfer *xfer;
    -
    - acct = purple_connection_get_account(gc);
    -
    - xfer = purple_xfer_new(acct, PURPLE_XFER_TYPE_SEND, who);
    - if (xfer)
    - {
    - purple_xfer_set_init_fnc(xfer, ft_outgoing_init);
    - purple_xfer_set_cancel_send_fnc(xfer, ft_outgoing_cancel);
    - }
    -
    - return xfer;
    -}
    -
    static void mw_protocol_send_file(PurpleProtocolXfer *prplxfer,
    PurpleConnection *gc,
    const char *who, const char *file) {
    @@ -5066,7 +5090,7 @@
    msgA = _("No matches");
    msgB = _("The identifier '%s' did not match any users in your"
    " Sametime community.");
    - msg = g_strdup_printf(msgB, (res && res->name) ? NSTR(res->name) : "");
    + msg = g_strdup_printf(msgB, (res && res->name) ? res->name : "");
    purple_notify_error(gc, _("No Matches"), msgA, msg,
    purple_request_cpar_from_connection(gc));
    @@ -5160,70 +5184,77 @@
    }
    }
    +static void
    +mw_protocol_init(mwProtocol *self)
    +{
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    + PurpleAccountUserSplit *split;
    + PurpleAccountOption *opt;
    + GList *l = NULL;
    +
    + protocol->id = PROTOCOL_ID;
    + protocol->name = PROTOCOL_NAME;
    +
    + /* set up the preferences */
    + purple_prefs_add_none(MW_PROTOCOL_OPT_BASE);
    + purple_prefs_add_int(MW_PROTOCOL_OPT_BLIST_ACTION,
    + BLIST_CHOICE_DEFAULT);
    +
    + /* set up account ID as user:server */
    + split = purple_account_user_split_new(_("Server"),
    + MW_PLUGIN_DEFAULT_HOST, ':');
    + protocol->user_splits = g_list_append(protocol->user_splits, split);
    +
    + /* remove dead preferences */
    + purple_prefs_remove(MW_PROTOCOL_OPT_PSYCHIC);
    + purple_prefs_remove(MW_PROTOCOL_OPT_SAVE_DYNAMIC);
    +
    + /* port to connect to */
    + opt = purple_account_option_int_new(_("Port"), MW_KEY_PORT,
    + MW_PLUGIN_DEFAULT_PORT);
    + l = g_list_append(l, opt);
    +
    + { /* copy the old force login setting from prefs if it's
    + there. Don't delete the preference, since there may be more
    + than one account that wants to check for it. */
    + gboolean b = FALSE;
    + const char *label = _("Force login (ignore server redirects)");
    +
    + if (purple_prefs_exists(MW_PROTOCOL_OPT_FORCE_LOGIN))
    + b = purple_prefs_get_bool(MW_PROTOCOL_OPT_FORCE_LOGIN);
    +
    + opt = purple_account_option_bool_new(label, MW_KEY_FORCE, b);
    + l = g_list_append(l, opt);
    + }
    +
    + /* pretend to be Sametime Connect */
    + opt = purple_account_option_bool_new(_("Hide client identity"),
    + MW_KEY_FAKE_IT, FALSE);
    + l = g_list_append(l, opt);
    +
    + protocol->account_options = l;
    + l = NULL;
    +}
    static void
    -mw_protocol_init(PurpleProtocol *protocol)
    +mw_protocol_class_init(mwProtocolClass *klass)
    {
    - PurpleAccountUserSplit *split;
    - PurpleAccountOption *opt;
    - GList *l = NULL;
    -
    - protocol->id = PROTOCOL_ID;
    - protocol->name = PROTOCOL_NAME;
    -
    - /* set up the preferences */
    - purple_prefs_add_none(MW_PROTOCOL_OPT_BASE);
    - purple_prefs_add_int(MW_PROTOCOL_OPT_BLIST_ACTION, BLIST_CHOICE_DEFAULT);
    -
    - /* set up account ID as user:server */
    - split = purple_account_user_split_new(_("Server"),
    - MW_PLUGIN_DEFAULT_HOST, ':');
    - protocol->user_splits = g_list_append(protocol->user_splits, split);
    -
    - /* remove dead preferences */
    - purple_prefs_remove(MW_PROTOCOL_OPT_PSYCHIC);
    - purple_prefs_remove(MW_PROTOCOL_OPT_SAVE_DYNAMIC);
    -
    - /* port to connect to */
    - opt = purple_account_option_int_new(_("Port"), MW_KEY_PORT,
    - MW_PLUGIN_DEFAULT_PORT);
    - l = g_list_append(l, opt);
    -
    - { /* copy the old force login setting from prefs if it's
    - there. Don't delete the preference, since there may be more
    - than one account that wants to check for it. */
    - gboolean b = FALSE;
    - const char *label = _("Force login (ignore server redirects)");
    -
    - if(purple_prefs_exists(MW_PROTOCOL_OPT_FORCE_LOGIN))
    - b = purple_prefs_get_bool(MW_PROTOCOL_OPT_FORCE_LOGIN);
    -
    - opt = purple_account_option_bool_new(label, MW_KEY_FORCE, b);
    - l = g_list_append(l, opt);
    - }
    -
    - /* pretend to be Sametime Connect */
    - opt = purple_account_option_bool_new(_("Hide client identity"),
    - MW_KEY_FAKE_IT, FALSE);
    - l = g_list_append(l, opt);
    -
    - protocol->account_options = l;
    - l = NULL;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = mw_protocol_login;
    + protocol_class->close = mw_protocol_close;
    + protocol_class->status_types = mw_protocol_status_types;
    + protocol_class->list_icon = mw_protocol_list_icon;
    +}
    +
    +static void
    +mw_protocol_class_finalize(G_GNUC_UNUSED mwProtocolClass *klass)
    +{
    }
    static void
    -mw_protocol_class_init(PurpleProtocolClass *klass)
    -{
    - klass->login = mw_protocol_login;
    - klass->close = mw_protocol_close;
    - klass->status_types = mw_protocol_status_types;
    - klass->list_icon = mw_protocol_list_icon;
    -}
    -
    -
    -static void
    -mw_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +mw_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = mw_protocol_get_actions;
    client_iface->list_emblem = mw_protocol_list_emblem;
    @@ -5237,7 +5268,7 @@
    static void
    -mw_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +mw_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->get_info = mw_protocol_get_info;
    server_iface->set_status = mw_protocol_set_status;
    @@ -5254,7 +5285,7 @@
    static void
    -mw_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +mw_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = mw_protocol_send_im;
    im_iface->send_typing = mw_protocol_send_typing;
    @@ -5262,7 +5293,7 @@
    static void
    -mw_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +mw_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = mw_protocol_chat_info;
    chat_iface->info_defaults = mw_protocol_chat_info_defaults;
    @@ -5276,7 +5307,7 @@
    static void
    -mw_protocol_privacy_iface_init(PurpleProtocolPrivacyIface *privacy_iface)
    +mw_protocol_privacy_iface_init(PurpleProtocolPrivacyInterface *privacy_iface)
    {
    privacy_iface->add_permit = mw_protocol_add_permit;
    privacy_iface->add_deny = mw_protocol_add_deny;
    @@ -5294,29 +5325,26 @@
    xfer_iface->new_xfer = mw_protocol_new_xfer;
    }
    -
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - mwProtocol, mw_protocol, PURPLE_TYPE_PROTOCOL, 0,
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - mw_protocol_client_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - mw_protocol_server_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - mw_protocol_im_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - mw_protocol_chat_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_PRIVACY_IFACE,
    - mw_protocol_privacy_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER,
    - mw_protocol_xfer_iface_init)
    -);
    -
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + mwProtocol, mw_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + mw_protocol_client_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + mw_protocol_server_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + mw_protocol_im_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + mw_protocol_chat_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_PRIVACY,
    + mw_protocol_privacy_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    + mw_protocol_xfer_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -5346,7 +5374,9 @@
    GLogLevelFlags logflags =
    G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION;
    - mw_protocol_register_type(plugin);
    + mw_protocol_register_type(G_TYPE_MODULE(plugin));
    +
    + mw_xfer_register_type(G_TYPE_MODULE(plugin));
    my_protocol = purple_protocols_add(MW_TYPE_PROTOCOL, error);
    if (!my_protocol)
    --- a/libpurple/protocols/sametime/sametime.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/sametime/sametime.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,5 +1,5 @@
    -#ifndef _SAMETIME_H_
    -#define _SAMETIME_H_
    +#ifndef PURPLE_SAMETIME_SAMETIME_H
    +#define PURPLE_SAMETIME_SAMETIME_H
    #include <gmodule.h>
    @@ -52,4 +52,4 @@
    */
    G_MODULE_EXPORT GType mw_protocol_get_type(void);
    -#endif /* _SAMETIME_H_ */
    +#endif /* PURPLE_SAMETIME_SAMETIME_H */
    --- a/libpurple/protocols/silc/silc.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/silc/silc.c Tue Oct 08 21:48:28 2019 -0500
    @@ -2113,10 +2113,10 @@
    NULL
    };
    -
    static void
    -silcpurple_protocol_init(PurpleProtocol *protocol)
    +silcpurple_protocol_init(SilcProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountOption *option;
    PurpleAccountUserSplit *split;
    char tmp[256];
    @@ -2195,16 +2195,23 @@
    }
    static void
    -silcpurple_protocol_class_init(PurpleProtocolClass *klass)
    +silcpurple_protocol_class_init(SilcProtocolClass *klass)
    {
    - klass->login = silcpurple_login;
    - klass->close = silcpurple_close;
    - klass->status_types = silcpurple_away_states;
    - klass->list_icon = silcpurple_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = silcpurple_login;
    + protocol_class->close = silcpurple_close;
    + protocol_class->status_types = silcpurple_away_states;
    + protocol_class->list_icon = silcpurple_list_icon;
    }
    static void
    -silcpurple_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +silcpurple_protocol_class_finalize(G_GNUC_UNUSED SilcProtocolClass *klass)
    +{
    +}
    +
    +static void
    +silcpurple_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = silcpurple_get_actions;
    client_iface->status_text = silcpurple_status_text;
    @@ -2213,7 +2220,7 @@
    }
    static void
    -silcpurple_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +silcpurple_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->set_info = silcpurple_set_info;
    server_iface->get_info = silcpurple_get_info;
    @@ -2227,13 +2234,13 @@
    }
    static void
    -silcpurple_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +silcpurple_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = silcpurple_send_im;
    }
    static void
    -silcpurple_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +silcpurple_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = silcpurple_chat_info;
    chat_iface->info_defaults = silcpurple_chat_info_defaults;
    @@ -2246,7 +2253,7 @@
    }
    static void
    -silcpurple_protocol_roomlist_iface_init(PurpleProtocolRoomlistIface *roomlist_iface)
    +silcpurple_protocol_roomlist_iface_init(PurpleProtocolRoomlistInterface *roomlist_iface)
    {
    roomlist_iface->get_list = silcpurple_roomlist_get_list;
    roomlist_iface->cancel = silcpurple_roomlist_cancel;
    @@ -2259,27 +2266,26 @@
    xfer_iface->new_xfer = silcpurple_ftp_new_xfer;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - SilcProtocol, silcpurple_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + SilcProtocol, silcpurple_protocol, PURPLE_TYPE_PROTOCOL, 0,
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - silcpurple_protocol_client_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + silcpurple_protocol_client_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - silcpurple_protocol_server_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + silcpurple_protocol_server_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - silcpurple_protocol_im_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + silcpurple_protocol_im_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - silcpurple_protocol_chat_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + silcpurple_protocol_chat_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_ROOMLIST_IFACE,
    - silcpurple_protocol_roomlist_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_ROOMLIST,
    + silcpurple_protocol_roomlist_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_XFER_IFACE,
    - silcpurple_protocol_xfer_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_XFER,
    + silcpurple_protocol_xfer_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -2308,7 +2314,7 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - silcpurple_protocol_register_type(plugin);
    + silcpurple_protocol_register_type(G_TYPE_MODULE(plugin));
    my_protocol = purple_protocols_add(SILCPURPLE_TYPE_PROTOCOL, error);
    if (!my_protocol)
    --- a/libpurple/protocols/silc/silcpurple.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/silc/silcpurple.h Tue Oct 08 21:48:28 2019 -0500
    @@ -17,8 +17,8 @@
    */
    -#ifndef SILCPURPLE_H
    -#define SILCPURPLE_H
    +#ifndef PURPLE_SILC_SILCPURPLE_H
    +#define PURPLE_SILC_SILCPURPLE_H
    #include <gmodule.h>
    @@ -193,4 +193,4 @@
    int geteuid(void);
    #endif
    -#endif /* SILCPURPLE_H */
    +#endif /* PURPLE_SILC_SILCPURPLE_H */
    --- a/libpurple/protocols/silc/wb.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/silc/wb.h Tue Oct 08 21:48:28 2019 -0500
    @@ -17,8 +17,8 @@
    */
    -#ifndef SILCPURPLE_WB_H
    -#define SILCPURPLE_WB_H
    +#ifndef PURPLE_SILC_WB_H
    +#define PURPLE_SILC_WB_H
    #include "silcpurple.h"
    #include "whiteboard.h"
    @@ -46,4 +46,4 @@
    void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list);
    void silcpurple_wb_clear(PurpleWhiteboard *wb);
    -#endif /* SILCPURPLE_WB_H */
    +#endif /* PURPLE_SILC_WB_H */
    --- a/libpurple/protocols/simple/ntlm.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/simple/ntlm.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* purple
    +/*
    + * purple
    *
    * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
    *
    @@ -21,10 +22,9 @@
    */
    #include "internal.h"
    +#include <purple.h>
    -#include "util.h"
    #include "ntlm.h"
    -#include "debug.h"
    #ifdef HAVE_NETTLE
    #include <nettle/des.h>
    --- a/libpurple/protocols/simple/ntlm.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/simple/ntlm.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,5 @@
    -/* purple
    +/*
    + * purple
    *
    * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
    *
    @@ -20,8 +21,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_NTLM_H
    -#define _PURPLE_NTLM_H
    +#ifndef PURPLE_SIMPLE_NTLM_H
    +#define PURPLE_SIMPLE_NTLM_H
    G_BEGIN_DECLS
    @@ -55,4 +56,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_NTLM_H */
    +#endif /* PURPLE_SIMPLE_NTLM_H */
    --- a/libpurple/protocols/simple/simple.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/simple/simple.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file simple.c
    - *
    * purple
    *
    * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
    @@ -25,19 +23,7 @@
    */
    #include "internal.h"
    -
    -#include "accountopt.h"
    -#include "buddylist.h"
    -#include "conversation.h"
    -#include "core.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "protocol.h"
    -#include "plugins.h"
    -#include "util.h"
    -#include "version.h"
    -#include "network.h"
    -#include "xmlnode.h"
    +#include <purple.h>
    #include "simple.h"
    #include "sipmsg.h"
    @@ -67,9 +53,9 @@
    }
    static gint
    -simple_uri_handler_find_account(gconstpointer a, gconstpointer b)
    +simple_uri_handler_find_account(PurpleAccount *account,
    + G_GNUC_UNUSED gconstpointer data)
    {
    - PurpleAccount *account = PURPLE_ACCOUNT(a);
    const gchar *protocol_id;
    protocol_id = purple_account_get_protocol_id(account);
    @@ -104,8 +90,8 @@
    /* Find online SIMPLE account */
    accounts = purple_accounts_get_all();
    - account_node = g_list_find_custom(accounts, NULL,
    - simple_uri_handler_find_account);
    + account_node = g_list_find_custom(
    + accounts, NULL, (GCompareFunc)simple_uri_handler_find_account);
    if (account_node == NULL) {
    return FALSE;
    @@ -2044,6 +2030,7 @@
    purple_connection_error(gc,
    PURPLE_CONNECTION_ERROR_INVALID_SETTINGS,
    _("SIP connect server not specified"));
    + g_strfreev(userserver);
    return;
    }
    @@ -2146,8 +2133,9 @@
    }
    static void
    -simple_protocol_init(PurpleProtocol *protocol)
    +simple_protocol_init(SIMPLEProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountUserSplit *split;
    PurpleAccountOption *option;
    @@ -2176,16 +2164,23 @@
    }
    static void
    -simple_protocol_class_init(PurpleProtocolClass *klass)
    +simple_protocol_class_init(SIMPLEProtocolClass *klass)
    {
    - klass->login = simple_login;
    - klass->close = simple_close;
    - klass->status_types = simple_status_types;
    - klass->list_icon = simple_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = simple_login;
    + protocol_class->close = simple_close;
    + protocol_class->status_types = simple_status_types;
    + protocol_class->list_icon = simple_list_icon;
    }
    static void
    -simple_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +simple_protocol_class_finalize(G_GNUC_UNUSED SIMPLEProtocolClass *klass)
    +{
    +}
    +
    +static void
    +simple_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->set_status = simple_set_status;
    server_iface->add_buddy = simple_add_buddy;
    @@ -2195,21 +2190,20 @@
    }
    static void
    -simple_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +simple_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = simple_im_send;
    im_iface->send_typing = simple_typing;
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - SIMPLEProtocol, simple_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + SIMPLEProtocol, simple_protocol, PURPLE_TYPE_PROTOCOL, 0,
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - simple_protocol_server_iface_init)
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + simple_protocol_server_iface_init)
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - simple_protocol_im_iface_init)
    -);
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + simple_protocol_im_iface_init));
    static PurplePluginInfo *
    plugin_query(GError **error)
    @@ -2238,7 +2232,7 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - simple_protocol_register_type(plugin);
    + simple_protocol_register_type(G_TYPE_MODULE(plugin));
    my_protocol = purple_protocols_add(SIMPLE_TYPE_PROTOCOL, error);
    if (!my_protocol)
    --- a/libpurple/protocols/simple/simple.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/simple/simple.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file simple.h
    - *
    * purple
    *
    * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
    @@ -20,18 +18,15 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SIMPLE_H
    -#define _PURPLE_SIMPLE_H
    +#ifndef PURPLE_SIMPLE_SIMPLE_H
    +#define PURPLE_SIMPLE_SIMPLE_H
    #include <glib.h>
    #include <gmodule.h>
    #include <gio/gio.h>
    #include <time.h>
    -#include "circularbuffer.h"
    -#include "network.h"
    -#include "proxy.h"
    -#include "protocol.h"
    +#include <purple.h>
    #include "sipmsg.h"
    @@ -154,4 +149,4 @@
    G_MODULE_EXPORT GType simple_protocol_get_type(void);
    -#endif /* _PURPLE_SIMPLE_H */
    +#endif /* PURPLE_SIMPLE_SIMPLE_H */
    --- a/libpurple/protocols/simple/sipmsg.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/simple/sipmsg.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file sipmsg.c
    - *
    * purple
    *
    * Copyright (C) 2005 Thomas Butter <butter@uni-mannheim.de>
    @@ -21,16 +19,7 @@
    */
    #include "internal.h"
    -
    -#include "accountopt.h"
    -#include "buddylist.h"
    -#include "conversation.h"
    -#include "debug.h"
    -#include "notify.h"
    -#include "protocol.h"
    -#include "plugins.h"
    -#include "util.h"
    -#include "version.h"
    +#include <purple.h>
    #include "simple.h"
    #include "sipmsg.h"
    --- a/libpurple/protocols/simple/sipmsg.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/simple/sipmsg.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    /**
    - * @file sipmsg.h
    - *
    * purple
    *
    * Copyright (C) 2005, Thomas Butter <butter@uni-mannheim.de>
    @@ -20,8 +18,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SIPMSG_H
    -#define _PURPLE_SIPMSG_H
    +#ifndef PURPLE_SIMPLE_SIPMSG_H
    +#define PURPLE_SIMPLE_SIPMSG_H
    #include <glib.h>
    @@ -47,4 +45,5 @@
    void sipmsg_remove_header(struct sipmsg *msg, const gchar *name);
    void sipmsg_print(const struct sipmsg *msg);
    char *sipmsg_to_string(const struct sipmsg *msg);
    -#endif /* _PURPLE_SIMPLE_H */
    +
    +#endif /* PURPLE_SIMPLE_SIPMSG_H */
    --- a/libpurple/protocols/zephyr/ZCkIfNot.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZCkIfNot.c Tue Oct 08 21:48:28 2019 -0500
    @@ -13,7 +13,7 @@
    Code_t ZCheckIfNotice(notice, from, predicate, args)
    ZNotice_t *notice;
    struct sockaddr_in *from;
    - register int (*predicate) __P((ZNotice_t *, void *));
    + register int (*predicate)(ZNotice_t *, void *);
    void *args;
    {
    ZNotice_t tmpnotice;
    --- a/libpurple/protocols/zephyr/ZGetLocs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZGetLocs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -10,28 +10,30 @@
    #include "internal.h"
    -#define min(a,b) ((a)<(b)?(a):(b))
    -
    /* Prototype for -Wmissing-prototypes */
    Code_t ZGetLocations(ZLocations_t *location, int *numlocs);
    Code_t ZGetLocations(ZLocations_t *location, int *numlocs)
    {
    - int i;
    + int i;
    - if (!__locate_list)
    - return (ZERR_NOLOCATIONS);
    + if (!__locate_list) {
    + return (ZERR_NOLOCATIONS);
    + }
    - if (__locate_next == __locate_num)
    - return (ZERR_NOMORELOCS);
    + if (__locate_next == __locate_num) {
    + return (ZERR_NOMORELOCS);
    + }
    - for (i=0;i<min(*numlocs, __locate_num-__locate_next);i++)
    - location[i] = __locate_list[i+__locate_next];
    + for (i = 0; i < MIN(*numlocs, __locate_num - __locate_next); i++) {
    + location[i] = __locate_list[i + __locate_next];
    + }
    - if (__locate_num-__locate_next < *numlocs)
    - *numlocs = __locate_num-__locate_next;
    + if (__locate_num - __locate_next < *numlocs) {
    + *numlocs = __locate_num - __locate_next;
    + }
    - __locate_next += *numlocs;
    + __locate_next += *numlocs;
    - return (ZERR_NONE);
    + return ZERR_NONE;
    }
    --- a/libpurple/protocols/zephyr/ZGetSubs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZGetSubs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -10,28 +10,31 @@
    #include "internal.h"
    -#define min(a,b) ((a)<(b)?(a):(b))
    -
    /* Prototype for -Wmissing-prototypes */
    Code_t ZGetSubscriptions(ZSubscription_t *subscription, int *numsubs);
    Code_t ZGetSubscriptions(ZSubscription_t *subscription, int *numsubs)
    {
    - int i;
    + int i;
    - if (!__subscriptions_list)
    - return (ZERR_NOSUBSCRIPTIONS);
    + if (!__subscriptions_list) {
    + return (ZERR_NOSUBSCRIPTIONS);
    + }
    - if (__subscriptions_next == __subscriptions_num)
    - return (ZERR_NOMORESUBSCRIPTIONS);
    + if (__subscriptions_next == __subscriptions_num) {
    + return (ZERR_NOMORESUBSCRIPTIONS);
    + }
    - for (i=0;i<min(*numsubs, __subscriptions_num-__subscriptions_next);i++)
    - subscription[i] = __subscriptions_list[i+__subscriptions_next];
    + for (i = 0; i < MIN(*numsubs, __subscriptions_num - __subscriptions_next);
    + i++) {
    + subscription[i] = __subscriptions_list[i + __subscriptions_next];
    + }
    - if (__subscriptions_num-__subscriptions_next < *numsubs)
    - *numsubs = __subscriptions_num-__subscriptions_next;
    + if (__subscriptions_num - __subscriptions_next < *numsubs) {
    + *numsubs = __subscriptions_num - __subscriptions_next;
    + }
    - __subscriptions_next += *numsubs;
    + __subscriptions_next += *numsubs;
    - return (ZERR_NONE);
    + return ZERR_NONE;
    }
    --- a/libpurple/protocols/zephyr/ZIfNotice.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZIfNotice.c Tue Oct 08 21:48:28 2019 -0500
    @@ -13,7 +13,7 @@
    Code_t ZIfNotice(notice, from, predicate, args)
    ZNotice_t *notice;
    struct sockaddr_in *from;
    - int (*predicate) __P((ZNotice_t *, void *));
    + int (*predicate)(ZNotice_t *, void *);
    void *args;
    {
    ZNotice_t tmpnotice;
    --- a/libpurple/protocols/zephyr/ZRetSubs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZRetSubs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -14,14 +14,7 @@
    static Code_t Z_RetSubs(ZNotice_t *notice, int *nsubs, Z_AuthProc auth_routine);
    -/* Need STDC definition when possible for unsigned short argument. */
    -#ifdef __STDC__
    Code_t ZRetrieveSubscriptions(unsigned short port, int *nsubs)
    -#else
    -Code_t ZRetrieveSubscriptions(port,nsubs)
    - unsigned short port;
    - int *nsubs;
    -#endif
    {
    int retval;
    ZNotice_t notice;
    @@ -126,7 +119,7 @@
    __subscriptions_list = (ZSubscription_t *)
    malloc((unsigned)(__subscriptions_num*
    sizeof(ZSubscription_t)));
    - if (__subscriptions_num && !__subscriptions_list) {
    + if (!__subscriptions_list) {
    ZFreeNotice(&retnotice);
    return (ENOMEM);
    }
    --- a/libpurple/protocols/zephyr/ZSubs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZSubs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -11,11 +11,10 @@
    #include "internal.h"
    -static Code_t Z_Subscriptions __P((register ZSubscription_t *sublist,
    - int nitems, unsigned int port,
    - char *opcode, int authit));
    -static Code_t subscr_sendoff __P((ZNotice_t *notice, char **lyst, int num,
    - int authit));
    +static Code_t Z_Subscriptions(register ZSubscription_t *sublist, int nitems,
    + unsigned int port, char *opcode, int authit);
    +static Code_t subscr_sendoff(ZNotice_t *notice, char **lyst, int num,
    + int authit);
    Code_t ZSubscribeTo(sublist, nitems, port)
    ZSubscription_t *sublist;
    --- a/libpurple/protocols/zephyr/ZVariables.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZVariables.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,14 +18,15 @@
    #include <pwd.h>
    #endif
    -static char *get_localvarfile __P((void));
    -static char *get_varval __P((char *fn, char *val));
    -static int varline __P((char *bfr, char *var));
    +static char *get_localvarfile(void);
    +static const gchar *get_varval(const gchar *fn, const gchar *val);
    +static int varline(const gchar *bfr, const gchar *var);
    -char *ZGetVariable(var)
    - char *var;
    +const gchar *
    +ZGetVariable(const gchar *var)
    {
    - char *varfile, *ret;
    + gchar *varfile;
    + const gchar *ret;
    if ((varfile = get_localvarfile()) == NULL)
    return ((char *)0);
    @@ -162,12 +163,11 @@
    return g_strconcat(base, "/.zephyr.vars", NULL);
    }
    -static char *get_varval(fn, var)
    - char *fn;
    - char *var;
    +static const gchar *
    +get_varval(const gchar *fn, const gchar *var)
    {
    FILE *fp;
    - static char varbfr[512];
    + static gchar varbfr[512];
    int i;
    fp = fopen(fn, "r");
    @@ -188,34 +188,35 @@
    /* If the variable in the line bfr[] is the same as var, return index to
    the variable value, else return 0. */
    -static int varline(bfr, var)
    - char *bfr;
    - char *var;
    +static int
    +varline(const gchar *bfr, const gchar *var)
    {
    - register char *cp;
    -
    + register const gchar *cp;
    - if (!bfr[0] || bfr[0] == '#') /* comment or null line */
    - return (0);
    + if (!bfr[0] || bfr[0] == '#') {
    + /* comment or null line */
    + return (0);
    + }
    - cp = bfr;
    - while (*cp && !isspace(*cp) && (*cp != '='))
    - cp++;
    + cp = bfr;
    + while (*cp && !isspace(*cp) && (*cp != '=')) {
    + cp++;
    + }
    -#ifndef WIN32
    -#define max(a,b) ((a > b) ? (a) : (b))
    -#endif
    -
    - if (g_ascii_strncasecmp(bfr, var, max(strlen(var), (gsize)(cp - bfr))))
    - return(0); /* var is not the var in
    - bfr ==> no match */
    + if (g_ascii_strncasecmp(bfr, var, MAX(strlen(var), (gsize)(cp - bfr)))) {
    + /* var is not the var in bfr ==> no match */
    + return 0;
    + }
    - cp = strchr(bfr, '=');
    - if (!cp)
    - return(0);
    - cp++;
    - while (*cp && isspace(*cp)) /* space up to variable value */
    + cp = strchr(bfr, '=');
    + if (!cp) {
    + return (0);
    + }
    cp++;
    + while (*cp && isspace(*cp)) {
    + /* space up to variable value */
    + cp++;
    + }
    - return (cp - bfr); /* return index */
    + return (cp - bfr); /* return index */
    }
    --- a/libpurple/protocols/zephyr/ZWait4Not.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/ZWait4Not.c Tue Oct 08 21:48:28 2019 -0500
    @@ -32,7 +32,7 @@
    Code_t Z_WaitForNotice (notice, pred, arg, timeout)
    ZNotice_t *notice;
    - int (*pred) __P((ZNotice_t *, void *));
    + int (*pred)(ZNotice_t *, void *);
    void *arg;
    int timeout;
    {
    --- a/libpurple/protocols/zephyr/Zinternal.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/Zinternal.c Tue Oct 08 21:48:28 2019 -0500
    @@ -57,14 +57,12 @@
    char __Zephyr_realm[REALM_SZ];
    #ifdef Z_DEBUG
    -void (*__Z_debug_print) __P((const char *fmt, va_list args, void *closure));
    +void (*__Z_debug_print)(const char *fmt, va_list args, void *closure);
    void *__Z_debug_print_closure;
    #endif
    -#define min(a,b) ((a)<(b)?(a):(b))
    -
    -static int Z_AddField __P((char **ptr, const char *field, char *end));
    -static int find_or_insert_uid __P((ZUnique_Id_t *uid, ZNotice_Kind_t kind));
    +static int Z_AddField(char **ptr, const char *field, char *end);
    +static int find_or_insert_uid(ZUnique_Id_t *uid, ZNotice_Kind_t kind);
    /* Find or insert uid in the old uids buffer. The buffer is a sorted
    * circular queue. We make the assumption that most packets arrive in
    @@ -890,7 +888,7 @@
    (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr,
    sizeof(__My_addr));
    }
    - message_len = min(notice->z_message_len-offset, fragsize);
    + message_len = MIN(notice->z_message_len - offset, fragsize);
    partnotice.z_message = (char*)notice->z_message+offset;
    partnotice.z_message_len = message_len;
    if ((retval = Z_FormatAuthHeader(&partnotice, buffer, Z_MAXHEADERLEN,
    @@ -964,7 +962,7 @@
    #undef ZSetDebug
    void ZSetDebug(proc, arg)
    - void (*proc) __P((const char *, va_list, void *));
    + void (*proc)(const char *, va_list, void *);
    char *arg;
    {
    __Z_debug_print = proc;
    --- a/libpurple/protocols/zephyr/com_err.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/com_err.h Tue Oct 08 21:48:28 2019 -0500
    @@ -8,31 +8,19 @@
    * with this package.
    */
    -#ifndef __COM_ERR_H
    -#define __COM_ERR_H
    +#ifndef PURPLE_ZEPHYR_COM_ERR_H
    +#define PURPLE_ZEPHYR_COM_ERR_H
    #define COM_ERR_BUF_LEN 25
    -/* Use __STDC__ to guess whether we can use stdarg, prototypes, and const.
    - * This is a public header file, so autoconf can't help us here. */
    -#ifdef __STDC__
    -# include <stdarg.h>
    -# define ETP(x) x
    -# define ETCONST const
    -#else
    -# define ETP(x) ()
    -# define ETCONST
    -#endif
    +#include <stdarg.h>
    -typedef void (*error_handler_t) ETP((ETCONST char *, long, ETCONST char *,
    - va_list));
    +typedef void (*error_handler_t)(const char *, long, const char *, va_list);
    extern error_handler_t com_err_hook;
    -void com_err ETP((ETCONST char *, long, ETCONST char *, ...));
    -ETCONST char *error_message ETP((long));
    -ETCONST char *error_message_r ETP((long, char *));
    -error_handler_t set_com_err_hook ETP((error_handler_t));
    -error_handler_t reset_com_err_hook ETP((void));
    +void com_err(const char *, long, const char *, ...);
    +const char *error_message(long);
    +const char *error_message_r(long, char *);
    +error_handler_t set_com_err_hook(error_handler_t);
    +error_handler_t reset_com_err_hook(void);
    -#undef ETP
    -
    -#endif /* ! defined(__COM_ERR_H) */
    +#endif /* PURPLE_ZEPHYR_COM_ERR_H */
    --- a/libpurple/protocols/zephyr/error_message.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/error_message.c Tue Oct 08 21:48:28 2019 -0500
    @@ -9,7 +9,7 @@
    #include "com_err.h"
    #include <sysdep.h>
    -char *error_table_name_r __P((int, char *));
    +char *error_table_name_r(int, char *);
    struct et_list * _et_list = (struct et_list *) NULL;
    @@ -45,10 +45,8 @@
    }
    strcpy (buf, "Unknown code ");
    - if (table_num) {
    - strcat (buf, error_table_name_r (table_num, namebuf));
    - strcat (buf, " ");
    - }
    + strcat (buf, error_table_name_r (table_num, namebuf));
    + strcat (buf, " ");
    for (cp = buf; *cp; cp++)
    ;
    if (offset >= 100) {
    --- a/libpurple/protocols/zephyr/error_table.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/error_table.h Tue Oct 08 21:48:28 2019 -0500
    @@ -5,7 +5,9 @@
    * For copyright info, see mit-sipb-copyright.h.
    */
    -#ifndef _ET_H
    +#ifndef PURPLE_ZEPHYR_ERROR_TABLE_H
    +#define PURPLE_ZEPHYR_ERROR_TABLE_H
    +
    struct error_table {
    char const * const * msgs;
    long base;
    @@ -21,5 +23,5 @@
    #define BITS_PER_CHAR 6 /* # bits to shift per character in name */
    const char *error_table_name(void);
    -#define _ET_H
    -#endif
    +
    +#endif /* PURPLE_ZEPHYR_ERROR_TABLE_H */
    --- a/libpurple/protocols/zephyr/internal.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/internal.h Tue Oct 08 21:48:28 2019 -0500
    @@ -1,5 +1,5 @@
    -#ifndef __INTERNAL_H__
    -#define __INTERNAL_H__
    +#ifndef PURPLE_ZEPHYR_INTERNAL_H
    +#define PURPLE_ZEPHYR_INTERNAL_H
    #include <sysdep.h>
    @@ -96,27 +96,23 @@
    extern int __Zephyr_port; /* Port number */
    extern struct in_addr __My_addr;
    -typedef Code_t (*Z_SendProc) __P((ZNotice_t *, char *, int, int));
    +typedef Code_t (*Z_SendProc)(ZNotice_t *, char *, int, int);
    -struct _Z_InputQ *Z_GetFirstComplete __P((void));
    -struct _Z_InputQ *Z_GetNextComplete __P((struct _Z_InputQ *));
    -Code_t Z_XmitFragment __P((ZNotice_t*, char *,int,int));
    -void Z_RemQueue __P((struct _Z_InputQ *));
    -Code_t Z_AddNoticeToEntry __P((struct _Z_InputQ*, ZNotice_t*, int));
    -Code_t Z_FormatAuthHeader __P((ZNotice_t *, char *, int, int *, Z_AuthProc));
    -Code_t Z_FormatHeader __P((ZNotice_t *, char *, int, int *, Z_AuthProc));
    -Code_t Z_FormatRawHeader __P((ZNotice_t *, char*, gsize,
    - int*, char **, char **));
    -Code_t Z_ReadEnqueue __P((void));
    -Code_t Z_ReadWait __P((void));
    -Code_t Z_SendLocation __P((char*, char*, Z_AuthProc, char*));
    -Code_t Z_SendFragmentedNotice __P((ZNotice_t *notice, int len,
    - Z_AuthProc cert_func,
    - Z_SendProc send_func));
    -Code_t Z_WaitForComplete __P((void));
    -Code_t Z_WaitForNotice __P((ZNotice_t *notice,
    - int (*pred) __P((ZNotice_t *, void *)), void *arg,
    - int timeout));
    +struct _Z_InputQ *Z_GetFirstComplete(void);
    +struct _Z_InputQ *Z_GetNextComplete(struct _Z_InputQ *);
    +Code_t Z_XmitFragment(ZNotice_t *, char *, int, int);
    +void Z_RemQueue(struct _Z_InputQ *);
    +Code_t Z_AddNoticeToEntry(struct _Z_InputQ *, ZNotice_t *, int);
    +Code_t Z_FormatAuthHeader(ZNotice_t *, char *, int, int *, Z_AuthProc);
    +Code_t Z_FormatHeader(ZNotice_t *, char *, int, int *, Z_AuthProc);
    +Code_t Z_FormatRawHeader(ZNotice_t *, char *, gsize, int *, char **, char **);
    +Code_t Z_ReadEnqueue(void);
    +Code_t Z_ReadWait(void);
    +Code_t Z_SendLocation(char *, char *, Z_AuthProc, char *);
    +Code_t Z_SendFragmentedNotice(ZNotice_t *notice, int len, Z_AuthProc cert_func,
    + Z_SendProc send_func);
    +Code_t Z_WaitForComplete(void);
    +Code_t Z_WaitForNotice(ZNotice_t *notice, int (*pred)(ZNotice_t *, void *),
    + void *arg, int timeout);
    -#endif /* __INTERNAL_H__ */
    -
    +#endif /* PURPLE_ZEPHYR_INTERNAL_H */
    --- a/libpurple/protocols/zephyr/sysdep.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/sysdep.h Tue Oct 08 21:48:28 2019 -0500
    @@ -8,8 +8,8 @@
    * "mit-copyright.h".
    */
    -#ifndef __SYSDEP_H__
    -#define __SYSDEP_H__
    +#ifndef PURPLE_ZEPHYR_SYSDEP_H
    +#define PURPLE_ZEPHYR_SYSDEP_H
    #include <config.h>
    #include <stdio.h>
    @@ -35,55 +35,17 @@
    #ifdef HAVE_SYS_WAIT_H
    # include <sys/wait.h>
    #endif
    -#ifndef WEXITSTATUS
    -# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
    -#endif
    -#ifndef WIFEXITED
    -# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
    -#endif
    #ifdef HAVE_SYS_CDEFS_H
    #include <sys/cdefs.h>
    #endif
    -/* Because we have public header files (and our prototypes need to agree with
    - * those header files, use __STDC__ to guess whether the compiler can handle
    - * stdarg, const, and prototypes. */
    -#ifdef __STDC__
    -# include <stdarg.h>
    -# define VA_START(ap, last) va_start(ap, last)
    -# ifndef __P
    -# define __P(x) x
    -# endif
    -#else
    -# include <varargs.h>
    -# define VA_START(ap, last) va_start(ap)
    -# define const
    -# ifndef __P
    -# define __P(x) ()
    -# endif
    -#endif
    -
    -/* openlog(). */
    -#ifdef LOG_AUTH
    -/* A decent syslog */
    -#define OPENLOG(str, opts, facility) openlog(str, opts, facility)
    -#else
    -/* Probably a 4.2-type syslog */
    -#define OPENLOG(str, opts, facility) openlog(str, opts)
    -#endif
    +#include <stdarg.h>
    #ifdef HAVE_FCNTL_H
    # include <fcntl.h>
    #endif
    -#ifdef HAVE_PATHS_H
    -# include <paths.h>
    -# define TEMP_DIRECTORY _PATH_VARTMP
    -#else
    -# define TEMP_DIRECTORY FOUND_TMP
    -#endif
    -
    #ifdef HAVE_UNISTD_H
    # include <unistd.h>
    #else
    @@ -92,15 +54,6 @@
    # endif
    uid_t getuid(void);
    char *ttyname(void);
    -#ifdef HAVE_GETHOSTID
    -ZEPHYR_INT32 gethostid(void);
    -#endif
    -#endif
    -
    -#ifndef STDIN_FILENO
    -#define STDIN_FILENO 0
    -#define STDOUT_FILENO 1
    -#define STDERR_FILENO 2
    #endif
    #ifdef HAVE_TERMIOS_H
    @@ -121,30 +74,18 @@
    /* Kerberos compatibility. */
    #ifdef ZEPHYR_USES_KERBEROS
    # include <krb.h>
    -#ifdef WIN32
    -
    -#else
    -# include <krb_err.h>
    -#endif /* WIN32 */
    +# ifndef WIN32
    +# include <krb_err.h>
    +# ifndef HAVE_KRB_GET_ERR_TEXT
    +# define krb_get_err_text(n) krb_err_txt[n]
    +# endif
    +# endif /* WIN32 */
    # include <des.h>
    -#ifndef WIN32
    -# ifndef HAVE_KRB_GET_ERR_TEXT
    -# define krb_get_err_text(n) krb_err_txt[n]
    -# endif
    -#endif /* WIN32 */
    # ifndef HAVE_KRB_LOG
    # define krb_log log
    # endif
    #endif /* ZEPHYR_USES_KERBEROS */
    -#ifdef HAVE_SYS_UIO_H
    -# include <sys/uio.h>
    -#endif
    -
    -#ifdef HAVE_SYS_UTSNAME_H
    -# include <sys/utsname.h>
    -#endif
    -
    #ifdef HAVE_SYS_SELECT_H
    # include <sys/select.h>
    #endif
    @@ -153,9 +94,4 @@
    #include <sys/msgbuf.h>
    #endif
    -#ifndef MSG_BSIZE
    -#define MSG_BSIZE BUFSIZ
    -#endif
    -
    -#endif /* __SYSDEP_H__ */
    -
    +#endif /* PURPLE_ZEPHYR_SYSDEP_H */
    --- a/libpurple/protocols/zephyr/zephyr.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/zephyr.c Tue Oct 08 21:48:28 2019 -0500
    @@ -692,6 +692,7 @@
    new_f->closing = "";
    }
    frames = new_f;
    + g_free(buf);
    } else {
    /* Not a formatting tag, add the character as normal. */
    g_string_append_c(frames->text, *message++);
    @@ -1368,10 +1369,11 @@
    #endif /* WIN32 */
    -static char *get_exposure_level(void)
    +static const gchar *
    +get_exposure_level(void)
    {
    /* XXX add real error reporting */
    - char *exposure = ZGetVariable("exposure");
    + const gchar *exposure = ZGetVariable("exposure");
    if (!exposure)
    return EXPOSE_REALMVIS;
    @@ -1551,22 +1553,20 @@
    g_free(filename);
    }
    -static char* normalize_zephyr_exposure(const char* exposure) {
    - char *exp2 = g_strstrip(g_ascii_strup(exposure,-1));
    -
    - if (!exp2)
    - return EXPOSE_REALMVIS;
    - if (!g_ascii_strcasecmp(exp2, EXPOSE_NONE))
    - return EXPOSE_NONE;
    - if (!g_ascii_strcasecmp(exp2, EXPOSE_OPSTAFF))
    - return EXPOSE_OPSTAFF;
    - if (!g_ascii_strcasecmp(exp2, EXPOSE_REALMANN))
    - return EXPOSE_REALMANN;
    - if (!g_ascii_strcasecmp(exp2, EXPOSE_NETVIS))
    - return EXPOSE_NETVIS;
    - if (!g_ascii_strcasecmp(exp2, EXPOSE_NETANN))
    - return EXPOSE_NETANN;
    - return EXPOSE_REALMVIS;
    +static gchar *
    +normalize_zephyr_exposure(const gchar *exposure)
    +{
    + gchar *exp2 = g_strstrip(g_ascii_strup(exposure, -1));
    +
    + if (!exp2) {
    + return g_strdup(EXPOSE_REALMVIS);
    + }
    + if (g_str_equal(exp2, EXPOSE_NONE) || g_str_equal(exp2, EXPOSE_OPSTAFF) ||
    + g_str_equal(exp2, EXPOSE_REALMANN) ||
    + g_str_equal(exp2, EXPOSE_NETVIS) || g_str_equal(exp2, EXPOSE_NETANN)) {
    + return exp2;
    + }
    + return g_strdup(EXPOSE_REALMVIS);
    }
    static void zephyr_login(PurpleAccount * account)
    @@ -1594,7 +1594,7 @@
    zephyr->account = account;
    /* Make sure that the exposure (visibility) is set to a sane value */
    - zephyr->exposure=g_strdup(normalize_zephyr_exposure(exposure));
    + zephyr->exposure = normalize_zephyr_exposure(exposure);
    if (purple_account_get_bool(purple_connection_get_account(gc),"use_tzc",0)) {
    zephyr->connection_type = PURPLE_ZEPHYR_TZC;
    @@ -2205,7 +2205,6 @@
    notice.z_kind = ACKED;
    notice.z_port = 0;
    - notice.z_opcode = "";
    notice.z_class = zclass;
    notice.z_class_inst = instance;
    notice.z_recipient = recipient;
    @@ -2263,7 +2262,7 @@
    return who;
    }
    - gc = purple_account_get_connection(account);
    + gc = purple_account_get_connection((PurpleAccount *)account);
    if (gc == NULL)
    return NULL;
    @@ -2453,17 +2452,17 @@
    return;
    if (!g_ascii_strcasecmp(classname,"%host%"))
    - classname = g_strdup(zephyr->ourhost);
    + classname = zephyr->ourhost;
    if (!g_ascii_strcasecmp(classname,"%canon%"))
    - classname = g_strdup(zephyr->ourhostcanon);
    + classname = zephyr->ourhostcanon;
    if (!instname || *instname == '\0')
    instname = "*";
    if (!g_ascii_strcasecmp(instname,"%host%"))
    - instname = g_strdup(zephyr->ourhost);
    + instname = zephyr->ourhost;
    if (!g_ascii_strcasecmp(instname,"%canon%"))
    - instname = g_strdup(zephyr->ourhostcanon);
    + instname = zephyr->ourhostcanon;
    if (!recip || (*recip == '*'))
    recip = "";
    @@ -2520,13 +2519,13 @@
    PurpleBlistNode *gnode, *cnode;
    /* XXX needs to be %host%,%canon%, and %me% clean */
    - for(gnode = purple_blist_get_root(); gnode;
    - gnode = purple_blist_node_get_sibling_next(gnode)) {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = purple_blist_node_get_sibling_next(gnode)) {
    for(cnode = purple_blist_node_get_first_child(gnode);
    cnode;
    cnode = purple_blist_node_get_sibling_next(cnode)) {
    PurpleChat *chat = (PurpleChat*)cnode;
    - char *zclass, *inst, *recip;
    + const gchar *zclass, *inst, *recip;
    char** triple;
    GHashTable *components;
    if(!PURPLE_IS_CHAT(cnode))
    @@ -2537,14 +2536,18 @@
    if(!(zclass = g_hash_table_lookup(components, "class")))
    continue;
    if(!(inst = g_hash_table_lookup(components, "instance")))
    - inst = g_strdup("");
    + inst = "";
    if(!(recip = g_hash_table_lookup(components, "recipient")))
    - recip = g_strdup("");
    + recip = "";
    /* purple_debug_info("zephyr","in zephyr_find_blist_chat name: %s\n",name?name:""); */
    triple = g_strsplit(name,",",3);
    - if (!g_ascii_strcasecmp(triple[0],zclass) && !g_ascii_strcasecmp(triple[1],inst) && !g_ascii_strcasecmp(triple[2],recip))
    + if (!g_ascii_strcasecmp(triple[0], zclass) &&
    + !g_ascii_strcasecmp(triple[1], inst) &&
    + !g_ascii_strcasecmp(triple[2], recip)) {
    + g_strfreev(triple);
    return chat;
    -
    + }
    + g_strfreev(triple);
    }
    }
    return NULL;
    @@ -2858,26 +2861,27 @@
    int retval, nsubs, one,i;
    ZSubscription_t subs;
    if (use_zeph02(zephyr)) {
    - GString* subout = g_string_new("Subscription list<br>");
    -
    - title = g_strdup_printf("Server subscriptions for %s", zephyr->username);
    + GString *subout;
    if (zephyr->port == 0) {
    - g_free(title);
    purple_debug_error("zephyr", "error while retrieving port\n");
    return;
    }
    if ((retval = ZRetrieveSubscriptions(zephyr->port,&nsubs)) != ZERR_NONE) {
    - g_free(title);
    /* XXX better error handling */
    purple_debug_error("zephyr", "error while retrieving subscriptions from server\n");
    return;
    }
    +
    + title = g_strdup_printf("Server subscriptions for %s",
    + zephyr->username);
    + subout = g_string_new("Subscription list<br>");
    for(i=0;i<nsubs;i++) {
    one = 1;
    if ((retval = ZGetSubscriptions(&subs,&one)) != ZERR_NONE) {
    /* XXX better error handling */
    g_free(title);
    + g_string_free(subout, TRUE);
    purple_debug_error("zephyr", "error while retrieving individual subscription\n");
    return;
    }
    @@ -2912,10 +2916,11 @@
    static void
    -zephyr_protocol_init(PurpleProtocol *protocol)
    +zephyr_protocol_init(ZephyrProtocol *self)
    {
    + PurpleProtocol *protocol = PURPLE_PROTOCOL(self);
    PurpleAccountOption *option;
    - char *tmp = get_exposure_level();
    + const gchar *tmp = get_exposure_level();
    protocol->id = "prpl-zephyr";
    protocol->name = "Zephyr";
    @@ -2942,7 +2947,8 @@
    option = purple_account_option_string_new(_("Realm"), "realm", "");
    protocol->account_options = g_list_append(protocol->account_options, option);
    - option = purple_account_option_string_new(_("Exposure"), "exposure_level", tmp?tmp: EXPOSE_REALMVIS);
    + option = purple_account_option_string_new(_("Exposure"), "exposure_level",
    + tmp);
    protocol->account_options = g_list_append(protocol->account_options, option);
    option = purple_account_option_string_new(_("Encoding"), "encoding", ZEPHYR_FALLBACK_CHARSET);
    @@ -2951,17 +2957,25 @@
    static void
    -zephyr_protocol_class_init(PurpleProtocolClass *klass)
    +zephyr_protocol_class_init(ZephyrProtocolClass *klass)
    {
    - klass->login = zephyr_login;
    - klass->close = zephyr_close;
    - klass->status_types = zephyr_status_types;
    - klass->list_icon = zephyr_list_icon;
    + PurpleProtocolClass *protocol_class = PURPLE_PROTOCOL_CLASS(klass);
    +
    + protocol_class->login = zephyr_login;
    + protocol_class->close = zephyr_close;
    + protocol_class->status_types = zephyr_status_types;
    + protocol_class->list_icon = zephyr_list_icon;
    }
    static void
    -zephyr_protocol_client_iface_init(PurpleProtocolClientIface *client_iface)
    +zephyr_protocol_class_finalize(G_GNUC_UNUSED ZephyrProtocolClass *klass)
    +{
    +}
    +
    +
    +static void
    +zephyr_protocol_client_iface_init(PurpleProtocolClientInterface *client_iface)
    {
    client_iface->get_actions = zephyr_get_actions;
    client_iface->normalize = zephyr_normalize;
    @@ -2970,7 +2984,7 @@
    static void
    -zephyr_protocol_server_iface_init(PurpleProtocolServerIface *server_iface)
    +zephyr_protocol_server_iface_init(PurpleProtocolServerInterface *server_iface)
    {
    server_iface->get_info = zephyr_zloc;
    server_iface->set_status = zephyr_set_status;
    @@ -2981,7 +2995,7 @@
    static void
    -zephyr_protocol_im_iface_init(PurpleProtocolIMIface *im_iface)
    +zephyr_protocol_im_iface_init(PurpleProtocolIMInterface *im_iface)
    {
    im_iface->send = zephyr_send_im;
    im_iface->send_typing = zephyr_send_typing;
    @@ -2989,7 +3003,7 @@
    static void
    -zephyr_protocol_chat_iface_init(PurpleProtocolChatIface *chat_iface)
    +zephyr_protocol_chat_iface_init(PurpleProtocolChatInterface *chat_iface)
    {
    chat_iface->info = zephyr_chat_info;
    chat_iface->join = zephyr_join_chat;
    @@ -3002,22 +3016,20 @@
    }
    -PURPLE_DEFINE_TYPE_EXTENDED(
    - ZephyrProtocol, zephyr_protocol, PURPLE_TYPE_PROTOCOL, 0,
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CLIENT_IFACE,
    - zephyr_protocol_client_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_SERVER_IFACE,
    - zephyr_protocol_server_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_IM_IFACE,
    - zephyr_protocol_im_iface_init)
    -
    - PURPLE_IMPLEMENT_INTERFACE_STATIC(PURPLE_TYPE_PROTOCOL_CHAT_IFACE,
    - zephyr_protocol_chat_iface_init)
    -);
    -
    +G_DEFINE_DYNAMIC_TYPE_EXTENDED(
    + ZephyrProtocol, zephyr_protocol, PURPLE_TYPE_PROTOCOL, 0,
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CLIENT,
    + zephyr_protocol_client_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_SERVER,
    + zephyr_protocol_server_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_IM,
    + zephyr_protocol_im_iface_init)
    +
    + G_IMPLEMENT_INTERFACE_DYNAMIC(PURPLE_TYPE_PROTOCOL_CHAT,
    + zephyr_protocol_chat_iface_init));
    static PurplePluginInfo *plugin_query(GError **error)
    {
    @@ -3040,7 +3052,7 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - zephyr_protocol_register_type(plugin);
    + zephyr_protocol_register_type(G_TYPE_MODULE(plugin));
    my_protocol = purple_protocols_add(ZEPHYR_TYPE_PROTOCOL, error);
    if (!my_protocol)
    --- a/libpurple/protocols/zephyr/zephyr.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/zephyr.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,9 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _ZEPHYR_H_
    -#define _ZEPHYR_H_
    +
    +#ifndef PURPLE_ZEPHYR_ZEPHYR_H
    +#define PURPLE_ZEPHYR_ZEPHYR_H
    #include <gmodule.h>
    @@ -48,4 +49,4 @@
    */
    G_MODULE_EXPORT GType zephyr_protocol_get_type(void);
    -#endif /* _ZEPHYR_H_ */
    +#endif /* PURPLE_ZEPHYR_ZEPHYR_H */
    --- a/libpurple/protocols/zephyr/zephyr_err.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/zephyr_err.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1,12 +1,5 @@
    #include "zephyr_err.h"
    -#ifdef __STDC__
    -#define NOARGS void
    -#else
    -#define NOARGS
    -#define const
    -#endif
    -
    static const char * const text[] = {
    "Packet too long or buffer too small",
    "Notice header too large",
    @@ -49,7 +42,9 @@
    static struct et_list link = { 0, 0 };
    -void initialize_zeph_error_table (NOARGS) {
    +void
    +initialize_zeph_error_table(void)
    +{
    if (!link.table) {
    link.next = _et_list;
    link.table = &et;
    --- a/libpurple/protocols/zephyr/zephyr_internal.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/protocols/zephyr/zephyr_internal.h Tue Oct 08 21:48:28 2019 -0500
    @@ -8,8 +8,8 @@
    * file "mit-copyright.h".
    */
    -#ifndef __ZEPHYR_H__
    -#define __ZEPHYR_H__
    +#ifndef PURPLE_ZEPHYR_ZEPHYR_INTERNAL_H
    +#define PURPLE_ZEPHYR_ZEPHYR_INTERNAL_H
    #include <config.h>
    @@ -26,16 +26,7 @@
    #endif
    #endif
    -/* Use __STDC__ to guess whether we can use stdarg, prototypes, and const.
    - * This is a public header file, so autoconf can't help us here. */
    -#ifdef __STDC__
    -# include <stdarg.h>
    -# define ZP(x) x
    -# define ZCONST const
    -#else
    -# define ZP(x) ()
    -# define ZCONST
    -#endif
    +#include <stdarg.h>
    #ifdef WIN32
    /* this really should be uint32_t */
    @@ -74,7 +65,7 @@
    typedef enum {
    UNSAFE, UNACKED, ACKED, HMACK, HMCTL, SERVACK, SERVNAK, CLIENTACK, STAT
    } ZNotice_Kind_t;
    -extern ZCONST char *ZNoticeKinds[9];
    +extern const char *ZNoticeKinds[9];
    /* Unique ID format */
    typedef struct {
    @@ -138,90 +129,84 @@
    /* for ZSetDebug */
    #ifdef Z_DEBUG
    -void (*__Z_debug_print) ZP((ZCONST char *fmt, va_list args, void *closure));
    +void (*__Z_debug_print)(const char *fmt, va_list args, void *closure);
    void *__Z_debug_print_closure;
    #endif
    -int ZCompareUIDPred ZP((ZNotice_t *, void *));
    -int ZCompareMultiUIDPred ZP((ZNotice_t *, void *));
    +int ZCompareUIDPred(ZNotice_t *, void *);
    +int ZCompareMultiUIDPred(ZNotice_t *, void *);
    /* Defines for ZFormatNotice, et al. */
    -typedef Code_t (*Z_AuthProc) ZP((ZNotice_t*, char *, int, int *));
    -Code_t ZMakeAuthentication ZP((ZNotice_t*, char *,int, int*));
    +typedef Code_t (*Z_AuthProc)(ZNotice_t *, char *, int, int *);
    +Code_t ZMakeAuthentication(ZNotice_t *, char *, int, int *);
    -char *ZGetSender ZP((void));
    -char *ZGetVariable ZP((char *));
    -Code_t ZSetVariable ZP((char *var, char *value));
    -Code_t ZUnsetVariable ZP((char *var));
    -int ZGetWGPort ZP((void));
    -Code_t ZSetDestAddr ZP((struct sockaddr_in *));
    -Code_t ZFormatNoticeList ZP((ZNotice_t*, char**, int,
    - char **, int*, Z_AuthProc));
    -Code_t ZParseNotice ZP((char*, int, ZNotice_t *));
    -Code_t ZReadAscii ZP((char*, int, unsigned char*, int));
    -Code_t ZReadAscii32 ZP((char *, int, unsigned long *));
    -Code_t ZReadAscii16 ZP((char *, int, unsigned short *));
    -Code_t ZSendPacket ZP((char*, int, int));
    -Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc));
    -Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Code_t (*)()));
    -Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc));
    -Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Code_t (*)()));
    -Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc));
    -Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc));
    -Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems,
    - char **buffer, int *ret_len));
    -Code_t ZLocateUser ZP((char *, int *, Z_AuthProc));
    -Code_t ZRequestLocations ZP((const char *, ZAsyncLocateData_t *,
    - ZNotice_Kind_t, Z_AuthProc));
    -Code_t ZhmStat ZP((struct in_addr *, ZNotice_t *));
    -Code_t ZInitialize ZP((void));
    -Code_t ZSetServerState ZP((int));
    -Code_t ZSetFD ZP((int));
    -Code_t ZFormatSmallRawNotice ZP((ZNotice_t*, ZPacket_t, int*));
    -int ZCompareUID ZP((ZUnique_Id_t*, ZUnique_Id_t*));
    -Code_t ZMakeAscii ZP((char*, int, unsigned char*, int));
    -Code_t ZMakeAscii32 ZP((char *, int, unsigned long));
    -Code_t ZMakeAscii16 ZP((char *, int, unsigned int));
    -Code_t ZReceivePacket ZP((ZPacket_t, int*, struct sockaddr_in*));
    -Code_t ZCheckAuthentication ZP((ZNotice_t*, struct sockaddr_in*));
    -Code_t ZSetLocation ZP((char *exposure));
    -Code_t ZUnsetLocation ZP((void));
    -Code_t ZFlushMyLocations ZP((void));
    -Code_t ZFormatRawNotice ZP((ZNotice_t *, char**, int *));
    -Code_t ZRetrieveSubscriptions ZP((unsigned short, int*));
    -Code_t ZOpenPort ZP((unsigned short *port));
    -Code_t ZClosePort ZP((void));
    -Code_t ZFlushLocations ZP((void));
    -Code_t ZFlushSubscriptions ZP((void));
    -Code_t ZFreeNotice ZP((ZNotice_t *notice));
    -Code_t ZParseLocations ZP((register ZNotice_t *notice,
    - register ZAsyncLocateData_t *zald, int *nlocs,
    - char **user));
    -int ZCompareALDPred ZP((ZNotice_t *notice, void *zald));
    -void ZFreeALD ZP((register ZAsyncLocateData_t *zald));
    -Code_t ZCheckIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from,
    - register int (*predicate) ZP((ZNotice_t *,void *)),
    - void *args));
    -Code_t ZPeekPacket ZP((char **buffer, int *ret_len,
    - struct sockaddr_in *from));
    -Code_t ZPeekNotice ZP((ZNotice_t *notice, struct sockaddr_in *from));
    -Code_t ZIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from,
    - int (*predicate) ZP((ZNotice_t *, void *)), void *args));
    -Code_t ZSubscribeTo ZP((ZSubscription_t *sublist, int nitems,
    - unsigned int port));
    -Code_t ZSubscribeToSansDefaults ZP((ZSubscription_t *sublist, int nitems,
    - unsigned int port));
    -Code_t ZUnsubscribeTo ZP((ZSubscription_t *sublist, int nitems,
    - unsigned int port));
    -Code_t ZCancelSubscriptions ZP((unsigned int port));
    -int ZPending ZP((void));
    -Code_t ZReceiveNotice ZP((ZNotice_t *notice, struct sockaddr_in *from));
    +char *ZGetSender(void);
    +const gchar *ZGetVariable(const gchar *);
    +Code_t ZSetVariable(char *var, char *value);
    +Code_t ZUnsetVariable(char *var);
    +int ZGetWGPort(void);
    +Code_t ZSetDestAddr(struct sockaddr_in *);
    +Code_t ZFormatNoticeList(ZNotice_t *, char **, int, char **, int *, Z_AuthProc);
    +Code_t ZParseNotice(char *, int, ZNotice_t *);
    +Code_t ZReadAscii(char *, int, unsigned char *, int);
    +Code_t ZReadAscii32(char *, int, unsigned long *);
    +Code_t ZReadAscii16(char *, int, unsigned short *);
    +Code_t ZSendPacket(char *, int, int);
    +Code_t ZSendList(ZNotice_t *, char *[], int, Z_AuthProc);
    +Code_t ZSrvSendList(ZNotice_t *, char *[], int, Z_AuthProc, Code_t (*)());
    +Code_t ZSendNotice(ZNotice_t *, Z_AuthProc);
    +Code_t ZSrvSendNotice(ZNotice_t *, Z_AuthProc, Code_t (*)());
    +Code_t ZFormatNotice(ZNotice_t *, char **, int *, Z_AuthProc);
    +Code_t ZFormatSmallNotice(ZNotice_t *, ZPacket_t, int *, Z_AuthProc);
    +Code_t ZFormatRawNoticeList(ZNotice_t *notice, char *list[], int nitems,
    + char **buffer, int *ret_len);
    +Code_t ZLocateUser(char *, int *, Z_AuthProc);
    +Code_t ZRequestLocations(const char *, ZAsyncLocateData_t *, ZNotice_Kind_t,
    + Z_AuthProc);
    +Code_t ZhmStat(struct in_addr *, ZNotice_t *);
    +Code_t ZInitialize(void);
    +Code_t ZSetServerState(int);
    +Code_t ZSetFD(int);
    +Code_t ZFormatSmallRawNotice(ZNotice_t *, ZPacket_t, int *);
    +int ZCompareUID(ZUnique_Id_t *, ZUnique_Id_t *);
    +Code_t ZMakeAscii(char *, int, unsigned char *, int);
    +Code_t ZMakeAscii32(char *, int, unsigned long);
    +Code_t ZMakeAscii16(char *, int, unsigned int);
    +Code_t ZReceivePacket(ZPacket_t, int *, struct sockaddr_in *);
    +Code_t ZCheckAuthentication(ZNotice_t *, struct sockaddr_in *);
    +Code_t ZSetLocation(char *exposure);
    +Code_t ZUnsetLocation(void);
    +Code_t ZFlushMyLocations(void);
    +Code_t ZFormatRawNotice(ZNotice_t *, char **, int *);
    +Code_t ZRetrieveSubscriptions(unsigned short, int *);
    +Code_t ZOpenPort(unsigned short *port);
    +Code_t ZClosePort(void);
    +Code_t ZFlushLocations(void);
    +Code_t ZFlushSubscriptions(void);
    +Code_t ZFreeNotice(ZNotice_t *notice);
    +Code_t ZParseLocations(register ZNotice_t *notice,
    + register ZAsyncLocateData_t *zald, int *nlocs,
    + char **user);
    +int ZCompareALDPred(ZNotice_t *notice, void *zald);
    +void ZFreeALD(register ZAsyncLocateData_t *zald);
    +Code_t ZCheckIfNotice(ZNotice_t *notice, struct sockaddr_in *from,
    + register int (*predicate)(ZNotice_t *, void *),
    + void *args);
    +Code_t ZPeekPacket(char **buffer, int *ret_len, struct sockaddr_in *from);
    +Code_t ZPeekNotice(ZNotice_t *notice, struct sockaddr_in *from);
    +Code_t ZIfNotice(ZNotice_t *notice, struct sockaddr_in *from,
    + int (*predicate)(ZNotice_t *, void *), void *args);
    +Code_t ZSubscribeTo(ZSubscription_t *sublist, int nitems, unsigned int port);
    +Code_t ZSubscribeToSansDefaults(ZSubscription_t *sublist, int nitems,
    + unsigned int port);
    +Code_t ZUnsubscribeTo(ZSubscription_t *sublist, int nitems, unsigned int port);
    +Code_t ZCancelSubscriptions(unsigned int port);
    +int ZPending(void);
    +Code_t ZReceiveNotice(ZNotice_t *notice, struct sockaddr_in *from);
    #ifdef Z_DEBUG
    -void Z_debug ZP((ZCONST char *, ...));
    +void Z_debug(const char *, ...);
    #endif
    -#undef ZP
    -
    /* Compatibility */
    #define ZNewLocateUser ZLocateUser
    @@ -236,7 +221,7 @@
    #define ZGetRealm() __Zephyr_realm
    #ifdef Z_DEBUG
    -void ZSetDebug ZP((void (*)(ZCONST char *, va_list, void *), void *));
    +void ZSetDebug(void (*)(const char *, va_list, void *), void *);
    #define ZSetDebug(proc,closure) (__Z_debug_print=(proc), \
    __Z_debug_print_closure=(closure), \
    (void) 0)
    @@ -336,4 +321,4 @@
    #define USER_STARTUP "STARTUP" /* Opcode: Come out of it */
    #define USER_EXIT "EXIT" /* Opcode: Exit the client */
    -#endif /* __ZEPHYR_H__ */
    +#endif /* PURPLE_ZEPHYR_ZEPHYR_INTERNAL_H */
    --- a/libpurple/proxy.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/proxy.c Tue Oct 08 21:48:28 2019 -0500
    @@ -24,7 +24,6 @@
    /* it is intended to : 1st handle http proxy, using the CONNECT command
    , 2nd provide an easy way to add socks support
    , 3rd draw women to it like flies to honey */
    -#define _PURPLE_PROXY_C_
    #include "internal.h"
    #include "debug.h"
    --- a/libpurple/proxy.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/proxy.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_PROXY_H_
    -#define _PURPLE_PROXY_H_
    +#ifndef PURPLE_PROXY_H
    +#define PURPLE_PROXY_H
    /**
    * SECTION:proxy
    * @section_id: libpurple-proxy
    @@ -355,4 +355,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_PROXY_H_ */
    +#endif /* PURPLE_PROXY_H */
    --- a/libpurple/purple-gio.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/purple-gio.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_GIO_H
    -#define _PURPLE_GIO_H
    +#ifndef PURPLE_GIO_H
    +#define PURPLE_GIO_H
    /**
    * SECTION:purple-gio
    * @section_id: libpurple-purple-gio
    @@ -73,4 +73,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_GIO_H */
    +#endif /* PURPLE_GIO_H */
    --- a/libpurple/purple.h.in Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/purple.h.in Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_PURPLE_H_
    -#define _PURPLE_PURPLE_H_
    +#ifndef PURPLE_PURPLE_H
    +#define PURPLE_PURPLE_H
    /**
    * SECTION:purple
    * @section_id: libpurple-purple
    @@ -39,14 +39,6 @@
    # include <config.h>
    #endif
    -#ifndef G_GNUC_NULL_TERMINATED
    -# if __GNUC__ >= 4
    -# define G_GNUC_NULL_TERMINATED __attribute__((__sentinel__))
    -# else
    -# define G_GNUC_NULL_TERMINATED
    -# endif
    -#endif
    -
    @PURPLE_H_INCLUDES@
    -#endif
    +#endif /* PURPLE_PURPLE_H */
    --- a/libpurple/queuedoutputstream.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/queuedoutputstream.h Tue Oct 08 21:48:28 2019 -0500
    @@ -21,8 +21,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_QUEUED_OUTPUT_STREAM_H
    -#define _PURPLE_QUEUED_OUTPUT_STREAM_H
    +#ifndef PURPLE_QUEUED_OUTPUT_STREAM_H
    +#define PURPLE_QUEUED_OUTPUT_STREAM_H
    /**
    * SECTION:queuedoutputstream
    * @section_id: libpurple-queuedoutputstream
    @@ -106,4 +106,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_QUEUED_OUTPUT_STREAM_H */
    +#endif /* PURPLE_QUEUED_OUTPUT_STREAM_H */
    --- a/libpurple/request-datasheet.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/request-datasheet.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_REQUEST_DATA_H_
    -#define _PURPLE_REQUEST_DATA_H_
    +#ifndef PURPLE_REQUEST_DATA_H
    +#define PURPLE_REQUEST_DATA_H
    /**
    * SECTION:request-datasheet
    * @section_id: libpurple-request-datasheet
    @@ -416,4 +416,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_REQUEST_DATA_H_ */
    +#endif /* PURPLE_REQUEST_DATA_H */
    --- a/libpurple/request.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/request.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#define _PURPLE_REQUEST_C_
    #include "internal.h"
    @@ -493,8 +492,8 @@
    g_return_if_fail(fields != NULL);
    g_strfreev(fields->tab_names);
    - g_list_foreach(fields->groups, (GFunc)purple_request_field_group_destroy, NULL);
    - g_list_free(fields->groups);
    + g_list_free_full(fields->groups,
    + (GDestroyNotify)purple_request_field_group_destroy);
    g_list_free(fields->required_fields);
    g_list_free(fields->validated_fields);
    g_list_free(fields->autosensitive_fields);
    @@ -843,8 +842,8 @@
    g_free(group->title);
    - g_list_foreach(group->fields, (GFunc)purple_request_field_destroy, NULL);
    - g_list_free(group->fields);
    + g_list_free_full(group->fields,
    + (GDestroyNotify)purple_request_field_destroy);
    g_free(group);
    }
    @@ -967,18 +966,8 @@
    }
    else if (field->type == PURPLE_REQUEST_FIELD_LIST)
    {
    - if (field->u.list.items != NULL)
    - {
    - g_list_foreach(field->u.list.items, (GFunc)g_free, NULL);
    - g_list_free(field->u.list.items);
    - }
    -
    - if (field->u.list.selected != NULL)
    - {
    - g_list_foreach(field->u.list.selected, (GFunc)g_free, NULL);
    - g_list_free(field->u.list.selected);
    - }
    -
    + g_list_free_full(field->u.list.items, g_free);
    + g_list_free_full(field->u.list.selected, g_free);
    g_hash_table_destroy(field->u.list.item_data);
    g_hash_table_destroy(field->u.list.selected_table);
    }
    @@ -1670,8 +1659,7 @@
    if (field->u.list.selected != NULL)
    {
    - g_list_foreach(field->u.list.selected, (GFunc)g_free, NULL);
    - g_list_free(field->u.list.selected);
    + g_list_free_full(field->u.list.selected, g_free);
    field->u.list.selected = NULL;
    }
    @@ -1692,9 +1680,7 @@
    purple_request_field_list_clear_selected(field);
    - if (!purple_request_field_list_get_multi_select(field) &&
    - items && items->next)
    - {
    + if (!purple_request_field_list_get_multi_select(field) && items->next) {
    purple_debug_warning("request",
    "More than one item added to non-multi-select "
    "field %s\n",
    --- a/libpurple/request.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/request.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_REQUEST_H_
    -#define _PURPLE_REQUEST_H_
    +#ifndef PURPLE_REQUEST_H
    +#define PURPLE_REQUEST_H
    /**
    * SECTION:request
    * @section_id: libpurple-request
    @@ -357,7 +357,8 @@
    *
    * Gets the #PurpleAccount associated with the request.
    *
    - * Returns: The associated #PurpleAccount, or NULL if none is.
    + * Returns: (transfer none): The associated #PurpleAccount, or %NULL if none is
    + * set.
    */
    PurpleAccount *
    purple_request_cpar_get_account(PurpleRequestCommonParameters *cpar);
    @@ -380,7 +381,8 @@
    *
    * Gets the #PurpleConversation associated with the request.
    *
    - * Returns: The associated #PurpleConversation, or NULL if none is.
    + * Returns: (transfer none): The associated #PurpleConversation, or %NULL if
    + * none is set.
    */
    PurpleConversation *
    purple_request_cpar_get_conversation(PurpleRequestCommonParameters *cpar);
    @@ -526,7 +528,8 @@
    *
    * Gets extra actions for the PurpleRequestFields dialog.
    *
    - * Returns: A list of actions (pairs of arguments, as in setter).
    + * Returns: (transfer none): A list of actions (pairs of arguments, as in
    + * setter).
    */
    GSList *
    purple_request_cpar_get_extra_actions(PurpleRequestCommonParameters *cpar);
    @@ -612,8 +615,8 @@
    *
    * Returns tab names of a field list.
    *
    - * Returns: NULL-terminated array of localized tab labels, or NULL if tabs
    - * are disabled.
    + * Returns: (array zero-terminated=1) (transfer none): Localized tab labels, or
    + * %NULL if tabs are disabled.
    */
    const gchar **
    purple_request_fields_get_tab_names(const PurpleRequestFields *fields);
    @@ -765,7 +768,7 @@
    *
    * Returns the account of a field with the specified ID.
    *
    - * Returns: The account value, if found, or NULL otherwise.
    + * Returns: (transfer none): The account value, if found, or %NULL otherwise.
    */
    PurpleAccount *purple_request_fields_get_account(const PurpleRequestFields *fields,
    const char *id);
    @@ -1779,7 +1782,7 @@
    *
    * Returns the default account in an account field.
    *
    - * Returns: The default account.
    + * Returns: (transfer none): The default account.
    */
    PurpleAccount *purple_request_field_account_get_default_value(
    const PurpleRequestField *field);
    @@ -1790,7 +1793,7 @@
    *
    * Returns the user-entered account in an account field.
    *
    - * Returns: The user-entered account.
    + * Returns: (transfer none): The user-entered account.
    */
    PurpleAccount *purple_request_field_account_get_value(
    const PurpleRequestField *field);
    @@ -2381,4 +2384,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_REQUEST_H_ */
    +#endif /* PURPLE_REQUEST_H */
    --- a/libpurple/roomlist.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/roomlist.c Tue Oct 08 21:48:28 2019 -0500
    @@ -94,21 +94,21 @@
    PurpleAccount *purple_roomlist_get_account(PurpleRoomlist *list)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
    + priv = purple_roomlist_get_instance_private(list);
    return priv->account;
    }
    void purple_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    + priv = purple_roomlist_get_instance_private(list);
    priv->fields = fields;
    if (ops && ops->set_fields)
    @@ -119,11 +119,11 @@
    void purple_roomlist_set_in_progress(PurpleRoomlist *list, gboolean in_progress)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    + priv = purple_roomlist_get_instance_private(list);
    priv->in_progress = in_progress;
    if (ops && ops->in_progress)
    @@ -134,22 +134,22 @@
    gboolean purple_roomlist_get_in_progress(PurpleRoomlist *list)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), FALSE);
    + priv = purple_roomlist_get_instance_private(list);
    return priv->in_progress;
    }
    void purple_roomlist_room_add(PurpleRoomlist *list, PurpleRoomlistRoom *room)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    g_return_if_fail(room != NULL);
    + priv = purple_roomlist_get_instance_private(list);
    priv->rooms = g_list_append(priv->rooms, room);
    if (ops && ops->add_room)
    @@ -173,15 +173,15 @@
    void purple_roomlist_cancel_get_list(PurpleRoomlist *list)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    PurpleProtocol *protocol = NULL;
    PurpleConnection *gc;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    +
    + priv = purple_roomlist_get_instance_private(list);
    gc = purple_account_get_connection(priv->account);
    -
    g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    if(gc)
    @@ -193,15 +193,16 @@
    void purple_roomlist_expand_category(PurpleRoomlist *list, PurpleRoomlistRoom *category)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    PurpleProtocol *protocol = NULL;
    PurpleConnection *gc;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    g_return_if_fail(category != NULL);
    g_return_if_fail(category->type & PURPLE_ROOMLIST_ROOMTYPE_CATEGORY);
    + priv = purple_roomlist_get_instance_private(list);
    +
    gc = purple_account_get_connection(priv->account);
    g_return_if_fail(PURPLE_IS_CONNECTION(gc));
    @@ -214,31 +215,31 @@
    GList * purple_roomlist_get_fields(PurpleRoomlist *list)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
    + priv = purple_roomlist_get_instance_private(list);
    return priv->fields;
    }
    gpointer purple_roomlist_get_protocol_data(PurpleRoomlist *list)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_ROOMLIST(list), NULL);
    + priv = purple_roomlist_get_instance_private(list);
    return priv->proto_data;
    }
    void purple_roomlist_set_protocol_data(PurpleRoomlist *list, gpointer proto_data)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    + priv = purple_roomlist_get_instance_private(list);
    priv->proto_data = proto_data;
    }
    @@ -345,8 +346,7 @@
    }
    g_list_free(priv->rooms);
    - g_list_foreach(priv->fields, (GFunc)purple_roomlist_field_free, NULL);
    - g_list_free(priv->fields);
    + g_list_free_full(priv->fields, (GDestroyNotify)purple_roomlist_field_free);
    G_OBJECT_CLASS(purple_roomlist_parent_class)->finalize(object);
    }
    @@ -393,7 +393,7 @@
    g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, roomlist_new))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, roomlist_new))
    list = purple_protocol_factory_iface_roomlist_new(protocol, account);
    else
    list = g_object_new(PURPLE_TYPE_ROOMLIST,
    @@ -406,6 +406,70 @@
    return list;
    }
    +/**************************************************************************
    + * Protocol Roomlist Interface API
    + **************************************************************************/
    +#define DEFINE_PROTOCOL_FUNC(protocol,funcname,...) \
    + PurpleProtocolRoomlistInterface *roomlist_iface = \
    + PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
    + if (roomlist_iface && roomlist_iface->funcname) \
    + roomlist_iface->funcname(__VA_ARGS__);
    +
    +#define DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol,defaultreturn,funcname,...) \
    + PurpleProtocolRoomlistInterface *roomlist_iface = \
    + PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(protocol); \
    + if (roomlist_iface && roomlist_iface->funcname) \
    + return roomlist_iface->funcname(__VA_ARGS__); \
    + else \
    + return defaultreturn;
    +
    +GType
    +purple_protocol_roomlist_iface_get_type(void)
    +{
    + static GType type = 0;
    +
    + if (G_UNLIKELY(type == 0)) {
    + static const GTypeInfo info = {
    + .class_size = sizeof(PurpleProtocolRoomlistInterface),
    + };
    +
    + type = g_type_register_static(G_TYPE_INTERFACE,
    + "PurpleProtocolRoomlistInterface", &info, 0);
    + }
    + return type;
    +}
    +
    +PurpleRoomlist *
    +purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol,
    + PurpleConnection *gc)
    +{
    + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, get_list, gc);
    +}
    +
    +void
    +purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol,
    + PurpleRoomlist *list)
    +{
    + DEFINE_PROTOCOL_FUNC(protocol, cancel, list);
    +}
    +
    +void
    +purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol,
    + PurpleRoomlist *list, PurpleRoomlistRoom *category)
    +{
    + DEFINE_PROTOCOL_FUNC(protocol, expand_category, list, category);
    +}
    +
    +char *
    +purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol,
    + PurpleRoomlistRoom *room)
    +{
    + DEFINE_PROTOCOL_FUNC_WITH_RETURN(protocol, NULL, room_serialize, room);
    +}
    +
    +#undef DEFINE_PROTOCOL_FUNC_WITH_RETURN
    +#undef DEFINE_PROTOCOL_FUNC
    +
    /**************************************************************************/
    /* Room API */
    /**************************************************************************/
    @@ -427,12 +491,13 @@
    void purple_roomlist_room_add_field(PurpleRoomlist *list, PurpleRoomlistRoom *room, gconstpointer field)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    PurpleRoomlistField *f;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    g_return_if_fail(room != NULL);
    +
    + priv = purple_roomlist_get_instance_private(list);
    g_return_if_fail(priv->fields != NULL);
    /* If this is the first call for this room, grab the first field in
    @@ -462,15 +527,16 @@
    void purple_roomlist_room_join(PurpleRoomlist *list, PurpleRoomlistRoom *room)
    {
    - PurpleRoomlistPrivate *priv =
    - purple_roomlist_get_instance_private(list);
    + PurpleRoomlistPrivate *priv = NULL;
    GHashTable *components;
    GList *l, *j;
    PurpleConnection *gc;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_ROOMLIST(list));
    g_return_if_fail(room != NULL);
    + priv = purple_roomlist_get_instance_private(list);
    +
    gc = purple_account_get_connection(priv->account);
    if (!gc)
    return;
    --- a/libpurple/roomlist.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/roomlist.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_ROOMLIST_H_
    -#define _PURPLE_ROOMLIST_H_
    +#ifndef PURPLE_ROOMLIST_H
    +#define PURPLE_ROOMLIST_H
    /**
    * SECTION:roomlist
    * @section_id: libpurple-roomlist
    @@ -28,15 +28,8 @@
    * @title: Room List API
    */
    -#define PURPLE_TYPE_ROOMLIST (purple_roomlist_get_type())
    -#define PURPLE_ROOMLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_ROOMLIST, PurpleRoomlist))
    -#define PURPLE_ROOMLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_ROOMLIST, PurpleRoomlistClass))
    -#define PURPLE_IS_ROOMLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_ROOMLIST))
    -#define PURPLE_IS_ROOMLIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_ROOMLIST))
    -#define PURPLE_ROOMLIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_ROOMLIST, PurpleRoomlistClass))
    -
    +#define PURPLE_TYPE_ROOMLIST (purple_roomlist_get_type())
    typedef struct _PurpleRoomlist PurpleRoomlist;
    -typedef struct _PurpleRoomlistClass PurpleRoomlistClass;
    #define PURPLE_TYPE_ROOMLIST_ROOM (purple_roomlist_room_get_type())
    @@ -131,21 +124,6 @@
    gpointer ui_data;
    };
    -/**
    - * PurpleRoomlistClass:
    - *
    - * Base class for all #PurpleRoomlist's
    - */
    -struct _PurpleRoomlistClass {
    - GObjectClass parent_class;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    G_BEGIN_DECLS
    /**************************************************************************/
    @@ -157,7 +135,7 @@
    *
    * Returns: The #GType for the Room List object.
    */
    -GType purple_roomlist_get_type(void);
    +G_DECLARE_FINAL_TYPE(PurpleRoomlist, purple_roomlist, PURPLE, ROOMLIST, GObject)
    /**
    * purple_roomlist_show_with_account:
    @@ -188,7 +166,7 @@
    * Retrieve the PurpleAccount that was given when the room list was
    * created.
    *
    - * Returns: The PurpleAccount tied to this room list.
    + * Returns: (transfer none): The PurpleAccount tied to this room list.
    */
    PurpleAccount *purple_roomlist_get_account(PurpleRoomlist *list);
    @@ -246,8 +224,8 @@
    * Returns a PurpleRoomlist structure from the protocol, and
    * instructs the protocol to start fetching the list.
    *
    - * Returns: A PurpleRoomlist* or %NULL if the protocol
    - * doesn't support that.
    + * Returns: (transfer full): A PurpleRoomlist* or %NULL if the protocol doesn't
    + * support that.
    */
    PurpleRoomlist *purple_roomlist_get_list(PurpleConnection *gc);
    @@ -329,6 +307,106 @@
    void purple_roomlist_set_ui_data(PurpleRoomlist *list, gpointer ui_data);
    /**************************************************************************/
    +/* Protocol Roomlist Interface API */
    +/**************************************************************************/
    +
    +#define PURPLE_TYPE_PROTOCOL_ROOMLIST \
    + (purple_protocol_roomlist_iface_get_type())
    +
    +typedef struct _PurpleProtocolRoomlistInterface PurpleProtocolRoomlistInterface;
    +
    +/**
    + * PurpleProtocolRoomlistInterface:
    + *
    + * The protocol roomlist interface.
    + *
    + * This interface provides callbacks for room listing.
    + */
    +struct _PurpleProtocolRoomlistInterface
    +{
    + /*< private >*/
    + GTypeInterface parent_iface;
    +
    + /*< public >*/
    + PurpleRoomlist *(*get_list)(PurpleConnection *gc);
    +
    + void (*cancel)(PurpleRoomlist *list);
    +
    + void (*expand_category)(PurpleRoomlist *list,
    + PurpleRoomlistRoom *category);
    +
    + /* room list serialize */
    + char *(*room_serialize)(PurpleRoomlistRoom *room);
    +};
    +
    +#define PURPLE_IS_PROTOCOL_ROOMLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST))
    +#define PURPLE_PROTOCOL_ROOMLIST_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE((obj), PURPLE_TYPE_PROTOCOL_ROOMLIST, \
    + PurpleProtocolRoomlistInterface))
    +
    +/**
    + * purple_protocol_roomlist_iface_get_type:
    + *
    + * Returns: The #GType for the protocol roomlist interface.
    + *
    + * Since: 3.0.0
    + */
    +GType purple_protocol_roomlist_iface_get_type(void);
    +
    +/**
    + * purple_protocol_roomlist_iface_get_list:
    + * @protocol: The #PurpleProtocol instance.
    + * @gc: The #PurpleAccount to get the roomlist for.
    + *
    + * Gets the list of rooms for @gc.
    + *
    + * Returns: (transfer full): The roomlist for @gc.
    + *
    + * Since: 3.0.0
    + */
    +PurpleRoomlist *purple_protocol_roomlist_iface_get_list(PurpleProtocol *protocol,
    + PurpleConnection *gc);
    +
    +/**
    + * purple_protocol_roomlist_iface_cancel:
    + * @protocol: The #PurpleProtocol instance.
    + * @list: The #PurpleRoomlist instance.
    + *
    + * Requesting a roomlist can take a long time. This function cancels a request
    + * that's already in progress.
    + *
    + * Since: 3.0.0
    + */
    +void purple_protocol_roomlist_iface_cancel(PurpleProtocol *protocol,
    + PurpleRoomlist *list);
    +
    +/**
    + * purple_protocol_roomlist_iface_expand_category:
    + * @protocol: The #PurpleProtocol instance.
    + * @list: The #PurpleRoomlist instance.
    + * @category: The category to expand.
    + *
    + * Expands the given @category for @list.
    + *
    + * Since: 3.0.0
    + */
    +void purple_protocol_roomlist_iface_expand_category(PurpleProtocol *protocol,
    + PurpleRoomlist *list, PurpleRoomlistRoom *category);
    +
    +/**
    + * purple_protocol_roomlist_iface_room_serialize:
    + * @protocol: The #PurpleProtocol instance.
    + * @room: The #PurpleRoomlistRoom instance.
    + *
    + * Serializes @room into a string that will be displayed in a user interface.
    + *
    + * Returns: (transfer full): The serialized form of @room.
    + *
    + * Since: 3.0.0
    + */
    +char *purple_protocol_roomlist_iface_room_serialize(PurpleProtocol *protocol,
    + PurpleRoomlistRoom *room);
    +
    +/**************************************************************************/
    /* Room API */
    /**************************************************************************/
    @@ -518,4 +596,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_ROOMLIST_H_ */
    +#endif /* PURPLE_ROOMLIST_H */
    --- a/libpurple/savedstatuses.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/savedstatuses.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SAVEDSTATUSES_H_
    -#define _PURPLE_SAVEDSTATUSES_H_
    +#ifndef PURPLE_SAVEDSTATUSES_H
    +#define PURPLE_SAVEDSTATUSES_H
    /**
    * SECTION:savedstatuses
    * @section_id: libpurple-savedstatuses
    @@ -471,5 +471,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_SAVEDSTATUSES_H_ */
    -
    +#endif /* PURPLE_SAVEDSTATUSES_H */
    --- a/libpurple/server.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/server.c Tue Oct 08 21:48:28 2019 -0500
    @@ -128,7 +128,7 @@
    protocol = purple_connection_get_protocol(gc);
    g_return_val_if_fail(protocol != NULL, val);
    - g_return_val_if_fail(PURPLE_PROTOCOL_HAS_IM_IFACE(protocol), val);
    + g_return_val_if_fail(PURPLE_IS_PROTOCOL_IM(protocol), val);
    account = purple_connection_get_account(gc);
    presence = purple_account_get_presence(account);
    @@ -136,7 +136,7 @@
    im = purple_conversations_find_im_with_account(recipient, account);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, IM_IFACE, send))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, IM, send))
    val = purple_protocol_im_iface_send(protocol, gc, msg);
    /*
    @@ -177,7 +177,7 @@
    if (gc) {
    protocol = purple_connection_get_protocol(gc);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, set_info)) {
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, set_info)) {
    account = purple_connection_get_account(gc);
    purple_signal_emit(purple_accounts_get_handle(),
    @@ -419,7 +419,7 @@
    {
    PurpleProtocol *protocol = NULL;
    PurpleChatConversation *chat;
    - char *buffy = message && *message ? g_strdup(message) : NULL;
    + char *buffy;
    chat = purple_conversations_find_chat(gc, id);
    @@ -429,6 +429,7 @@
    if(gc)
    protocol = purple_connection_get_protocol(gc);
    + buffy = message && *message ? g_strdup(message) : NULL;
    purple_signal_emit(purple_conversations_get_handle(), "chat-inviting-user",
    chat, name, &buffy);
    @@ -461,7 +462,7 @@
    g_return_val_if_fail(msg != NULL, -EINVAL);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, send))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, send))
    return purple_protocol_chat_iface_send(protocol, gc, id, msg);
    return -EINVAL;
    --- a/libpurple/server.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/server.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SERVER_H_
    -#define _PURPLE_SERVER_H_
    +#ifndef PURPLE_SERVER_H
    +#define PURPLE_SERVER_H
    /**
    * SECTION:server
    * @section_id: libpurple-server
    @@ -319,7 +319,7 @@
    *
    * Called by a protocol when an account has joined a chat.
    *
    - * Returns: The resulting conversation
    + * Returns: (transfer none): The resulting conversation.
    */
    PurpleChatConversation *purple_serv_got_joined_chat(PurpleConnection *gc,
    int id, const char *name);
    @@ -370,5 +370,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_SERVER_H_ */
    -
    +#endif /* PURPLE_SERVER_H */
    --- a/libpurple/signals.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/signals.c Tue Oct 08 21:48:28 2019 -0500
    @@ -77,9 +77,7 @@
    static void
    destroy_signal_data(PurpleSignalData *signal_data)
    {
    - g_list_foreach(signal_data->handlers, (GFunc)g_free, NULL);
    - g_list_free(signal_data->handlers);
    -
    + g_list_free_full(signal_data->handlers, g_free);
    g_free(signal_data->value_types);
    g_free(signal_data);
    }
    --- a/libpurple/signals.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/signals.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SIGNALS_H_
    -#define _PURPLE_SIGNALS_H_
    +#ifndef PURPLE_SIGNALS_H
    +#define PURPLE_SIGNALS_H
    /**
    * SECTION:signals
    * @section_id: libpurple-signals
    @@ -837,5 +837,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_SIGNALS_H_ */
    -
    +#endif /* PURPLE_SIGNALS_H */
    --- a/libpurple/smiley-custom.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/smiley-custom.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SMILEY_CUSTOM_H_
    -#define _PURPLE_SMILEY_CUSTOM_H_
    +#ifndef PURPLE_SMILEY_CUSTOM_H
    +#define PURPLE_SMILEY_CUSTOM_H
    /**
    * SECTION:smiley-custom
    * @include:smiley-custom.h
    @@ -46,7 +46,7 @@
    * Adds a new smiley to the store. The @shortcut should be unique, but the
    * @image contents don't have to.
    *
    - * Returns: a new #PurpleSmiley, or %NULL if error occured.
    + * Returns: (transfer none): A new #PurpleSmiley, or %NULL if error occurred.
    */
    PurpleSmiley *
    purple_smiley_custom_add(PurpleImage *image, const gchar *shortcut);
    @@ -66,7 +66,7 @@
    *
    * Returns the whole list of user-configured custom smileys.
    *
    - * Returns: a #PurpleSmileyList of custom smileys.
    + * Returns: (transfer none): A #PurpleSmileyList of custom smileys.
    */
    PurpleSmileyList *
    purple_smiley_custom_get_list(void);
    @@ -88,4 +88,4 @@
    void
    _purple_smiley_custom_uninit(void);
    -#endif /* _PURPLE_SMILEY_CUSTOM_H_ */
    +#endif /* PURPLE_SMILEY_CUSTOM_H */
    --- a/libpurple/smiley-list.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/smiley-list.h Tue Oct 08 21:48:28 2019 -0500
    @@ -106,7 +106,8 @@
    *
    * Retrieves a smiley with the specified @shortcut from the @list.
    *
    - * Returns: a #PurpleSmiley if the smiley was found, %NULL otherwise.
    + * Returns: (transfer none): A #PurpleSmiley if the smiley was found, %NULL
    + * otherwise.
    */
    PurpleSmiley *purple_smiley_list_get_by_shortcut(PurpleSmileyList *list, const gchar *shortcut);
    --- a/libpurple/smiley-parser.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/smiley-parser.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SMILEY_PARSER_H_
    -#define _PURPLE_SMILEY_PARSER_H_
    +#ifndef PURPLE_SMILEY_PARSER_H
    +#define PURPLE_SMILEY_PARSER_H
    /**
    * SECTION:smiley-parser
    * @include:smiley-parser.h
    @@ -60,7 +60,7 @@
    * @conv: the conversation of a message.
    * @html_message: the html message, or escaped plain message.
    * @use_remote_smileys: %TRUE if remote smileys of @conv should be parsed.
    - * @cb: the callback to replace smiley text with an image.
    + * @cb: (scope call): The callback to replace smiley text with an image.
    * @ui_data: the user data to be passed to @cb and
    * #purple_smiley_theme_get_smileys.
    *
    @@ -85,8 +85,8 @@
    * purple_smiley_parser_replace:
    * @smileys: the list of smileys to replace.
    * @html_message: the html message, or escaped plain message.
    - * @cb: the callback to replace smiley text with an image.
    - * @ui_data: the user data to be passed to @cb.
    + * @cb: (scope call): The callback to replace smiley text with an image.
    + * @ui_data: (closure cb): The user data to be passed to the callback.
    *
    * Replaces all textual smiley representations from @smileys list with images.
    *
    @@ -134,4 +134,4 @@
    void
    _purple_smiley_parser_uninit(void);
    -#endif /* _PURPLE_SMILEY_PARSER_H_ */
    +#endif /* PURPLE_SMILEY_PARSER_H */
    --- a/libpurple/smiley-theme.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/smiley-theme.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SMILEY_THEME_H_
    -#define _PURPLE_SMILEY_THEME_H_
    +#ifndef PURPLE_SMILEY_THEME_H
    +#define PURPLE_SMILEY_THEME_H
    /**
    * SECTION:smiley-theme
    * @include:smiley-theme.h
    @@ -30,8 +30,8 @@
    *
    * A smiley theme is a set of standard smileys, that may be displayed in user's
    * message window instead of their textual representations. It may be
    - * categorized depending on the selected protocol, as in #PidginSmileyTheme, but
    - * it's up to the UI to choose behavior.
    + * categorized depending on the selected protocol, but it's up to the UI to
    + * choose behavior.
    */
    #include <glib-object.h>
    @@ -122,4 +122,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_SMILEY_THEME_H_ */
    +#endif /* PURPLE_SMILEY_THEME_H */
    --- a/libpurple/sound.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/sound.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SOUND_H_
    -#define _PURPLE_SOUND_H_
    +#ifndef PURPLE_SOUND_H
    +#define PURPLE_SOUND_H
    /**
    * SECTION:sound
    * @section_id: libpurple-sound
    @@ -193,4 +193,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_SOUND_H_ */
    +#endif /* PURPLE_SOUND_H */
    --- a/libpurple/sslconn.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/sslconn.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#define _PURPLE_SSLCONN_C_
    #include "internal.h"
    --- a/libpurple/sslconn.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/sslconn.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_SSLCONN_H_
    -#define _PURPLE_SSLCONN_H_
    +#ifndef PURPLE_SSLCONN_H
    +#define PURPLE_SSLCONN_H
    /**
    * SECTION:sslconn
    * @section_id: libpurple-sslconn
    @@ -264,4 +264,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_SSLCONN_H_ */
    +#endif /* PURPLE_SSLCONN_H */
    --- a/libpurple/status.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/status.c Tue Oct 08 21:48:28 2019 -0500
    @@ -344,8 +344,8 @@
    g_free(status_type->id);
    g_free(status_type->name);
    - g_list_foreach(status_type->attrs, (GFunc)purple_status_attribute_destroy, NULL);
    - g_list_free(status_type->attrs);
    + g_list_free_full(status_type->attrs,
    + (GDestroyNotify)purple_status_attribute_destroy);
    g_free(status_type);
    }
    @@ -733,13 +733,15 @@
    purple_status_set_active_with_attrs_list(PurpleStatus *status, gboolean active,
    GList *attrs)
    {
    + PurpleStatusPrivate *priv = NULL;
    gboolean changed = FALSE;
    GList *l;
    GList *specified_attr_ids = NULL;
    PurpleStatusType *status_type;
    - PurpleStatusPrivate *priv = purple_status_get_instance_private(status);
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_STATUS(status));
    +
    + priv = purple_status_get_instance_private(status);
    if (!active && purple_status_is_exclusive(status))
    {
    @@ -859,20 +861,22 @@
    PurpleStatusType *
    purple_status_get_status_type(PurpleStatus *status)
    {
    - PurpleStatusPrivate *priv = purple_status_get_instance_private(status);
    + PurpleStatusPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
    + priv = purple_status_get_instance_private(status);
    return priv->status_type;
    }
    PurplePresence *
    purple_status_get_presence(PurpleStatus *status)
    {
    - PurpleStatusPrivate *priv = purple_status_get_instance_private(status);
    + PurpleStatusPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
    + priv = purple_status_get_instance_private(status);
    return priv->presence;
    }
    @@ -919,10 +923,11 @@
    gboolean
    purple_status_is_active(PurpleStatus *status)
    {
    - PurpleStatusPrivate *priv = purple_status_get_instance_private(status);
    + PurpleStatusPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_STATUS(status), FALSE);
    + priv = purple_status_get_instance_private(status);
    return priv->active;
    }
    @@ -942,11 +947,12 @@
    GValue *
    purple_status_get_attr_value(PurpleStatus *status, const char *id)
    {
    - PurpleStatusPrivate *priv = purple_status_get_instance_private(status);
    + PurpleStatusPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_STATUS(status), NULL);
    g_return_val_if_fail(id != NULL, NULL);
    + priv = purple_status_get_instance_private(status);
    return (GValue *)g_hash_table_lookup(priv->attr_values, id);
    }
    --- a/libpurple/status.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/status.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_STATUS_H_
    -#define _PURPLE_STATUS_H_
    +#ifndef PURPLE_STATUS_H
    +#define PURPLE_STATUS_H
    /**
    * SECTION:status
    * @section_id: libpurple-status
    @@ -523,7 +523,7 @@
    *
    * Returns the status's presence.
    *
    - * Returns: The status's presence.
    + * Returns: (transfer none): The status's presence.
    */
    PurplePresence *purple_status_get_presence(PurpleStatus *status);
    @@ -702,5 +702,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_STATUS_H_ */
    -
    +#endif /* PURPLE_STATUS_H */
    --- a/libpurple/stringref.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/stringref.h Tue Oct 08 21:48:28 2019 -0500
    @@ -22,8 +22,8 @@
    *
    */
    -#ifndef _PURPLE_STRINGREF_H_
    -#define _PURPLE_STRINGREF_H_
    +#ifndef PURPLE_STRINGREF_H
    +#define PURPLE_STRINGREF_H
    /**
    * SECTION:stringref
    * @section_id: libpurple-stringref
    @@ -141,4 +141,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_STRINGREF_H_ */
    +#endif /* PURPLE_STRINGREF_H */
    --- a/libpurple/stun.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/stun.c Tue Oct 08 21:48:28 2019 -0500
    @@ -119,7 +119,7 @@
    /* set unknown */
    nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
    - nattype.lookup_time = time(NULL);
    + nattype.lookup_time = g_get_monotonic_time();
    /* callbacks */
    do_callbacks();
    @@ -217,7 +217,7 @@
    purple_debug_info("stun", "got public ip %s\n", nattype.publicip);
    nattype.status = PURPLE_STUN_STATUS_DISCOVERED;
    nattype.type = PURPLE_STUN_NAT_TYPE_UNKNOWN_NAT;
    - nattype.lookup_time = time(NULL);
    + nattype.lookup_time = g_get_monotonic_time();
    /* is it a NAT? */
    @@ -269,7 +269,7 @@
    if(fd < 0) {
    nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
    - nattype.lookup_time = time(NULL);
    + nattype.lookup_time = g_get_monotonic_time();
    do_callbacks();
    g_resolver_free_addresses(ld->addresses);
    g_free(ld);
    @@ -306,7 +306,7 @@
    (struct sockaddr *)&(sc->addr),
    sizeof(struct sockaddr_in)) < (gssize)sizeof(struct stun_header)) {
    nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
    - nattype.lookup_time = time(NULL);
    + nattype.lookup_time = g_get_monotonic_time();
    do_callbacks();
    close_stun_conn(sc);
    return;
    @@ -328,7 +328,7 @@
    res, &error);
    if(error != NULL) {
    nattype.status = PURPLE_STUN_STATUS_UNDISCOVERED;
    - nattype.lookup_time = time(NULL);
    + nattype.lookup_time = g_get_monotonic_time();
    do_callbacks();
    @@ -339,7 +339,7 @@
    ld->port = GPOINTER_TO_INT(data);
    if (!purple_network_listen_range(12108, 12208, AF_UNSPEC, SOCK_DGRAM, TRUE, hbn_listen_cb, ld)) {
    nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
    - nattype.lookup_time = time(NULL);
    + nattype.lookup_time = g_get_monotonic_time();
    do_callbacks();
    @@ -411,8 +411,9 @@
    /* If we don't have a successful status and it has been 5
    minutes since we last did a lookup, redo the lookup */
    - if (nattype.status != PURPLE_STUN_STATUS_DISCOVERED
    - && (time(NULL) - nattype.lookup_time) > 300) {
    + if (nattype.status != PURPLE_STUN_STATUS_DISCOVERED &&
    + (g_get_monotonic_time() - nattype.lookup_time) >
    + 300 * G_USEC_PER_SEC) {
    use_cached_result = FALSE;
    }
    @@ -425,7 +426,7 @@
    if(!servername || (strlen(servername) < 2)) {
    nattype.status = PURPLE_STUN_STATUS_UNKNOWN;
    - nattype.lookup_time = time(NULL);
    + nattype.lookup_time = g_get_monotonic_time();
    if(cb)
    g_timeout_add(10, call_callback, cb);
    return &nattype;
    --- a/libpurple/stun.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/stun.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_STUN_H_
    -#define _PURPLE_STUN_H_
    +#ifndef PURPLE_STUN_H
    +#define PURPLE_STUN_H
    /**
    * SECTION:stun
    * @section_id: libpurple-stun
    @@ -85,7 +85,7 @@
    PurpleStunNatType type;
    char publicip[16];
    char *servername;
    - time_t lookup_time;
    + gint64 lookup_time;
    };
    typedef void (*PurpleStunCallback) (PurpleStunNatDiscovery *discovery);
    @@ -94,9 +94,9 @@
    /**
    * purple_stun_discover:
    - * @cb: The callback to call when the STUN discovery is finished if the
    - * discovery would block. If the discovery is done, this is NOT
    - * called.
    + * @cb: (scope async): The callback to call when the STUN discovery is finished
    + * if the discovery would block. If the discovery is done, this is NOT
    + * called.
    *
    * Starts a NAT discovery. It returns a PurpleStunNatDiscovery if the discovery
    * is already done. Otherwise the callback is called when the discovery is over
    @@ -111,4 +111,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_STUN_H_ */
    +#endif /* PURPLE_STUN_H */
    --- a/libpurple/theme-manager.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/theme-manager.c Tue Oct 08 21:48:28 2019 -0500
    @@ -159,7 +159,9 @@
    &loaders);
    /* Add themes from ~/.purple */
    + G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    path = g_build_filename(purple_user_dir(), "themes", NULL);
    + G_GNUC_END_IGNORE_DEPRECATIONS
    purple_theme_manager_build_dir(loaders, path);
    g_free(path);
    --- a/libpurple/trie.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/trie.c Tue Oct 08 21:48:28 2019 -0500
    @@ -203,12 +203,8 @@
    ******************************************************************************/
    static void
    -purple_trie_states_cleanup(PurpleTrie *trie)
    +purple_trie_states_cleanup(PurpleTriePrivate *priv)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    -
    - g_return_if_fail(priv != NULL);
    -
    if (priv->root_state != NULL) {
    purple_memory_pool_cleanup(priv->states_mempool);
    priv->root_state = NULL;
    @@ -217,13 +213,11 @@
    /* Allocates a state and binds it to the parent. */
    static PurpleTrieState *
    -purple_trie_state_new(PurpleTrie *trie, PurpleTrieState *parent, guchar character)
    +purple_trie_state_new(PurpleTriePrivate *priv, PurpleTrieState *parent,
    + guchar character)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    PurpleTrieState *state;
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    state = purple_memory_pool_alloc0(priv->states_mempool,
    sizeof(PurpleTrieState), sizeof(gpointer));
    g_return_val_if_fail(state != NULL, NULL);
    @@ -252,16 +246,13 @@
    }
    static gboolean
    -purple_trie_states_build(PurpleTrie *trie)
    +purple_trie_states_build(PurpleTriePrivate *priv)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    PurpleTrieState *root;
    PurpleMemoryPool *reclist_mpool;
    PurpleTrieRecordList *reclist, *it;
    gulong cur_len;
    - g_return_val_if_fail(priv != NULL, FALSE);
    -
    if (priv->root_state != NULL)
    return TRUE;
    @@ -273,7 +264,7 @@
    PURPLE_TRIE_STATES_LARGE_POOL_BLOCK_SIZE);
    }
    - priv->root_state = root = purple_trie_state_new(trie, NULL, '\0');
    + priv->root_state = root = purple_trie_state_new(priv, NULL, '\0');
    g_return_val_if_fail(root != NULL, FALSE);
    g_assert(root->longest_suffix == NULL);
    @@ -306,8 +297,8 @@
    prefix = prefix->children[character];
    } else {
    /* We need to create a new branch of trie. */
    - prefix = purple_trie_state_new(trie, prefix,
    - character);
    + prefix = purple_trie_state_new(priv, prefix,
    + character);
    if (!prefix) {
    g_warn_if_reached();
    g_object_unref(reclist_mpool);
    @@ -443,7 +434,7 @@
    purple_trie_replace(PurpleTrie *trie, const gchar *src,
    PurpleTrieReplaceCb replace_cb, gpointer user_data)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    PurpleTrieMachine machine;
    GString *out;
    gsize i;
    @@ -452,9 +443,11 @@
    return NULL;
    g_return_val_if_fail(replace_cb != NULL, g_strdup(src));
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_TRIE(trie), NULL);
    - purple_trie_states_build(trie);
    + priv = purple_trie_get_instance_private(trie);
    +
    + purple_trie_states_build(priv);
    machine.state = priv->root_state;
    machine.root_state = priv->root_state;
    @@ -502,16 +495,17 @@
    machines = g_new(PurpleTrieMachine, tries_count);
    for (i = 0; i < tries_count; i++, tries = tries->next) {
    PurpleTrie *trie = tries->data;
    - PurpleTriePrivate *priv =
    - purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    - if (priv == NULL) {
    + if (!PURPLE_TRIE(trie)) {
    g_warn_if_reached();
    g_free(machines);
    return NULL;
    }
    - purple_trie_states_build(trie);
    + priv = purple_trie_get_instance_private(trie);
    +
    + purple_trie_states_build(priv);
    machines[i].state = priv->root_state;
    machines[i].root_state = priv->root_state;
    @@ -557,7 +551,7 @@
    purple_trie_find(PurpleTrie *trie, const gchar *src,
    PurpleTrieFindCb find_cb, gpointer user_data)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    PurpleTrieMachine machine;
    gulong found_count = 0;
    gsize i;
    @@ -565,9 +559,11 @@
    if (src == NULL)
    return 0;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_TRIE(trie), 0);
    - purple_trie_states_build(trie);
    + priv = purple_trie_get_instance_private(trie);
    +
    + purple_trie_states_build(priv);
    machine.state = priv->root_state;
    machine.root_state = priv->root_state;
    @@ -611,16 +607,17 @@
    machines = g_new(PurpleTrieMachine, tries_count);
    for (i = 0; i < tries_count; i++, tries = tries->next) {
    PurpleTrie *trie = tries->data;
    - PurpleTriePrivate *priv =
    - purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    - if (priv == NULL) {
    + if (!PURPLE_IS_TRIE(trie)) {
    g_warn_if_reached();
    g_free(machines);
    return 0;
    }
    - purple_trie_states_build(trie);
    + priv = purple_trie_get_instance_private(trie);
    +
    + purple_trie_states_build(priv);
    machines[i].state = priv->root_state;
    machines[i].root_state = priv->root_state;
    @@ -668,13 +665,15 @@
    gboolean
    purple_trie_add(PurpleTrie *trie, const gchar *word, gpointer data)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    PurpleTrieRecord *rec;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_TRIE(trie), FALSE);
    g_return_val_if_fail(word != NULL, FALSE);
    g_return_val_if_fail(word[0] != '\0', FALSE);
    + priv = purple_trie_get_instance_private(trie);
    +
    if (g_hash_table_lookup(priv->records_map, word) != NULL) {
    purple_debug_warning("trie", "record exists: %s", word);
    return FALSE;
    @@ -683,7 +682,7 @@
    /* Every change in a trie invalidates longest_suffix map.
    * These prefixes could be updated instead of cleaning the whole graph.
    */
    - purple_trie_states_cleanup(trie);
    + purple_trie_states_cleanup(priv);
    rec = purple_memory_pool_alloc(priv->records_obj_mempool,
    sizeof(PurpleTrieRecord), sizeof(gpointer));
    @@ -703,19 +702,21 @@
    void
    purple_trie_remove(PurpleTrie *trie, const gchar *word)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    PurpleTrieRecordList *it;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_TRIE(trie));
    g_return_if_fail(word != NULL);
    g_return_if_fail(word[0] != '\0');
    + priv = purple_trie_get_instance_private(trie);
    +
    it = g_hash_table_lookup(priv->records_map, word);
    if (it == NULL)
    return;
    /* see purple_trie_add */
    - purple_trie_states_cleanup(trie);
    + purple_trie_states_cleanup(priv);
    priv->records_total_size -= it->rec->word_len;
    priv->records = purple_record_list_remove(priv->records, it);
    @@ -729,10 +730,11 @@
    guint
    purple_trie_get_size(PurpleTrie *trie)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_TRIE(trie), 0);
    + priv = purple_trie_get_instance_private(trie);
    return g_hash_table_size(priv->records_map);
    }
    @@ -744,20 +746,22 @@
    gboolean
    purple_trie_get_reset_on_match(PurpleTrie *trie)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    - g_return_val_if_fail(priv, FALSE);
    + g_return_val_if_fail(PURPLE_IS_TRIE(trie), FALSE);
    + priv = purple_trie_get_instance_private(trie);
    return priv->reset_on_match;
    }
    void
    purple_trie_set_reset_on_match(PurpleTrie *trie, gboolean reset)
    {
    - PurpleTriePrivate *priv = purple_trie_get_instance_private(trie);
    + PurpleTriePrivate *priv = NULL;
    - g_return_if_fail(priv);
    + g_return_if_fail(PURPLE_IS_TRIE(trie));
    + priv = purple_trie_get_instance_private(trie);
    priv->reset_on_match = reset;
    g_object_notify_by_pspec(G_OBJECT(trie), properties[PROP_RESET_ON_MATCH]);
    }
    --- a/libpurple/upnp.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/upnp.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_UPNP_H_
    -#define _PURPLE_UPNP_H_
    +#ifndef PURPLE_UPNP_H
    +#define PURPLE_UPNP_H
    /**
    * SECTION:upnp
    * @section_id: libpurple-upnp
    @@ -51,9 +51,9 @@
    /**
    * purple_upnp_discover:
    - * @cb: an optional callback function to be notified when the UPnP
    - * discovery is complete
    - * @cb_data: Extra data to be passed to the callback
    + * @cb: (scope async): An optional callback function to be notified when the
    + * UPnP discovery is complete.
    + * @cb_data: (closure): Extra data to be passed to the callback.
    *
    * Sends a discovery request to search for a UPnP enabled IGD that
    * contains the WANIPConnection service that will allow us to recieve the
    @@ -87,9 +87,9 @@
    * purple_upnp_set_port_mapping:
    * @portmap: The port to map to this client
    * @protocol: The protocol to map, either "TCP" or "UDP"
    - * @cb: an optional callback function to be notified when the mapping
    - * addition is complete
    - * @cb_data: Extra data to be passed to the callback
    + * @cb: (scope async): An optional callback function to be notified when the
    + * mapping addition is complete.
    + * @cb_data: (closure): Extra data to be passed to the callback.
    *
    * Maps Ports in a UPnP enabled IGD that sits on the local network to
    * this purple client. Essentially, this function takes care of the port
    @@ -105,9 +105,9 @@
    * purple_upnp_remove_port_mapping:
    * @portmap: The port to delete the mapping for
    * @protocol: The protocol to map to. Either "TCP" or "UDP"
    - * @cb: an optional callback function to be notified when the mapping
    - * removal is complete
    - * @cb_data: Extra data to be passed to the callback
    + * @cb: (scope async): An optional callback function to be notified when the
    + * mapping removal is complete.
    + * @cb_data: (closure): Extra data to be passed to the callback.
    *
    * Deletes a port mapping in a UPnP enabled IGD that sits on the local network
    * to this purple client. Essentially, this function takes care of deleting the
    @@ -122,4 +122,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_UPNP_H_ */
    +#endif /* PURPLE_UPNP_H */
    --- a/libpurple/util.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/util.c Tue Oct 08 21:48:28 2019 -0500
    @@ -303,8 +303,9 @@
    str++;
    if (*str == '\0') {
    - if (rest != NULL && *str != '\0')
    + if (rest != NULL) {
    *rest = str;
    + }
    return 0;
    }
    @@ -645,16 +646,6 @@
    /* Quarter */
    case 'Q':
    - if (count <= 2) {
    - /* Numerical */
    - } else if (count == 3) {
    - /* Abbreviation */
    - } else if (count >= 4) {
    - /* Full */
    - count = 4;
    - }
    - break;
    -
    /* Stand-alone Quarter */
    case 'q':
    if (count <= 2) {
    @@ -669,21 +660,6 @@
    /* Month */
    case 'M':
    - 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;
    -
    /* Stand-alone Month */
    case 'L':
    if (count <= 2) {
    @@ -825,14 +801,6 @@
    /* Hour (0-11) */
    case 'K':
    - if (count == 1) {
    - /* No padding */
    - } else if (count >= 2) {
    - /* Zero-padded */
    - count = 2;
    - }
    - break;
    -
    /* Hour (1-24) */
    case 'k':
    if (count == 1) {
    @@ -932,16 +900,6 @@
    }
    -/**************************************************************************/
    -/* GLib Event Loop Functions */
    -/**************************************************************************/
    -
    -void purple_timeout_reset(GSource *source, gint64 seconds_from_now)
    -{
    - g_source_set_ready_time(source, g_get_monotonic_time() + (seconds_from_now * G_USEC_PER_SEC));
    -}
    -
    -
    /**************************************************************************
    * Markup Functions
    **************************************************************************/
    @@ -1371,9 +1329,7 @@
    return FALSE;
    if (q != NULL && (!no_value_token ||
    - (no_value_token && strncmp(p, no_value_token,
    - strlen(no_value_token)))))
    - {
    + strncmp(p, no_value_token, strlen(no_value_token)))) {
    GString *dest = g_string_new("");
    if (is_link)
    @@ -2355,6 +2311,7 @@
    char *d;
    url_buf = g_string_free(gurl_buf, FALSE);
    + gurl_buf = NULL;
    /* strip off trailing periods */
    if (*url_buf) {
    @@ -2379,6 +2336,10 @@
    t = g_utf8_find_next_char(t, NULL);
    }
    }
    +
    + if (gurl_buf) {
    + g_string_free(gurl_buf, TRUE);
    + }
    }
    if(*c == ')' && !inside_html) {
    @@ -3107,7 +3068,7 @@
    if (str[0] == '\0')
    return FALSE;
    - if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, normalize))
    + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, normalize))
    return TRUE;
    normalized = purple_protocol_client_iface_normalize(PURPLE_PROTOCOL(protocol),
    @@ -3336,35 +3297,6 @@
    }
    char *
    -purple_str_size_to_units(goffset size)
    -{
    - static const char * const size_str[] = { "bytes", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB" };
    - float size_mag;
    - gsize size_index = 0;
    -
    - if (size == -1) {
    - return g_strdup(_("Calculating..."));
    - }
    - else if (size == 0) {
    - return g_strdup(_("Unknown."));
    - }
    - else {
    - size_mag = (float)size;
    -
    - while ((size_index < G_N_ELEMENTS(size_str) - 1) && (size_mag > 1024)) {
    - size_mag /= 1024;
    - size_index++;
    - }
    -
    - if (size_index == 0) {
    - return g_strdup_printf("%" G_GOFFSET_FORMAT " %s", size, _(size_str[size_index]));
    - } else {
    - return g_strdup_printf("%.2f %s", size_mag, _(size_str[size_index]));
    - }
    - }
    -}
    -
    -char *
    purple_str_seconds_to_string(guint secs)
    {
    char *ret = NULL;
    --- a/libpurple/util.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/util.h Tue Oct 08 21:48:28 2019 -0500
    @@ -22,8 +22,8 @@
    * namespace.
    */
    -#ifndef _PURPLE_UTIL_H_
    -#define _PURPLE_UTIL_H_
    +#ifndef PURPLE_UTIL_H
    +#define PURPLE_UTIL_H
    /**
    * SECTION:util
    * @section_id: libpurple-util
    @@ -322,43 +322,6 @@
    /**************************************************************************/
    -/* GLib Event Loop Functions */
    -/**************************************************************************/
    -
    -/**
    - * purple_timeout_reset:
    - * @source: A #GTimeoutSource.
    - * @seconds_from_now: Seconds to add to current monotonic time.
    - *
    - * Resets a #GTimeoutSource to be dispatched after @seconds_from_now seconds,
    - * after which it'll continue dispatching at its specified interval.
    - *
    - * The #GSource API exposes a function g_source_set_ready_time(), which is
    - * meant to be used for implementing custom source types. It sets a #GSource
    - * to be dispatched when the given monotonic time is reached, and it's also
    - * the function that's used by the #GTimeoutSource implementation to keep
    - * dispatching at a specified interval.
    - *
    - * The #GTimeoutSource API doesn't expose a function to reset when a
    - * #GTimeoutSource will dispatch the next time, but because it works to
    - * directly call g_source_set_ready_time() on a #GTimeoutSource, and since
    - * it seems unlikely that the implementation will change, we just do that
    - * for now as a workaround for this API shortcoming.
    - *
    - * For the moment, these would be correct ways to achieve a similar effect,
    - * both of which are ugly:
    - *
    - * - Remove the old #GTimeoutSource by calling g_source_remove(), and add a
    - * new #GTimeoutSource by calling g_timeout_add_seconds(). Destroying and
    - * creating #GSource objects is unnecessarily expensive.
    - * - Implement a custom #GResettableTimeoutSource. This means duplicating
    - * #GTimeoutSource and adding one function g_resettable_timeout_reset()
    - * which simply calls g_source_set_ready_time().
    - */
    -void purple_timeout_reset(GSource *source, gint64 seconds_from_now);
    -
    -
    -/**************************************************************************/
    /* Markup Functions */
    /**************************************************************************/
    @@ -413,7 +376,7 @@
    * @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: A callback to format the value before adding it.
    + * @format_cb: (scope call): A callback to format the value before adding it.
    *
    * Extracts a field of data from HTML.
    *
    @@ -1183,17 +1146,6 @@
    const char *purple_strcasestr(const char *haystack, const char *needle);
    /**
    - * purple_str_size_to_units:
    - * @size: The size
    - *
    - * Returns a string representing a filesize in the appropriate
    - * units (MB, KB, GB, etc.)
    - *
    - * Returns: The string in units form. This must be freed.
    - */
    -char *purple_str_size_to_units(goffset size);
    -
    -/**
    * purple_str_seconds_to_string:
    * @sec: The seconds.
    *
    @@ -1332,8 +1284,8 @@
    * This function extracts a list of URIs from the a "text/uri-list"
    * string. It was "borrowed" from gnome_uri_list_extract_uris
    *
    - * Returns: (element-type utf8): A GList containing strings allocated with
    - * g_malloc that have been split from uri-list.
    + * Returns: (element-type utf8) (transfer full): A list of strings that have
    + * been split from uri-list.
    */
    GList *purple_uri_list_extract_uris(const gchar *uri_list);
    @@ -1345,10 +1297,10 @@
    * "text/uri-list" string. It was "borrowed" from
    * gnome_uri_list_extract_filenames
    *
    - * Returns: (element-type utf8): A GList containing strings allocated with
    - * g_malloc that contain the filenames in the uri-list. Note that
    - * unlike the purple_uri_list_extract_uris() function, this will
    - * discard any non-file uri from the result value.
    + * Returns: (element-type utf8) (transfer full): A list of strings that contain
    + * the filenames in the uri-list. Note that unlike the
    + * purple_uri_list_extract_uris() function, this will discard any
    + * non-file uri from the result value.
    */
    GList *purple_uri_list_extract_filenames(const gchar *uri_list);
    @@ -1615,4 +1567,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_UTIL_H_ */
    +#endif /* PURPLE_UTIL_H */
    --- a/libpurple/version.h.in Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/version.h.in Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_VERSION_H_
    -#define _PURPLE_VERSION_H_
    +#ifndef PURPLE_VERSION_H
    +#define PURPLE_VERSION_H
    /**
    * SECTION:version
    * @section_id: libpurple-version
    @@ -99,5 +99,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_VERSION_H_ */
    -
    +#endif /* PURPLE_VERSION_H */
    --- a/libpurple/whiteboard.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/whiteboard.c Tue Oct 08 21:48:28 2019 -0500
    @@ -107,41 +107,41 @@
    void purple_whiteboard_set_protocol_ops(PurpleWhiteboard *wb, PurpleWhiteboardOps *ops)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
    + priv = purple_whiteboard_get_instance_private(wb);
    priv->protocol_ops = ops;
    }
    PurpleAccount *purple_whiteboard_get_account(PurpleWhiteboard *wb)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), NULL);
    + priv = purple_whiteboard_get_instance_private(wb);
    return priv->account;
    }
    const char *purple_whiteboard_get_who(PurpleWhiteboard *wb)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), NULL);
    + priv = purple_whiteboard_get_instance_private(wb);
    return priv->who;
    }
    void purple_whiteboard_set_state(PurpleWhiteboard *wb, int state)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
    + priv = purple_whiteboard_get_instance_private(wb);
    priv->state = state;
    g_object_notify_by_pspec(G_OBJECT(wb), properties[PROP_STATE]);
    @@ -149,11 +149,11 @@
    int purple_whiteboard_get_state(PurpleWhiteboard *wb)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, -1);
    + g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), -1);
    + priv = purple_whiteboard_get_instance_private(wb);
    return priv->state;
    }
    @@ -198,12 +198,12 @@
    gboolean purple_whiteboard_get_dimensions(PurpleWhiteboard *wb, int *width, int *height)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    PurpleWhiteboardOps *protocol_ops;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), FALSE);
    + priv = purple_whiteboard_get_instance_private(wb);
    protocol_ops = priv->protocol_ops;
    if (protocol_ops && protocol_ops->get_dimensions)
    @@ -223,12 +223,12 @@
    void purple_whiteboard_send_draw_list(PurpleWhiteboard *wb, GList *list)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    PurpleWhiteboardOps *protocol_ops;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
    + priv = purple_whiteboard_get_instance_private(wb);
    protocol_ops = priv->protocol_ops;
    if (protocol_ops && protocol_ops->send_draw_list)
    @@ -255,12 +255,12 @@
    void purple_whiteboard_send_clear(PurpleWhiteboard *wb)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    PurpleWhiteboardOps *protocol_ops;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
    + priv = purple_whiteboard_get_instance_private(wb);
    protocol_ops = priv->protocol_ops;
    if (protocol_ops && protocol_ops->clear)
    @@ -269,12 +269,12 @@
    void purple_whiteboard_send_brush(PurpleWhiteboard *wb, int size, int color)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    PurpleWhiteboardOps *protocol_ops;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
    + priv = purple_whiteboard_get_instance_private(wb);
    protocol_ops = priv->protocol_ops;
    if (protocol_ops && protocol_ops->set_brush)
    @@ -283,12 +283,12 @@
    gboolean purple_whiteboard_get_brush(PurpleWhiteboard *wb, int *size, int *color)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    PurpleWhiteboardOps *protocol_ops;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), FALSE);
    + priv = purple_whiteboard_get_instance_private(wb);
    protocol_ops = priv->protocol_ops;
    if (protocol_ops && protocol_ops->get_brush)
    @@ -307,21 +307,21 @@
    GList *purple_whiteboard_get_draw_list(PurpleWhiteboard *wb)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), NULL);
    + priv = purple_whiteboard_get_instance_private(wb);
    return priv->draw_list;
    }
    void purple_whiteboard_set_draw_list(PurpleWhiteboard *wb, GList* draw_list)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
    + priv = purple_whiteboard_get_instance_private(wb);
    priv->draw_list = draw_list;
    g_object_notify_by_pspec(G_OBJECT(wb), properties[PROP_DRAW_LIST]);
    @@ -329,21 +329,21 @@
    void purple_whiteboard_set_protocol_data(PurpleWhiteboard *wb, gpointer proto_data)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_WHITEBOARD(wb));
    + priv = purple_whiteboard_get_instance_private(wb);
    priv->proto_data = proto_data;
    }
    gpointer purple_whiteboard_get_protocol_data(PurpleWhiteboard *wb)
    {
    - PurpleWhiteboardPrivate *priv =
    - purple_whiteboard_get_instance_private(wb);
    + PurpleWhiteboardPrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PURPLE_IS_WHITEBOARD(wb), NULL);
    + priv = purple_whiteboard_get_instance_private(wb);
    return priv->proto_data;
    }
    @@ -519,7 +519,7 @@
    g_return_val_if_fail(PURPLE_IS_PROTOCOL(protocol), NULL);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY_IFACE, whiteboard_new))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, FACTORY, whiteboard_new))
    wb = purple_protocol_factory_iface_whiteboard_new(protocol, account,
    who, state);
    else
    --- a/libpurple/whiteboard.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/whiteboard.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_WHITEBOARD_H_
    -#define _PURPLE_WHITEBOARD_H_
    +#ifndef PURPLE_WHITEBOARD_H
    +#define PURPLE_WHITEBOARD_H
    /**
    * SECTION:whiteboard
    * @section_id: libpurple-whiteboard
    @@ -28,18 +28,11 @@
    * @title: Whiteboard Object
    */
    -#define PURPLE_TYPE_WHITEBOARD (purple_whiteboard_get_type())
    -#define PURPLE_WHITEBOARD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_WHITEBOARD, PurpleWhiteboard))
    -#define PURPLE_WHITEBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_WHITEBOARD, PurpleWhiteboardClass))
    -#define PURPLE_IS_WHITEBOARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_WHITEBOARD))
    -#define PURPLE_IS_WHITEBOARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_WHITEBOARD))
    -#define PURPLE_WHITEBOARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_WHITEBOARD, PurpleWhiteboardClass))
    +#define PURPLE_TYPE_WHITEBOARD (purple_whiteboard_get_type())
    +typedef struct _PurpleWhiteboard PurpleWhiteboard;
    #define PURPLE_TYPE_WHITEBOARD_UI_OPS (purple_whiteboard_ui_ops_get_type())
    -typedef struct _PurpleWhiteboard PurpleWhiteboard;
    -typedef struct _PurpleWhiteboardClass PurpleWhiteboardClass;
    -
    typedef struct _PurpleWhiteboardUiOps PurpleWhiteboardUiOps;
    typedef struct _PurpleWhiteboardOps PurpleWhiteboardOps;
    @@ -123,21 +116,6 @@
    gpointer ui_data;
    };
    -/**
    - * PurpleWhiteboardClass:
    - *
    - * Base class for all #PurpleWhiteboard's
    - */
    -struct _PurpleWhiteboardClass {
    - GObjectClass parent_class;
    -
    - /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    -};
    -
    G_BEGIN_DECLS
    /******************************************************************************/
    @@ -149,7 +127,8 @@
    *
    * Returns: The #GType for the #PurpleWhiteboard object.
    */
    -GType purple_whiteboard_get_type(void);
    +G_DECLARE_FINAL_TYPE(PurpleWhiteboard, purple_whiteboard, PURPLE, WHITEBOARD,
    + GObject)
    /**
    * purple_whiteboard_ui_ops_get_type:
    @@ -419,4 +398,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_WHITEBOARD_H_ */
    +#endif /* PURPLE_WHITEBOARD_H */
    --- a/libpurple/win32/libc_interface.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/win32/libc_interface.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _LIBC_INTERFACE_H_
    -#define _LIBC_INTERFACE_H_
    +#ifndef PURPLE_WIN32_LIBC_INTERFACE_H
    +#define PURPLE_WIN32_LIBC_INTERFACE_H
    #include <config.h>
    @@ -156,4 +156,4 @@
    G_END_DECLS
    -#endif /* _LIBC_INTERFACE_H_ */
    +#endif /* PURPLE_WIN32_LIBC_INTERFACE_H */
    --- a/libpurple/win32/libc_internal.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/win32/libc_internal.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _LIBC_INTERNAL_
    -#define _LIBC_INTERNAL_
    +#ifndef PURPLE_WIN32_LIBC_INTERNAL
    +#define PURPLE_WIN32_LIBC_INTERNAL
    #include <glib.h>
    G_BEGIN_DECLS
    @@ -113,4 +113,4 @@
    G_END_DECLS
    -#endif /* _LIBC_INTERNAL_ */
    +#endif /* PURPLE_WIN32_LIBC_INTERNAL */
    --- a/libpurple/win32/win32dep.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/win32/win32dep.h Tue Oct 08 21:48:28 2019 -0500
    @@ -20,8 +20,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _WIN32DEP_H_
    -#define _WIN32DEP_H_
    +#ifndef PURPLE_WIN32_WIN32DEP_H
    +#define PURPLE_WIN32_WIN32DEP_H
    #include <config.h>
    @@ -78,5 +78,4 @@
    G_END_DECLS
    -#endif /* _WIN32DEP_H_ */
    -
    +#endif /* PURPLE_WIN32_WIN32DEP_H */
    --- a/libpurple/win32/wpurpleerror.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/win32/wpurpleerror.h Tue Oct 08 21:48:28 2019 -0500
    @@ -22,8 +22,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#ifndef _WPURPLEERROR_H
    -#define _WPURPLEERROR_H
    +#ifndef PURPLE_WIN32_WPURPLEERROR_H
    +#define PURPLE_WIN32_WPURPLEERROR_H
    /* Here we define unix socket errors as windows socket errors */
    @@ -80,4 +80,4 @@
    #undef EHOSTUNREACH
    #define EHOSTUNREACH WSAEHOSTUNREACH
    -#endif /* end _WPURPLEERROR_H */
    +#endif /* PURPLE_WIN32_WPURPLEERROR_H */
    --- a/libpurple/xfer.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/xfer.c Tue Oct 08 21:48:28 2019 -0500
    @@ -65,36 +65,17 @@
    int watcher; /* Watcher. */
    goffset bytes_sent; /* The number of bytes sent. */
    - time_t start_time; /* When the transfer of data began. */
    - time_t end_time; /* When the transfer of data ended. */
    + gint64 start_time; /* When the transfer of data began. */
    + gint64 end_time; /* When the transfer of data ended. */
    size_t current_buffer_size; /* This gradually increases for fast
    network connections. */
    PurpleXferStatus status; /* File Transfer's status. */
    - /* I/O operations, which should be set by the protocol using
    - * purple_xfer_set_init_fnc() and friends. Setting #init is
    - * mandatory; all others are optional.
    - */
    - struct
    - {
    - void (*init)(PurpleXfer *xfer);
    - void (*request_denied)(PurpleXfer *xfer);
    - void (*start)(PurpleXfer *xfer);
    - void (*end)(PurpleXfer *xfer);
    - void (*cancel_send)(PurpleXfer *xfer);
    - void (*cancel_recv)(PurpleXfer *xfer);
    - gssize (*read)(guchar **buffer, size_t size, PurpleXfer *xfer);
    - gssize (*write)(const guchar *buffer, size_t size, PurpleXfer *xfer);
    - void (*ack)(PurpleXfer *xfer, const guchar *buffer, size_t size);
    - } ops;
    -
    + gpointer ui_data; /* UI-specific data */
    PurpleXferUiOps *ui_ops; /* UI-specific operations. */
    - /* TODO Remove this and use protocol-specific subclasses. */
    - void *proto_data; /* Protocol-specific data. */
    -
    /*
    * Used to moderate the file transfer when either the read/write ui_ops are
    * set or fd is not set. In those cases, the UI/protocol call the respective
    @@ -134,6 +115,7 @@
    PROP_START_TIME,
    PROP_END_TIME,
    PROP_STATUS,
    + PROP_UI_DATA,
    PROP_LAST
    };
    @@ -170,9 +152,11 @@
    void
    purple_xfer_set_status(PurpleXfer *xfer, PurpleXferStatus status)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    if (purple_debug_is_verbose())
    purple_debug_info("xfer", "Changing status of xfer %p from %s to %s\n",
    @@ -236,9 +220,6 @@
    gsize size;
    PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(message != NULL);
    -
    thumbnail_data = purple_xfer_get_thumbnail(xfer, &size);
    im = purple_conversations_find_im_with_account(priv->who,
    @@ -278,6 +259,9 @@
    purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message,
    gboolean is_error)
    {
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    + g_return_if_fail(message != NULL);
    +
    purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE);
    }
    @@ -471,7 +455,7 @@
    if (purple_xfer_get_filename(xfer) != NULL)
    {
    size = purple_xfer_get_size(xfer);
    - size_buf = purple_str_size_to_units(size);
    + size_buf = g_format_size_full(size, G_FORMAT_SIZE_LONG_FORMAT);
    buf = g_strdup_printf(_("%s wants to send you %s (%s)"),
    buddy ? purple_buddy_get_alias(buddy) : priv->who,
    purple_xfer_get_filename(xfer), size_buf);
    @@ -546,14 +530,16 @@
    void
    purple_xfer_request(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(priv->ops.init != NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + /* this is unref'd in the finishers, like cancel and stop */
    g_object_ref(xfer);
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_RECEIVE)
    + priv = purple_xfer_get_instance_private(xfer);
    +
    + if (priv->type == PURPLE_XFER_TYPE_RECEIVE)
    {
    purple_signal_emit(purple_xfers_get_handle(), "file-recv-request", xfer);
    if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL)
    @@ -561,8 +547,7 @@
    /* The file-transfer was cancelled by a plugin */
    purple_xfer_cancel_local(xfer);
    }
    - else if (purple_xfer_get_filename(xfer) ||
    - purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_ACCEPTED)
    + else if (priv->filename || priv->status == PURPLE_XFER_STATUS_ACCEPTED)
    {
    gchar* message = NULL;
    PurpleBuddy *buddy = purple_blist_find_buddy(priv->account, priv->who);
    @@ -588,34 +573,38 @@
    }
    void
    -purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename)
    +purple_xfer_request_accepted(PurpleXfer *xfer, const gchar *filename)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    - PurpleXferType type;
    + PurpleXferClass *klass = NULL;
    + PurpleXferPrivate *priv = NULL;
    GStatBuf st;
    - char *msg, *utf8, *base;
    - PurpleAccount *account;
    + gchar *msg, *utf8, *base;
    PurpleBuddy *buddy;
    - if (priv == NULL)
    - return;
    -
    - type = purple_xfer_get_xfer_type(xfer);
    - account = purple_xfer_get_account(xfer);
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + klass = PURPLE_XFER_GET_CLASS(xfer);
    + priv = purple_xfer_get_instance_private(xfer);
    purple_debug_misc("xfer", "request accepted for %p\n", xfer);
    - if (!filename && type == PURPLE_XFER_TYPE_RECEIVE) {
    - purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_ACCEPTED);
    - priv->ops.init(xfer);
    + if(filename == NULL) {
    + if(priv->type == PURPLE_XFER_TYPE_RECEIVE) {
    + purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_ACCEPTED);
    + klass->init(xfer);
    + } else {
    + purple_debug_warning(
    + "xfer",
    + "can not set file transfer without a file name"
    + );
    + }
    +
    return;
    - } else {
    - g_return_if_fail(filename != NULL);
    }
    - buddy = purple_blist_find_buddy(account, priv->who);
    -
    - if (type == PURPLE_XFER_TYPE_SEND) {
    + buddy = purple_blist_find_buddy(priv->account, priv->who);
    +
    + if (priv->type == PURPLE_XFER_TYPE_SEND) {
    /* Sending a file */
    /* Check the filename. */
    PurpleXferUiOps *ui_ops;
    @@ -630,7 +619,7 @@
    utf8 = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
    msg = g_strdup_printf(_("%s is not a valid filename.\n"), utf8);
    - purple_xfer_error(type, account, priv->who, msg);
    + purple_xfer_error(priv->type, priv->account, priv->who, msg);
    g_free(utf8);
    g_free(msg);
    @@ -674,70 +663,78 @@
    }
    purple_xfer_add(xfer);
    - priv->ops.init(xfer);
    -
    +
    + klass->init(xfer);
    }
    void
    purple_xfer_request_denied(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferClass *klass = NULL;
    +
    + g_return_if_fail(PURPLE_XFER(xfer));
    +
    + klass = PURPLE_XFER_GET_CLASS(xfer);
    purple_debug_misc("xfer", "xfer %p denied\n", xfer);
    - if (priv->ops.request_denied != NULL)
    - priv->ops.request_denied(xfer);
    + if(klass && klass->request_denied) {
    + klass->request_denied(xfer);
    + }
    g_object_unref(xfer);
    }
    int purple_xfer_get_fd(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, 0);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->fd;
    }
    int purple_xfer_get_watcher(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, 0);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->watcher;
    }
    PurpleXferType
    purple_xfer_get_xfer_type(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, PURPLE_XFER_TYPE_UNKNOWN);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), PURPLE_XFER_TYPE_UNKNOWN);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->type;
    }
    PurpleAccount *
    purple_xfer_get_account(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->account;
    }
    void
    purple_xfer_set_remote_user(PurpleXfer *xfer, const char *who)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    g_free(priv->who);
    priv->who = g_strdup(who);
    @@ -747,20 +744,22 @@
    const char *
    purple_xfer_get_remote_user(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->who;
    }
    PurpleXferStatus
    purple_xfer_get_status(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, PURPLE_XFER_STATUS_UNKNOWN);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), PURPLE_XFER_STATUS_UNKNOWN);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->status;
    }
    @@ -787,50 +786,55 @@
    const char *
    purple_xfer_get_filename(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->filename;
    }
    const char *
    purple_xfer_get_local_filename(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->local_filename;
    }
    goffset
    purple_xfer_get_bytes_sent(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, 0);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->bytes_sent;
    }
    goffset
    purple_xfer_get_bytes_remaining(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, 0);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->size - priv->bytes_sent;
    }
    goffset
    purple_xfer_get_size(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, 0);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->size;
    }
    @@ -849,59 +853,65 @@
    guint16
    purple_xfer_get_local_port(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, -1);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), -1);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->local_port;
    }
    const char *
    purple_xfer_get_remote_ip(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->remote_ip;
    }
    guint16
    purple_xfer_get_remote_port(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, -1);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), -1);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->remote_port;
    }
    -time_t
    +gint64
    purple_xfer_get_start_time(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, 0);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->start_time;
    }
    -time_t
    +gint64
    purple_xfer_get_end_time(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, 0);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->end_time;
    }
    void purple_xfer_set_fd(PurpleXfer *xfer, int fd)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    priv->fd = fd;
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FD]);
    @@ -909,10 +919,11 @@
    void purple_xfer_set_watcher(PurpleXfer *xfer, int watcher)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    priv->watcher = watcher;
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_WATCHER]);
    @@ -921,10 +932,12 @@
    void
    purple_xfer_set_completed(PurpleXfer *xfer, gboolean completed)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferPrivate *priv = NULL;
    PurpleXferUiOps *ui_ops;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    if (completed == TRUE) {
    char *msg = NULL;
    @@ -970,9 +983,11 @@
    void
    purple_xfer_set_message(PurpleXfer *xfer, const char *message)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    if (message != priv->message) {
    g_free(priv->message);
    @@ -985,19 +1000,22 @@
    const char *
    purple_xfer_get_message(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->message;
    }
    void
    purple_xfer_set_filename(PurpleXfer *xfer, const char *filename)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    if (filename != priv->filename) {
    g_free(priv->filename);
    @@ -1010,9 +1028,11 @@
    void
    purple_xfer_set_local_filename(PurpleXfer *xfer, const char *filename)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    if (filename != priv->local_filename) {
    g_free(priv->local_filename);
    @@ -1025,10 +1045,11 @@
    void
    purple_xfer_set_size(PurpleXfer *xfer, goffset size)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    priv->size = size;
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_FILE_SIZE]);
    @@ -1037,10 +1058,11 @@
    void
    purple_xfer_set_local_port(PurpleXfer *xfer, guint16 local_port)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    priv->local_port = local_port;
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_LOCAL_PORT]);
    @@ -1049,10 +1071,11 @@
    void
    purple_xfer_set_bytes_sent(PurpleXfer *xfer, goffset bytes_sent)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    priv->bytes_sent = bytes_sent;
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_BYTES_SENT]);
    @@ -1061,104 +1084,14 @@
    PurpleXferUiOps *
    purple_xfer_get_ui_ops(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->ui_ops;
    }
    -void
    -purple_xfer_set_init_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.init = fnc;
    -}
    -
    -void purple_xfer_set_request_denied_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.request_denied = fnc;
    -}
    -
    -void
    -purple_xfer_set_read_fnc(PurpleXfer *xfer, gssize (*fnc)(guchar **, size_t, PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.read = fnc;
    -}
    -
    -void
    -purple_xfer_set_write_fnc(PurpleXfer *xfer,
    - gssize (*fnc)(const guchar *, size_t, PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.write = fnc;
    -}
    -
    -void
    -purple_xfer_set_ack_fnc(PurpleXfer *xfer,
    - void (*fnc)(PurpleXfer *, const guchar *, size_t))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.ack = fnc;
    -}
    -
    -void
    -purple_xfer_set_start_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.start = fnc;
    -}
    -
    -void
    -purple_xfer_set_end_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.end = fnc;
    -}
    -
    -void
    -purple_xfer_set_cancel_send_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.cancel_send = fnc;
    -}
    -
    -void
    -purple_xfer_set_cancel_recv_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *))
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->ops.cancel_recv = fnc;
    -}
    -
    static void
    purple_xfer_increase_buffer_size(PurpleXfer *xfer)
    {
    @@ -1168,42 +1101,68 @@
    FT_MAX_BUFFER_SIZE);
    }
    +static gssize
    +do_read(PurpleXfer *xfer, guchar **buffer, gsize size)
    +{
    + PurpleXferPrivate *priv = NULL;
    + gssize r;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    + g_return_val_if_fail(buffer != NULL, 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    +
    + *buffer = g_malloc0(size);
    +
    + r = read(priv->fd, *buffer, size);
    + if (r < 0 && errno == EAGAIN) {
    + r = 0;
    + } else if (r < 0) {
    + r = -1;
    + } else if (r == 0) {
    + r = -1;
    + }
    +
    + return r;
    +}
    +
    gssize
    purple_xfer_read(PurpleXfer *xfer, guchar **buffer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    - gssize s, r;
    -
    - g_return_val_if_fail(priv != NULL, 0);
    + PurpleXferPrivate *priv = NULL;
    + PurpleXferClass *klass = NULL;
    + gsize s;
    + gssize r;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    g_return_val_if_fail(buffer != NULL, 0);
    - if (purple_xfer_get_size(xfer) == 0)
    + priv = purple_xfer_get_instance_private(xfer);
    +
    + if (purple_xfer_get_size(xfer) == 0) {
    s = priv->current_buffer_size;
    - else
    - s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)priv->current_buffer_size);
    -
    - if (priv->ops.read != NULL) {
    - r = (priv->ops.read)(buffer, s, xfer);
    + } else {
    + s = MIN(
    + (gssize)purple_xfer_get_bytes_remaining(xfer),
    + (gssize)priv->current_buffer_size
    + );
    }
    - else {
    - *buffer = g_malloc0(s);
    -
    - r = read(priv->fd, *buffer, s);
    - if (r < 0 && errno == EAGAIN)
    - r = 0;
    - else if (r < 0)
    - r = -1;
    - else if (r == 0)
    - r = -1;
    +
    + klass = PURPLE_XFER_GET_CLASS(xfer);
    + if(klass && klass->read) {
    + r = klass->read(xfer, buffer, s);
    + } else {
    + r = do_read(xfer, buffer, s);
    }
    - if (r >= 0 && (gsize)r == priv->current_buffer_size)
    + if (r >= 0 && (gsize)r == priv->current_buffer_size) {
    /*
    - * We managed to read the entire buffer. This means our this
    + * We managed to read the entire buffer. This means our
    * network is fast and our buffer is too small, so make it
    * bigger.
    */
    purple_xfer_increase_buffer_size(xfer);
    + }
    return r;
    }
    @@ -1211,20 +1170,18 @@
    static gssize
    do_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferPrivate *priv = NULL;
    gssize r;
    - g_return_val_if_fail(priv != NULL, 0);
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    g_return_val_if_fail(buffer != NULL, 0);
    - g_return_val_if_fail(size != 0, 0);
    -
    - if (priv->ops.write != NULL) {
    - r = (priv->ops.write)(buffer, size, xfer);
    - } else {
    - r = write(priv->fd, buffer, size);
    - if (r < 0 && errno == EAGAIN)
    - r = 0;
    - }
    + g_return_val_if_fail(size != 0, 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    +
    + r = write(priv->fd, buffer, size);
    + if (r < 0 && errno == EAGAIN)
    + r = 0;
    return r;
    }
    @@ -1232,12 +1189,20 @@
    gssize
    purple_xfer_write(PurpleXfer *xfer, const guchar *buffer, gsize size)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferClass *klass = NULL;
    gssize s;
    - g_return_val_if_fail(priv != NULL, 0);
    -
    - s = MIN((gssize)purple_xfer_get_bytes_remaining(xfer), (gssize)size);
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    +
    + s = MIN(
    + (gssize)purple_xfer_get_bytes_remaining(xfer),
    + (gssize)size
    + );
    +
    + klass = PURPLE_XFER_GET_CLASS(xfer);
    + if(klass && klass->write) {
    + return klass->write(xfer, buffer, s);
    + }
    return do_write(xfer, buffer, s);
    }
    @@ -1245,27 +1210,31 @@
    gboolean
    purple_xfer_write_file(PurpleXfer *xfer, const guchar *buffer, gsize size)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferPrivate *priv = NULL;
    PurpleXferUiOps *ui_ops;
    gsize wc;
    + goffset remaining;
    gboolean fs_known;
    - g_return_val_if_fail(priv != NULL, FALSE);
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), FALSE);
    g_return_val_if_fail(buffer != NULL, FALSE);
    + priv = purple_xfer_get_instance_private(xfer);
    +
    ui_ops = purple_xfer_get_ui_ops(xfer);
    - fs_known = (purple_xfer_get_size(xfer) > 0);
    -
    - if (fs_known && (goffset)size > purple_xfer_get_bytes_remaining(xfer)) {
    + fs_known = (priv->size > 0);
    +
    + remaining = purple_xfer_get_bytes_remaining(xfer);
    + if (fs_known && (goffset)size > remaining) {
    purple_debug_warning("xfer",
    "Got too much data (truncating at %" G_GOFFSET_FORMAT
    ").\n", purple_xfer_get_size(xfer));
    - size = purple_xfer_get_bytes_remaining(xfer);
    + size = remaining;
    }
    - if (ui_ops && ui_ops->ui_write)
    + if (ui_ops && ui_ops->ui_write) {
    wc = ui_ops->ui_write(xfer, buffer, size);
    - else {
    + } else {
    if (priv->dest_fp == NULL) {
    purple_debug_error("xfer",
    "File is not opened for writing\n");
    @@ -1282,8 +1251,10 @@
    return FALSE;
    }
    - purple_xfer_set_bytes_sent(xfer, purple_xfer_get_bytes_sent(xfer) +
    - size);
    + purple_xfer_set_bytes_sent(
    + xfer,
    + purple_xfer_get_bytes_sent(xfer) + size
    + );
    return TRUE;
    }
    @@ -1291,13 +1262,14 @@
    gssize
    purple_xfer_read_file(PurpleXfer *xfer, guchar *buffer, gsize size)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferPrivate *priv = NULL;
    PurpleXferUiOps *ui_ops;
    gssize got_len;
    - g_return_val_if_fail(priv != NULL, FALSE);
    - g_return_val_if_fail(buffer != NULL, FALSE);
    -
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), 0);
    + g_return_val_if_fail(buffer != NULL, 0);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    ui_ops = purple_xfer_get_ui_ops(xfer);
    if (ui_ops && ui_ops->ui_read) {
    @@ -1367,7 +1339,10 @@
    }
    } else if (priv->type == PURPLE_XFER_TYPE_SEND) {
    gssize result = 0;
    - gsize s = MIN((gsize)purple_xfer_get_bytes_remaining(xfer), (gsize)priv->current_buffer_size);
    + gsize s = MIN(
    + (gsize)purple_xfer_get_bytes_remaining(xfer),
    + (gsize)priv->current_buffer_size
    + );
    gboolean read = TRUE;
    /* this is so the protocol can keep the connection open
    @@ -1406,10 +1381,13 @@
    /* Need to indicate the protocol is still ready... */
    priv->ready |= PURPLE_XFER_READY_PROTOCOL;
    + g_free(buffer);
    g_return_if_reached();
    }
    - if (result < 0)
    + if (result < 0) {
    + g_free(buffer);
    return;
    + }
    }
    if (priv->buffer) {
    @@ -1422,6 +1400,7 @@
    r = do_write(xfer, buffer, result);
    if (r == -1) {
    + purple_debug_error("xfer", "do_write failed! %s\n", g_strerror(errno));
    purple_xfer_cancel_remote(xfer);
    if (!priv->buffer)
    /* We don't free buffer if priv->buffer is set, because in
    @@ -1452,16 +1431,18 @@
    }
    if (r > 0) {
    - if (priv->ops.ack != NULL)
    - priv->ops.ack(xfer, buffer, r);
    -
    - g_free(buffer);
    + PurpleXferClass *klass = PURPLE_XFER_GET_CLASS(xfer);
    +
    + if (klass && klass->ack)
    + klass->ack(xfer, buffer, r);
    if (ui_ops != NULL && ui_ops->update_progress != NULL)
    ui_ops->update_progress(xfer,
    purple_xfer_get_progress(xfer));
    }
    + g_free(buffer);
    +
    if (purple_xfer_get_bytes_sent(xfer) >= purple_xfer_get_size(xfer) &&
    !purple_xfer_is_completed(xfer)) {
    purple_xfer_set_completed(xfer, TRUE);
    @@ -1502,8 +1483,8 @@
    static void
    begin_transfer(PurpleXfer *xfer, PurpleInputCondition cond)
    {
    + PurpleXferClass *klass = PURPLE_XFER_GET_CLASS(xfer);
    PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    - PurpleXferType type = purple_xfer_get_xfer_type(xfer);
    PurpleXferUiOps *ui_ops = purple_xfer_get_ui_ops(xfer);
    if (priv->start_time != 0) {
    @@ -1513,7 +1494,7 @@
    if (ui_ops == NULL || (ui_ops->ui_read == NULL && ui_ops->ui_write == NULL)) {
    priv->dest_fp = g_fopen(purple_xfer_get_local_filename(xfer),
    - type == PURPLE_XFER_TYPE_RECEIVE ? "wb" : "rb");
    + priv->type == PURPLE_XFER_TYPE_RECEIVE ? "wb" : "rb");
    if (priv->dest_fp == NULL) {
    purple_xfer_show_file_error(xfer, purple_xfer_get_local_filename(xfer));
    @@ -1529,16 +1510,20 @@
    }
    }
    - if (priv->fd != -1)
    - purple_xfer_set_watcher(xfer,
    - purple_input_add(priv->fd, cond, transfer_cb, xfer));
    -
    - priv->start_time = time(NULL);
    + if (priv->fd != -1) {
    + purple_xfer_set_watcher(
    + xfer,
    + purple_input_add(priv->fd, cond, transfer_cb, xfer)
    + );
    + }
    +
    + priv->start_time = g_get_monotonic_time();
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_START_TIME]);
    - if (priv->ops.start != NULL)
    - priv->ops.start(xfer);
    + if (klass && klass->start) {
    + klass->start(xfer);
    + }
    }
    static void
    @@ -1558,11 +1543,12 @@
    void
    purple_xfer_ui_ready(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    - PurpleInputCondition cond;
    - PurpleXferType type;
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferPrivate *priv = NULL;
    + PurpleInputCondition cond = 0;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    priv->ready |= PURPLE_XFER_READY_UI;
    @@ -1573,15 +1559,18 @@
    purple_debug_misc("xfer", "UI (and protocol) ready on ft %p, so proceeding\n", xfer);
    - type = purple_xfer_get_xfer_type(xfer);
    - if (type == PURPLE_XFER_TYPE_SEND)
    + if (priv->type == PURPLE_XFER_TYPE_SEND) {
    cond = PURPLE_INPUT_WRITE;
    - else /* if (type == PURPLE_XFER_TYPE_RECEIVE) */
    + } else if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
    cond = PURPLE_INPUT_READ;
    -
    - if (priv->watcher == 0 && priv->fd != -1)
    - purple_xfer_set_watcher(xfer,
    - purple_input_add(priv->fd, cond, transfer_cb, xfer));
    + }
    +
    + if (priv->watcher == 0 && priv->fd != -1) {
    + purple_xfer_set_watcher(
    + xfer,
    + purple_input_add(priv->fd, cond, transfer_cb, xfer)
    + );
    + }
    priv->ready = PURPLE_XFER_READY_NONE;
    @@ -1591,9 +1580,11 @@
    void
    purple_xfer_protocol_ready(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    priv->ready |= PURPLE_XFER_READY_PROTOCOL;
    @@ -1613,19 +1604,17 @@
    void
    purple_xfer_start(PurpleXfer *xfer, int fd, const char *ip, guint16 port)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferPrivate *priv = NULL;
    PurpleInputCondition cond;
    - PurpleXferType type;
    GObject *obj;
    - g_return_if_fail(priv != NULL);
    - g_return_if_fail(purple_xfer_get_xfer_type(xfer) != PURPLE_XFER_TYPE_UNKNOWN);
    -
    - type = purple_xfer_get_xfer_type(xfer);
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_STARTED);
    - if (type == PURPLE_XFER_TYPE_RECEIVE) {
    + if (priv->type == PURPLE_XFER_TYPE_RECEIVE) {
    cond = PURPLE_INPUT_READ;
    if (ip != NULL) {
    @@ -1639,16 +1628,20 @@
    g_object_thaw_notify(obj);
    /* Establish a file descriptor. */
    - purple_proxy_connect(NULL, priv->account, priv->remote_ip,
    - priv->remote_port, connect_cb, xfer);
    + purple_proxy_connect(
    + NULL,
    + priv->account,
    + priv->remote_ip,
    + priv->remote_port,
    + connect_cb,
    + xfer
    + );
    return;
    - }
    - else {
    + } else {
    purple_xfer_set_fd(xfer, fd);
    }
    - }
    - else {
    + } else {
    cond = PURPLE_INPUT_WRITE;
    purple_xfer_set_fd(xfer, fd);
    @@ -1660,9 +1653,13 @@
    void
    purple_xfer_end(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    + PurpleXferClass *klass = NULL;
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + klass = PURPLE_XFER_GET_CLASS(xfer);
    + priv = purple_xfer_get_instance_private(xfer);
    /* See if we are actually trying to cancel this. */
    if (!purple_xfer_is_completed(xfer)) {
    @@ -1670,12 +1667,13 @@
    return;
    }
    - priv->end_time = time(NULL);
    + priv->end_time = g_get_monotonic_time();
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
    - if (priv->ops.end != NULL)
    - priv->ops.end(xfer);
    + if (klass && klass->end != NULL) {
    + klass->end(xfer);
    + }
    if (priv->watcher != 0) {
    purple_input_remove(priv->watcher);
    @@ -1716,11 +1714,15 @@
    void
    purple_xfer_cancel_local(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferClass *klass = NULL;
    + PurpleXferPrivate *priv = NULL;
    PurpleXferUiOps *ui_ops;
    char *msg = NULL;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + klass = PURPLE_XFER_GET_CLASS(xfer);
    + priv = purple_xfer_get_instance_private(xfer);
    /* TODO: We definitely want to close any open request dialogs associated
    with this transfer. However, in some cases the request dialog might
    @@ -1737,7 +1739,7 @@
    purple_request_close_with_handle(xfer);
    purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_LOCAL);
    - priv->end_time = time(NULL);
    + priv->end_time = g_get_monotonic_time();
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
    @@ -1753,15 +1755,15 @@
    purple_xfer_conversation_write(xfer, msg, FALSE);
    g_free(msg);
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
    + if (priv->type == PURPLE_XFER_TYPE_SEND)
    {
    - if (priv->ops.cancel_send != NULL)
    - priv->ops.cancel_send(xfer);
    - }
    - else
    - {
    - if (priv->ops.cancel_recv != NULL)
    - priv->ops.cancel_recv(xfer);
    + if (klass && klass->cancel_send) {
    + klass->cancel_send(xfer);
    + }
    + } else {
    + if (klass && klass->cancel_recv) {
    + klass->cancel_recv(xfer);
    + }
    }
    if (priv->watcher != 0) {
    @@ -1769,8 +1771,9 @@
    purple_xfer_set_watcher(xfer, 0);
    }
    - if (priv->fd != -1)
    + if (priv->fd != -1) {
    close(priv->fd);
    + }
    if (priv->dest_fp != NULL) {
    fclose(priv->dest_fp);
    @@ -1779,8 +1782,9 @@
    ui_ops = purple_xfer_get_ui_ops(xfer);
    - if (ui_ops != NULL && ui_ops->cancel_local != NULL)
    + if (ui_ops != NULL && ui_ops->cancel_local != NULL) {
    ui_ops->cancel_local(xfer);
    + }
    g_object_unref(xfer);
    }
    @@ -1788,17 +1792,21 @@
    void
    purple_xfer_cancel_remote(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferClass *klass = NULL;
    + PurpleXferPrivate *priv = NULL;
    PurpleXferUiOps *ui_ops;
    gchar *msg;
    PurpleAccount *account;
    PurpleBuddy *buddy;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + klass = PURPLE_XFER_GET_CLASS(xfer);
    + priv = purple_xfer_get_instance_private(xfer);
    purple_request_close_with_handle(xfer);
    purple_xfer_set_status(xfer, PURPLE_XFER_STATUS_CANCEL_REMOTE);
    - priv->end_time = time(NULL);
    + priv->end_time = g_get_monotonic_time();
    g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_END_TIME]);
    @@ -1819,15 +1827,14 @@
    purple_xfer_error(purple_xfer_get_xfer_type(xfer), account, priv->who, msg);
    g_free(msg);
    - if (purple_xfer_get_xfer_type(xfer) == PURPLE_XFER_TYPE_SEND)
    - {
    - if (priv->ops.cancel_send != NULL)
    - priv->ops.cancel_send(xfer);
    - }
    - else
    - {
    - if (priv->ops.cancel_recv != NULL)
    - priv->ops.cancel_recv(xfer);
    + if (priv->type == PURPLE_XFER_TYPE_SEND) {
    + if (klass && klass->cancel_send) {
    + klass->cancel_send(xfer);
    + }
    + } else if(priv->type == PURPLE_XFER_TYPE_RECEIVE) {
    + if (klass && klass->cancel_recv) {
    + klass->cancel_recv(xfer);
    + }
    }
    if (priv->watcher != 0) {
    @@ -1852,12 +1859,11 @@
    }
    void
    -purple_xfer_error(PurpleXferType type, PurpleAccount *account, const char *who, const char *msg)
    +purple_xfer_error(PurpleXferType type, PurpleAccount *account, const gchar *who, const gchar *msg)
    {
    - char *title;
    + gchar *title = NULL;
    g_return_if_fail(msg != NULL);
    - g_return_if_fail(type != PURPLE_XFER_TYPE_UNKNOWN);
    if (account) {
    PurpleBuddy *buddy;
    @@ -1866,10 +1872,11 @@
    who = purple_buddy_get_alias(buddy);
    }
    - if (type == PURPLE_XFER_TYPE_SEND)
    + if (type == PURPLE_XFER_TYPE_SEND) {
    title = g_strdup_printf(_("File transfer to %s failed."), who);
    - else
    + } else if (type == PURPLE_XFER_TYPE_RECEIVE) {
    title = g_strdup_printf(_("File transfer from %s failed."), who);
    + }
    purple_notify_error(NULL, NULL, title, msg,
    purple_request_cpar_from_account(account));
    @@ -1885,19 +1892,23 @@
    g_return_if_fail(PURPLE_IS_XFER(xfer));
    ui_ops = purple_xfer_get_ui_ops(xfer);
    - if (ui_ops != NULL && ui_ops->update_progress != NULL)
    + if (ui_ops != NULL && ui_ops->update_progress != NULL) {
    ui_ops->update_progress(xfer, purple_xfer_get_progress(xfer));
    + }
    }
    gconstpointer
    purple_xfer_get_thumbnail(PurpleXfer *xfer, gsize *len)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    - if (len)
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    +
    + if (len) {
    *len = priv->thumbnail_size;
    + }
    return priv->thumbnail_data;
    }
    @@ -1905,9 +1916,11 @@
    const gchar *
    purple_xfer_get_thumbnail_mimetype(PurpleXfer *xfer)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    + PurpleXferPrivate *priv = NULL;
    +
    + g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    +
    + priv = purple_xfer_get_instance_private(xfer);
    return priv->thumbnail_mimetype;
    }
    @@ -1916,11 +1929,13 @@
    purple_xfer_set_thumbnail(PurpleXfer *xfer, gconstpointer thumbnail,
    gsize size, const gchar *mimetype)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    + PurpleXferPrivate *priv = NULL;
    gpointer old_thumbnail_data;
    gchar *old_mimetype;
    - g_return_if_fail(priv != NULL);
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + priv = purple_xfer_get_instance_private(xfer);
    /* Hold onto these in case they are equal to passed-in pointers */
    old_thumbnail_data = priv->thumbnail_data;
    @@ -1944,53 +1959,43 @@
    void
    purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats)
    {
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - if (priv->ui_ops->add_thumbnail) {
    - priv->ui_ops->add_thumbnail(xfer, formats);
    + PurpleXferUiOps *ui_ops;
    +
    + g_return_if_fail(PURPLE_IS_XFER(xfer));
    +
    + ui_ops = purple_xfer_get_ui_ops(xfer);
    + if (ui_ops && ui_ops->add_thumbnail) {
    + ui_ops->add_thumbnail(xfer, formats);
    }
    }
    -void
    -purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data)
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_if_fail(priv != NULL);
    -
    - priv->proto_data = proto_data;
    -}
    -
    -gpointer
    -purple_xfer_get_protocol_data(PurpleXfer *xfer)
    -{
    - PurpleXferPrivate *priv = purple_xfer_get_instance_private(xfer);
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    - return priv->proto_data;
    -}
    -
    void purple_xfer_set_ui_data(PurpleXfer *xfer, gpointer ui_data)
    {
    + PurpleXferPrivate *priv = NULL;
    +
    g_return_if_fail(PURPLE_IS_XFER(xfer));
    - xfer->ui_data = ui_data;
    + priv = purple_xfer_get_instance_private(xfer);
    +
    + priv->ui_data = ui_data;
    +
    + g_object_notify_by_pspec(G_OBJECT(xfer), properties[PROP_UI_DATA]);
    }
    gpointer purple_xfer_get_ui_data(PurpleXfer *xfer)
    {
    + PurpleXferPrivate *priv = NULL;
    +
    g_return_val_if_fail(PURPLE_IS_XFER(xfer), NULL);
    - return xfer->ui_data;
    + priv = purple_xfer_get_instance_private(xfer);
    +
    + return priv->ui_data;
    }
    /**************************************************************************
    * GObject code
    **************************************************************************/
    -/* Set method for GObject properties */
    static void
    purple_xfer_set_property(GObject *obj, guint param_id, const GValue *value,
    GParamSpec *pspec)
    @@ -2035,13 +2040,15 @@
    case PROP_STATUS:
    purple_xfer_set_status(xfer, g_value_get_enum(value));
    break;
    + case PROP_UI_DATA:
    + purple_xfer_set_ui_data(xfer, g_value_get_pointer(value));
    + break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    break;
    }
    }
    -/* Get method for GObject properties */
    static void
    purple_xfer_get_property(GObject *obj, guint param_id, GValue *value,
    GParamSpec *pspec)
    @@ -2089,33 +2096,23 @@
    g_value_set_int64(value, purple_xfer_get_bytes_sent(xfer));
    break;
    case PROP_START_TIME:
    -#if SIZEOF_TIME_T == 4
    - g_value_set_int(value, purple_xfer_get_start_time(xfer));
    -#elif SIZEOF_TIME_T == 8
    g_value_set_int64(value, purple_xfer_get_start_time(xfer));
    -#else
    -#error Unknown size of time_t
    -#endif
    break;
    case PROP_END_TIME:
    -#if SIZEOF_TIME_T == 4
    - g_value_set_int(value, purple_xfer_get_end_time(xfer));
    -#elif SIZEOF_TIME_T == 8
    g_value_set_int64(value, purple_xfer_get_end_time(xfer));
    -#else
    -#error Unknown size of time_t
    -#endif
    break;
    case PROP_STATUS:
    g_value_set_enum(value, purple_xfer_get_status(xfer));
    break;
    + case PROP_UI_DATA:
    + g_value_set_pointer(value, purple_xfer_get_ui_data(xfer));
    + break;
    default:
    G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    break;
    }
    }
    -/* GObject initialization function */
    static void
    purple_xfer_init(PurpleXfer *xfer)
    {
    @@ -2127,7 +2124,6 @@
    priv->ready = PURPLE_XFER_READY_NONE;
    }
    -/* Called when done constructing */
    static void
    purple_xfer_constructed(GObject *object)
    {
    @@ -2146,13 +2142,13 @@
    priv->buffer = g_byte_array_sized_new(FT_INITIAL_BUFFER_SIZE);
    }
    - if (ui_ops != NULL && ui_ops->new_xfer != NULL)
    + if (ui_ops != NULL && ui_ops->new_xfer != NULL) {
    ui_ops->new_xfer(xfer);
    + }
    xfers = g_list_prepend(xfers, xfer);
    }
    -/* GObject finalize function */
    static void
    purple_xfer_finalize(GObject *object)
    {
    @@ -2163,13 +2159,15 @@
    /* Close the file browser, if it's open */
    purple_request_close_with_handle(xfer);
    - if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED)
    + if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED) {
    purple_xfer_cancel_local(xfer);
    + }
    ui_ops = purple_xfer_get_ui_ops(xfer);
    - if (ui_ops != NULL && ui_ops->destroy != NULL)
    + if (ui_ops != NULL && ui_ops->destroy != NULL) {
    ui_ops->destroy(xfer);
    + }
    xfers = g_list_remove(xfers, xfer);
    @@ -2178,8 +2176,9 @@
    g_free(priv->remote_ip);
    g_free(priv->local_filename);
    - if (priv->buffer)
    + if (priv->buffer) {
    g_byte_array_free(priv->buffer, TRUE);
    + }
    g_free(priv->thumbnail_data);
    g_free(priv->thumbnail_mimetype);
    @@ -2195,11 +2194,12 @@
    obj_class->finalize = purple_xfer_finalize;
    obj_class->constructed = purple_xfer_constructed;
    -
    - /* Setup properties */
    obj_class->get_property = purple_xfer_get_property;
    obj_class->set_property = purple_xfer_set_property;
    + klass->write = do_write;
    + klass->read = do_read;
    +
    properties[PROP_TYPE] = g_param_spec_enum("type", "Transfer type",
    "The type of file transfer.", PURPLE_TYPE_XFER_TYPE,
    PURPLE_XFER_TYPE_UNKNOWN,
    @@ -2265,49 +2265,26 @@
    G_MININT64, G_MAXINT64, 0,
    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    - properties[PROP_START_TIME] =
    -#if SIZEOF_TIME_T == 4
    - g_param_spec_int
    -#elif SIZEOF_TIME_T == 8
    - g_param_spec_int64
    -#else
    -#error Unknown size of time_t
    -#endif
    - ("start-time", "Start time",
    - "The time the transfer of a file started.",
    -#if SIZEOF_TIME_T == 4
    - G_MININT, G_MAXINT, 0,
    -#elif SIZEOF_TIME_T == 8
    - G_MININT64, G_MAXINT64, 0,
    -#else
    -#error Unknown size of time_t
    -#endif
    - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
    -
    - properties[PROP_END_TIME] =
    -#if SIZEOF_TIME_T == 4
    - g_param_spec_int
    -#elif SIZEOF_TIME_T == 8
    - g_param_spec_int64
    -#else
    -#error Unknown size of time_t
    -#endif
    - ("end-time", "End time",
    - "The time the transfer of a file ended.",
    -#if SIZEOF_TIME_T == 4
    - G_MININT, G_MAXINT, 0,
    -#elif SIZEOF_TIME_T == 8
    - G_MININT64, G_MAXINT64, 0,
    -#else
    -#error Unknown size of time_t
    -#endif
    - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
    + properties[PROP_START_TIME] = g_param_spec_int64(
    + "start-time", "Start time",
    + "The monotonic time the transfer of a file started.",
    + G_MININT64, G_MAXINT64, 0,
    + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
    +
    + properties[PROP_END_TIME] = g_param_spec_int64(
    + "end-time", "End time",
    + "The monotonic time the transfer of a file ended.", G_MININT64,
    + G_MAXINT64, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
    properties[PROP_STATUS] = g_param_spec_enum("status", "Status",
    "The current status for the file transfer.",
    PURPLE_TYPE_XFER_STATUS, PURPLE_XFER_STATUS_UNKNOWN,
    G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    + properties[PROP_UI_DATA] = g_param_spec_pointer("ui-data", "UI Data",
    + "The UI specific data for this xfer",
    + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    +
    g_object_class_install_properties(obj_class, PROP_LAST, properties);
    }
    @@ -2466,7 +2443,10 @@
    if(iface && iface->can_receive)
    return iface->can_receive(prplxfer, connection, who);
    - return FALSE;
    + /* If the PurpleProtocolXfer doesn't implement this function, we assume
    + * there are no conditions where we can't send a file to the given user.
    + */
    + return TRUE;
    }
    void
    @@ -2480,7 +2460,6 @@
    g_return_if_fail(PURPLE_IS_PROTOCOL_XFER(prplxfer));
    g_return_if_fail(PURPLE_IS_CONNECTION(connection));
    g_return_if_fail(who);
    - g_return_if_fail(filename);
    iface = PURPLE_PROTOCOL_XFER_GET_IFACE(prplxfer);
    if(iface && iface->send_file)
    --- a/libpurple/xfer.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/xfer.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_XFER_H_
    -#define _PURPLE_XFER_H_
    +#ifndef PURPLE_XFER_H
    +#define PURPLE_XFER_H
    /**
    * SECTION:xfer
    * @section_id: libpurple-xfer
    @@ -29,13 +29,6 @@
    * @see_also: <link linkend="chapter-signals-xfer">File Transfer signals</link>
    */
    -#define PURPLE_TYPE_XFER (purple_xfer_get_type())
    -#define PURPLE_XFER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_XFER, PurpleXfer))
    -#define PURPLE_XFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_XFER, PurpleXferClass))
    -#define PURPLE_IS_XFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_XFER))
    -#define PURPLE_IS_XFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_XFER))
    -#define PURPLE_XFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_XFER, PurpleXferClass))
    -
    #define PURPLE_TYPE_XFER_UI_OPS (purple_xfer_ui_ops_get_type())
    #define PURPLE_TYPE_PROTOCOL_XFER (purple_protocol_xfer_get_type())
    @@ -46,9 +39,6 @@
    /**************************************************************************/
    /* Data Structures */
    /**************************************************************************/
    -typedef struct _PurpleXfer PurpleXfer;
    -typedef struct _PurpleXferClass PurpleXferClass;
    -
    typedef struct _PurpleXferUiOps PurpleXferUiOps;
    typedef struct _PurpleProtocolXfer PurpleProtocolXfer;
    @@ -73,7 +63,6 @@
    PURPLE_XFER_TYPE_UNKNOWN = 0,
    PURPLE_XFER_TYPE_SEND,
    PURPLE_XFER_TYPE_RECEIVE
    -
    } PurpleXferType;
    /**
    @@ -101,6 +90,12 @@
    PURPLE_XFER_STATUS_CANCEL_REMOTE
    } PurpleXferStatus;
    +G_BEGIN_DECLS
    +
    +#define PURPLE_TYPE_XFER (purple_xfer_get_type())
    +
    +G_DECLARE_DERIVABLE_TYPE(PurpleXfer, purple_xfer, PURPLE, XFER, GObject)
    +
    /**
    * PurpleXferUiOps:
    * @new_xfer: UI op that's called after a new transfer is created.
    @@ -160,23 +155,17 @@
    };
    /**
    - * PurpleXfer:
    - * @ui_data: The UI data associated with this file transfer. This is a
    - * convenience field provided to the UIs -- it is not used by the
    - * libpurple core.
    - *
    - * A core representation of a file transfer.
    - */
    -struct _PurpleXfer
    -{
    - GObject gparent;
    -
    - /*< public >*/
    - gpointer ui_data;
    -};
    -
    -/**
    * PurpleXferClass:
    + * @init: Called when the file transfer is accepted by the user. Must call
    + * purple_xfer_start() and must be implemented.
    + * @request_denied: Called when the file transfer is denied by the other side.
    + * @start: Called to start the file transfer.
    + * @end: Called when the file transfer should end.
    + * @cancel_send: Handler for cancelling a sending file transfer.
    + * @cancel_recv: Handler for cancelling a receiving file transfer.
    + * @read: Called when reading data from the file transfer.
    + * @write: Called when writing data to the file transfer.
    + * @ack: Called when a file transfer is acknowledged.
    *
    * Base class for all #PurpleXfer's
    */
    @@ -184,11 +173,18 @@
    {
    GObjectClass parent_class;
    + void (*init)(PurpleXfer *xfer);
    + void (*request_denied)(PurpleXfer *xfer);
    + void (*start)(PurpleXfer *xfer);
    + void (*end)(PurpleXfer *xfer);
    + void (*cancel_send)(PurpleXfer *xfer);
    + void (*cancel_recv)(PurpleXfer *xfer);
    + gssize (*read)(PurpleXfer *xfer, guchar **buffer, gsize size);
    + gssize (*write)(PurpleXfer *xfer, const guchar *buffer, gsize size);
    + void (*ack)(PurpleXfer *xfer, const guchar *buffer, gsize size);
    +
    /*< private >*/
    - void (*_purple_reserved1)(void);
    - void (*_purple_reserved2)(void);
    - void (*_purple_reserved3)(void);
    - void (*_purple_reserved4)(void);
    + gpointer reserved[4];
    };
    /**
    @@ -218,20 +214,11 @@
    PurpleConnection *connection, const gchar *who);
    };
    -G_BEGIN_DECLS
    -
    /**************************************************************************/
    /* File Transfer API */
    /**************************************************************************/
    /**
    - * purple_xfer_get_type:
    - *
    - * Returns: The #GType for the #PurpleXfer object.
    - */
    -GType purple_xfer_get_type(void);
    -
    -/**
    * purple_xfer_new:
    * @account: The account sending or receiving the file.
    * @type: The type of file transfer.
    @@ -264,7 +251,7 @@
    *
    * Called if the user accepts the file transfer request.
    */
    -void purple_xfer_request_accepted(PurpleXfer *xfer, const char *filename);
    +void purple_xfer_request_accepted(PurpleXfer *xfer, const gchar *filename);
    /**
    * purple_xfer_request_denied:
    @@ -461,9 +448,9 @@
    *
    * Returns the time the transfer of a file started.
    *
    - * Returns: The time when the transfer started.
    + * Returns: The monotonic time when the transfer started.
    */
    -time_t purple_xfer_get_start_time(PurpleXfer *xfer);
    +gint64 purple_xfer_get_start_time(PurpleXfer *xfer);
    /**
    * purple_xfer_get_end_time:
    @@ -471,9 +458,9 @@
    *
    * Returns the time the transfer of a file ended.
    *
    - * Returns: The time when the transfer ended.
    + * Returns: The monotonic time when the transfer ended.
    */
    -time_t purple_xfer_get_end_time(PurpleXfer *xfer);
    +gint64 purple_xfer_get_end_time(PurpleXfer *xfer);
    /**
    * purple_xfer_set_fd:
    @@ -593,94 +580,6 @@
    PurpleXferUiOps *purple_xfer_get_ui_ops(PurpleXfer *xfer);
    /**
    - * purple_xfer_set_read_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The read function.
    - *
    - * Sets the read function for the file transfer.
    - */
    -void purple_xfer_set_read_fnc(PurpleXfer *xfer,
    - gssize (*fnc)(guchar **, size_t, PurpleXfer *));
    -
    -/**
    - * purple_xfer_set_write_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The write function.
    - *
    - * Sets the write function for the file transfer.
    - */
    -void purple_xfer_set_write_fnc(PurpleXfer *xfer,
    - gssize (*fnc)(const guchar *, size_t, PurpleXfer *));
    -
    -/**
    - * purple_xfer_set_ack_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The acknowledge function.
    - *
    - * Sets the acknowledge function for the file transfer.
    - */
    -void purple_xfer_set_ack_fnc(PurpleXfer *xfer,
    - void (*fnc)(PurpleXfer *, const guchar *, size_t));
    -
    -/**
    - * purple_xfer_set_request_denied_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The request denied protocol callback.
    - *
    - * Sets the function to be called if the request is denied.
    - */
    -void purple_xfer_set_request_denied_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
    -
    -/**
    - * purple_xfer_set_init_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The transfer initialization function.
    - *
    - * Sets the transfer initialization function for the file transfer.
    - *
    - * This function is required, and must call purple_xfer_start() with
    - * the necessary parameters. This will be called if the file transfer
    - * is accepted by the user.
    - */
    -void purple_xfer_set_init_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
    -
    -/**
    - * purple_xfer_set_start_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The start transfer function.
    - *
    - * Sets the start transfer function for the file transfer.
    - */
    -void purple_xfer_set_start_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
    -
    -/**
    - * purple_xfer_set_end_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The end transfer function.
    - *
    - * Sets the end transfer function for the file transfer.
    - */
    -void purple_xfer_set_end_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
    -
    -/**
    - * purple_xfer_set_cancel_send_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The cancel send function.
    - *
    - * Sets the cancel send function for the file transfer.
    - */
    -void purple_xfer_set_cancel_send_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
    -
    -/**
    - * purple_xfer_set_cancel_recv_fnc:
    - * @xfer: The file transfer.
    - * @fnc: The cancel receive function.
    - *
    - * Sets the cancel receive function for the file transfer.
    - */
    -void purple_xfer_set_cancel_recv_fnc(PurpleXfer *xfer, void (*fnc)(PurpleXfer *));
    -
    -/**
    * purple_xfer_read:
    * @xfer: The file transfer.
    * @buffer: The buffer that will be created to contain the data.
    @@ -883,25 +782,6 @@
    void purple_xfer_prepare_thumbnail(PurpleXfer *xfer, const gchar *formats);
    /**
    - * purple_xfer_set_protocol_data:
    - * @xfer: The file transfer.
    - * @proto_data: The protocol data to set for the file transfer.
    - *
    - * Sets the protocol data for a file transfer.
    - */
    -void purple_xfer_set_protocol_data(PurpleXfer *xfer, gpointer proto_data);
    -
    -/**
    - * purple_xfer_get_protocol_data:
    - * @xfer: The file transfer.
    - *
    - * Gets the protocol data for a file transfer.
    - *
    - * Returns: The protocol data for the file transfer.
    - */
    -gpointer purple_xfer_get_protocol_data(PurpleXfer *xfer);
    -
    -/**
    * purple_xfer_set_ui_data:
    * @xfer: The file transfer.
    * @ui_data: A pointer to associate with this file transfer.
    @@ -1031,5 +911,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_XFER_H_ */
    -
    +#endif /* PURPLE_XFER_H */
    --- a/libpurple/xmlnode.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/xmlnode.c Tue Oct 08 21:48:28 2019 -0500
    @@ -23,7 +23,6 @@
    * libxode uses memory pools that we simply have no need for, I decided to
    * write my own stuff. Also, re-writing this lets me be as lightweight
    * as I want to be. Thank you libxode for giving me a good starting point */
    -#define _PURPLE_XMLNODE_C_
    #include "internal.h"
    #include "debug.h"
    @@ -491,7 +490,7 @@
    static char *
    purple_xmlnode_to_str_helper(const PurpleXmlNode *node, int *len, gboolean formatting, int depth)
    {
    - GString *text = g_string_new("");
    + GString *text;
    const char *prefix;
    const PurpleXmlNode *c;
    char *node_name, *esc, *esc2, *tab = NULL;
    @@ -499,6 +498,8 @@
    g_return_val_if_fail(node != NULL, NULL);
    + text = g_string_new("");
    +
    if(pretty && depth) {
    tab = g_strnfill(depth, '\t');
    text = g_string_append(text, tab);
    --- a/libpurple/xmlnode.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/libpurple/xmlnode.h Tue Oct 08 21:48:28 2019 -0500
    @@ -19,8 +19,8 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#ifndef _PURPLE_XMLNODE_H_
    -#define _PURPLE_XMLNODE_H_
    +#ifndef PURPLE_XMLNODE_H
    +#define PURPLE_XMLNODE_H
    /**
    * SECTION:xmlnode
    * @section_id: libpurple-xmlnode
    @@ -424,5 +424,4 @@
    G_END_DECLS
    -#endif /* _PURPLE_XMLNODE_H_ */
    -
    +#endif /* PURPLE_XMLNODE_H */
    --- a/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -22,7 +22,7 @@
    #
    project('pidgin', 'c',
    version : '3.0.0-devel',
    - meson_version : '>=0.37.0')
    + meson_version : '>=0.47.0')
    purple_soversion = 20
    parts = meson.project_version().split('-')
    @@ -72,10 +72,8 @@
    sedpath = find_program('sed')
    # Storing build arguments
    -if meson.version().version_compare('>=0.42.0')
    - meson.add_postconf_script('mkmesonconf.py')
    - conf.set('HAVE_MESON_CONFIG', true)
    -endif
    +meson.add_postconf_script('mkmesonconf.py')
    +conf.set('HAVE_MESON_CONFIG', true)
    # Checks for programs.
    compiler = meson.get_compiler('c')
    @@ -290,20 +288,22 @@
    endif
    conf.set_quoted('DISPLAY_VERSION', DISPLAY_VERSION)
    -force_deps = not get_option('missing-dependencies')
    +#######################################################################
    +# Check for GObject Introspection
    +#######################################################################
    +
    +enable_introspection = dependency('gobject-introspection-1.0', version : '>= 1.30.0',
    + required : get_option('introspection')).found()
    +conf.set('ENABLE_INTROSPECTION', enable_introspection)
    # #######################################################################
    # # Check for GTK+ 2.18 and other things used by the GTK UI
    # #######################################################################
    -enable_gestures = get_option('gestures')
    -
    # #######################################################################
    # Check Pidgin dependencies
    # #######################################################################
    if get_option('gtkui')
    - gtk = dependency('gtk+-3.0', version : '>= 3.10.0')
    -
    - webkit = dependency('webkitgtk-3.0', version : '>= 1.3.7')
    + gtk = dependency('gtk+-3.0', version : '>= 3.20.0')
    talkatu_dep = dependency('talkatu', version: '>=0.1.0', required : false)
    if talkatu_dep.found()
    @@ -315,7 +315,7 @@
    talkatu_proj = subproject('talkatu',
    default_options : [
    'doc=' + get_option('doc').to_string(),
    - 'gobject-introspection=' + get_option('introspection').to_string(),
    + 'gobject-introspection=' + enable_introspection.to_string(),
    'nls=' + get_option('nls').to_string(),
    ]
    )
    @@ -323,22 +323,6 @@
    talkatu_gir = talkatu_proj.get_variable('talkatu_gir')[0]
    talkatu_include_directories = []
    endif
    -
    - #######################################################################
    - # Check if we should compile with enchant support
    - #######################################################################
    - # We need enchant for spell checking dictionary enumeration,
    - # because webkit1 doesn't have this.
    - enable_enchant = get_option('enchant')
    - if enable_enchant
    - enchant = dependency('enchant', required : force_deps)
    - enable_enchant = enchant.found()
    - conf.set('USE_ENCHANT', enable_enchant)
    - else
    - enchant = []
    - endif
    -else # GTK
    - enable_enchant = false
    endif # GTK
    ENABLE_GTK = get_option('gtkui')
    @@ -347,25 +331,15 @@
    #######################################################################
    # Check if we should compile with X support
    #######################################################################
    -with_x = get_option('x') and not IS_WIN32
    +if IS_WIN32
    + x11 = disabler()
    +else
    + x11 = dependency('x11', required : get_option('x'))
    +endif
    +conf.set('HAVE_X11', x11.found())
    -if with_x
    - x11 = dependency('x11')
    - if x11.found()
    - conf.set('HAVE_X11', true)
    - else
    - with_x = false
    - if force_deps
    - error('''
    -X11 development headers not found.
    -Use -Dx=false if you do not need X11 support.
    -''')
    - endif
    - endif
    -else
    - x11 = []
    -endif
    -if not get_option('gtkui') or not with_x
    +enable_gestures = get_option('gestures')
    +if not get_option('gtkui') or not x11.found()
    enable_gestures = false
    endif
    @@ -387,59 +361,39 @@
    # Check for GStreamer
    #######################################################################
    -enable_gst = get_option('gstreamer')
    -if enable_gst
    - gstreamer = dependency('gstreamer-1.0', required : force_deps)
    - if gstreamer.found()
    - conf.set('USE_GSTREAMER', true)
    - else
    - enable_gst = false
    - endif
    -else
    - gstreamer = []
    -endif
    +gstreamer = dependency('gstreamer-1.0', required : get_option('gstreamer'))
    +conf.set('USE_GSTREAMER', gstreamer.found())
    #######################################################################
    # Check for GStreamer Video
    #######################################################################
    -enable_gstvideo = enable_gst and get_option('gstreamer-video')
    -if enable_gstvideo
    +if gstreamer.found()
    gstreamer_video = dependency('gstreamer-video-1.0',
    - required : false)
    - if gstreamer_video.found()
    - conf.set('USE_GSTVIDEO', true)
    - else
    - enable_gstvideo = false
    - endif
    + required : get_option('gstreamer-video'))
    + conf.set('USE_GSTVIDEO', gstreamer_video.found())
    else
    - gstreamer_video = []
    + gstreamer_video = disabler()
    endif
    #######################################################################
    # Check for Farstream
    #######################################################################
    -if get_option('farstream')
    - farstream = dependency('farstream-0.2', version : '>= 0.2.7',
    - required : false)
    - enable_farstream = farstream.found()
    -else
    - farstream = []
    - enable_farstream = false
    -endif
    +farstream = dependency('farstream-0.2', version : '>= 0.2.7',
    + required : get_option('farstream'))
    #######################################################################
    # Check for Voice and Video support
    #######################################################################
    -if get_option('vv')
    - if enable_gst and enable_gstvideo and enable_farstream
    +if get_option('vv').enabled() or get_option('vv').auto()
    + if gstreamer.found() and gstreamer_video.found() and farstream.found()
    conf.set('USE_VV', true)
    enable_vv = true
    else
    - if force_deps
    + if get_option('vv').enabled()
    error('''
    Dependencies for voice/video were not met.
    Install the necessary gstreamer and farstream packages first.
    -Or use -Dvv=false if you do not need voice/video support.
    +Or use -Dvv=disabled if you do not need voice/video support.
    ''')
    endif
    enable_vv = false
    @@ -465,73 +419,43 @@
    #######################################################################
    # Check for Meanwhile headers (for Sametime)
    #######################################################################
    -if get_option('meanwhile')
    - meanwhile = dependency('meanwhile', version : ['>= 1.0.0', '< 2.0.0'], required : force_deps)
    - gmime = dependency('gmime-3.0', version : '>= 3.0.0', required : force_deps)
    - enable_meanwhile = meanwhile.found() and gmime.found()
    -else
    - enable_meanwhile = false
    - meanwhile = []
    -endif
    +meanwhile = dependency('meanwhile', version : ['>= 1.0.0', '< 2.0.0'], required : get_option('meanwhile'))
    +gmime = dependency('gmime-3.0', version : '>= 3.0.0', required : get_option('meanwhile'))
    +enable_meanwhile = meanwhile.found() and gmime.found()
    #######################################################################
    # Check for Native Avahi headers (for Bonjour)
    #######################################################################
    -enable_avahi = get_option('avahi')
    -avahi = []
    -if enable_avahi and IS_WIN32
    +if IS_WIN32
    # Just keep enabled.
    -elif enable_avahi
    + enable_avahi = get_option('avahi').enabled() or get_option('avahi').auto()
    + avahi = []
    +else
    # Attempt to autodetect Avahi
    - avahi_client = dependency('avahi-client', required : false)
    - avahi_glib = dependency('avahi-glib', required : false)
    - if avahi_client.found() and avahi_glib.found()
    - avahi = [avahi_client, avahi_glib]
    - else
    - enable_avahi = false
    - if force_deps
    - error('''
    -avahi development package not found.
    -Use -Davahi=false if you do not need avahi (Bonjour) support.
    -''')
    - endif
    - endif
    + avahi_client = dependency('avahi-client', required : get_option('avahi'))
    + avahi_glib = dependency('avahi-glib', required : get_option('avahi'))
    + avahi = [avahi_client, avahi_glib]
    + enable_avahi = avahi_client.found() and avahi_glib.found()
    endif
    #######################################################################
    # Check for SILC client includes and libraries
    #######################################################################
    -have_silc = false
    -if get_option('silc')
    - silc = dependency('silcclient', version : '>= 1.1', required : false)
    - if silc.found()
    - have_silc = true
    - # SILC Toolkit >= 1.0.1 has a new MIME API
    - conf.set('HAVE_SILCMIME_H', true)
    - else
    - if force_deps
    - error('''
    -SILC development package not found.
    -Use -Dsilc=false if you do not need SILC support.
    -''')
    - endif
    - endif
    -endif
    +silc = dependency('silcclient', version : '>= 1.1', required : get_option('silc'))
    #######################################################################
    # Check for Gadu-Gadu protocol library (libgadu)
    #######################################################################
    -enable_libgadu = get_option('libgadu')
    -if enable_libgadu
    - libgadu = dependency('libgadu', version : '>= 1.12.0', required : false)
    - have_libgadu = libgadu.found()
    +libgadu = dependency('libgadu', version : '>= 1.12.0', required : get_option('libgadu'))
    - if have_libgadu
    - if not compiler.has_function('gg_is_gpl_compliant', dependencies : libgadu)
    - have_libgadu = false
    +if libgadu.found()
    + if not compiler.has_function('gg_is_gpl_compliant', dependencies : libgadu)
    + if get_option('libgadu').auto()
    + libgadu = disabler()
    + else
    message('''
    libgadu is not compatible with the GPL when compiled with OpenSSL support.
    @@ -541,20 +465,11 @@
    ''')
    endif
    endif
    -
    - if not have_libgadu and force_deps
    - error('''
    -Libgadu development headers not found.
    -Use -Dlibgadu=false if you do not need GG (GaduGadu) support.
    -''')
    - endif
    -else
    - have_libgadu = false
    endif
    DEFAULT_PRPLS = ['bonjour', 'facebook', 'gg', 'irc', 'jabber', 'novell',
    - 'oscar', 'sametime', 'silc', 'simple', 'zephyr']
    + 'null', 'oscar', 'sametime', 'silc', 'simple', 'zephyr']
    ALL_PRPLS = DEFAULT_PRPLS + ['null']
    dynamic_list = get_option('dynamic-prpls').split(',')
    @@ -569,9 +484,9 @@
    # Do nothing.
    elif prpl == 'bonjour' and not enable_avahi
    # Do nothing.
    - elif prpl == 'silc' and not have_silc
    + elif prpl == 'silc' and not silc.found()
    # Do nothing.
    - elif prpl == 'gg' and not have_libgadu
    + elif prpl == 'gg' and not libgadu.found()
    # Do nothing.
    elif prpl == 'zephyr' and IS_WIN32
    # Do nothing.
    @@ -640,26 +555,6 @@
    add_project_arguments(newflag, language : 'c')
    endif
    endforeach
    -
    - if get_option('fortify')
    -# AC_MSG_CHECKING(for FORTIFY_SOURCE support)
    -# AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <features.h>]], [[
    -# #if !(__GNUC_PREREQ (4, 1) \
    -# || (defined __GNUC_RH_RELEASE__ && __GNUC_PREREQ (4, 0)) \
    -# || (defined __GNUC_RH_RELEASE__ && __GNUC_PREREQ (3, 4) \
    -# && __GNUC_MINOR__ == 4 \
    -# && (__GNUC_PATCHLEVEL__ > 2 \
    -# || (__GNUC_PATCHLEVEL__ == 2 && __GNUC_RH_RELEASE__ >= 8))))
    -# #error No FORTIFY_SOURCE support
    -# #endif
    -# return 0;
    -# ]])], [
    -# AC_MSG_RESULT(yes)
    -# DEBUG_CFLAGS='$DEBUG_CFLAGS -Wp,-D_FORTIFY_SOURCE=2'
    -# ], [
    -# AC_MSG_RESULT(no)
    -# ])
    - endif
    endif
    if get_option('buildtype') != 'plain' and SUNCC
    add_project_arguments('-features=extensions', language : 'c')
    @@ -679,12 +574,12 @@
    # Check for Unity and Messaging Menu
    # Remove when Ubuntu 16.04 is EOL
    #######################################################################
    -enable_unity = get_option('unity-integration')
    +UNITY = [
    + dependency('unity', version : '>= 6.8', required : get_option('unity-integration')),
    + dependency('messaging-menu', version : '>= 12.10', required : get_option('unity-integration'))
    +]
    +enable_unity = UNITY[0].found() and UNITY[1].found()
    if enable_unity
    - UNITY = [
    - dependency('unity', version : '>= 6.8'),
    - dependency('messaging-menu', version : '>= 12.10')
    - ]
    conf.set('USES_MM_CHAT_SECTION', 'X-MessagingMenu-UsesChatSection=true')
    else
    conf.set('USES_MM_CHAT_SECTION', '')
    @@ -694,90 +589,29 @@
    # Check for Secret Service headers
    #######################################################################
    -enable_secret_service = get_option('secret-service') and not IS_WIN32
    -if enable_secret_service
    - secretservice = dependency('libsecret-1', required : force_deps)
    - if secretservice.found()
    - else
    - enable_secret_service = false
    - endif
    +if IS_WIN32
    + secretservice = disabler()
    +else
    + secretservice = dependency('libsecret-1', required : get_option('secret-service'))
    endif
    #######################################################################
    # Check for KWallet headers
    #######################################################################
    -enable_kwallet = get_option('kwallet') and not IS_WIN32
    -enable_kwallet = false
    -
    -if enable_kwallet
    +if IS_WIN32
    + kwallet = disabler()
    +else
    # Ensure C++ compiler works
    add_languages('cpp')
    cxx_compiler = meson.get_compiler('cpp')
    -
    - qt4 = dependency('qt4', modules : 'Core', required : force_deps)
    - enable_kwallet = qt4.found()
    -endif
    + add_project_arguments('-DHAVE_CONFIG_H=1', language : 'cpp')
    -if enable_kwallet
    -# AC_MSG_CHECKING([for metaobject compiler])
    -# MOC=`$PKG_CONFIG --variable=moc_location QtCore`
    -# AC_SUBST(MOC)
    -# AC_MSG_RESULT([$MOC])
    -#
    -# KWALLET_CXXFLAGS=''
    -# KWALLET_LIBS=''
    -# if test -z '$with_kwallet_includes' || test -z '$with_kwallet_libs'; then
    -# AC_CHECK_PROG(KDE4_CONFIG, kde4-config, kde4-config, no)
    -# if test 'x$KDE4_CONFIG' = 'xno'; then
    -# enable_kwallet = false
    -# if test 'x$force_deps' = 'xyes'; then
    -# AC_MSG_ERROR([
    -#kde4-config not found. $KDE4_CONFIG
    -#Use -Dkwallet=false if you do not need KWallet support.
    -#])
    -# fi
    -# fi
    -# fi
    -endif
    + qt5 = import('qt5')
    -if enable_kwallet
    -# if test 'x$KDE4_CONFIG' != 'xno'; then
    -# KWALLET_CXXFLAGS='$QT4_CFLAGS -I`$KDE4_CONFIG --path include`'
    -# fi
    -# CPPFLAGS='$CPPFLAGS $KWALLET_CXXFLAGS'
    -# AC_CHECK_HEADER([kwallet.h], , [
    -# enable_kwallet = false
    -# if test 'x$force_deps' = 'xyes'; then
    -# AC_MSG_ERROR([
    -#KWallet development headers not found.
    -#Use -Dkwallet=false if you do not need KWallet support.
    -#])
    -# fi
    -#])
    -endif
    + qt5_dep = dependency('qt5', modules: ['Core'], required : get_option('kwallet'))
    -if enable_kwallet
    -# AC_MSG_CHECKING([for KWallet libraries])
    -# if test 'x$KDE4_CONFIG' != 'xno'; then
    -# KWALLET_LIBS='-L`$KDE4_CONFIG --install lib`/kde4/devel -lkdeui'
    -# else
    -# KWALLET_LIBS='-lkdeui'
    -# fi
    -# KWALLET_LIBS='$KWALLET_LIBS'
    -# LIBS='$LIBS $KWALLET_LIBS $QT4_LIBS'
    -# AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <kwallet.h>],
    -# [KWallet::Wallet::LocalWallet();])], [AC_MSG_RESULT([yes])],
    -# [
    -# AC_MSG_RESULT(no)
    -# enable_kwallet = false
    -# if test 'x$force_deps' = 'xyes'; then
    -# AC_MSG_ERROR([
    -#KWallet development libraries not found.
    -#Use -Dkwallet=false if you do not need KWallet support.
    -#])
    -# fi
    -# ])
    + kwallet = dependency('KF5Wallet', required : get_option('kwallet'))
    endif
    #######################################################################
    @@ -793,7 +627,7 @@
    gplugin_proj = subproject('gplugin',
    default_options : [
    'doc=' + get_option('doc').to_string(),
    - 'gobject-introspection=' + get_option('introspection').to_string(),
    + 'gobject-introspection=' + enable_introspection.to_string(),
    'nls=' + get_option('nls').to_string(),
    ]
    )
    @@ -802,46 +636,14 @@
    gplugin_include_directories = []
    endif
    -#######################################################################
    -# Check for GObject Introspection
    -#######################################################################
    -
    -enable_introspection = get_option('introspection')
    -if enable_introspection
    - if dependency('gobject-introspection-1.0', version : '>= 1.30.0',
    - required : force_deps).found()
    - conf.set('ENABLE_INTROSPECTION', true)
    - else
    - enable_introspection = false
    - endif
    -endif
    -
    -if get_option('plugins')
    - PLUGINS = true
    -else
    - PLUGINS = false
    -endif
    +PLUGINS = get_option('plugins')
    #######################################################################
    # Check for Nettle (Crypto Library)
    #######################################################################
    -enable_nettle = get_option('nettle')
    -
    -if enable_nettle
    - nettle = dependency('nettle', version : '>= 3.0', required : false)
    - enable_nettle = nettle.found()
    - conf.set('HAVE_NETTLE', enable_nettle)
    -
    - if not enable_nettle and force_deps
    - error('''
    -Nettle development headers not found
    -Use -Dnettle=false if you do not need it.
    -''')
    - endif
    -else
    - nettle = []
    -endif
    +nettle = dependency('nettle', version : '>= 3.0', required : get_option('nettle'))
    +conf.set('HAVE_NETTLE', nettle.found())
    #######################################################################
    # Check for Cyrus-SASL (for xmpp/irc)
    @@ -850,22 +652,15 @@
    conf.set('HAVE_' + func.to_upper(),
    compiler.has_function(func))
    endforeach
    -enable_cyrus_sasl = get_option('cyrus-sasl')
    -if enable_cyrus_sasl
    - sasl = dependency('libsasl2', version : '>= 2.0', required : false)
    - enable_cyrus_sasl = sasl.found()
    - conf.set('HAVE_CYRUS_SASL', enable_cyrus_sasl)
    +sasl = dependency('libsasl2', version : '>= 2.0', required : get_option('cyrus-sasl'))
    +conf.set('HAVE_CYRUS_SASL', sasl.found())
    - if not enable_cyrus_sasl and force_deps
    - error('''
    -Cyrus SASL library not found
    -Use -Dcyrus-sasl=false if you do not need it.
    -''')
    - endif
    -else
    - enable_cyrus_sasl = false
    - sasl = []
    -endif
    +#######################################################################
    +# Check for external libzephyr
    +#######################################################################
    +ext_zephyr = dependency('zephyr', required : get_option('zephyr'))
    +EXTERNAL_LIBZEPHYR = ext_zephyr.found()
    +conf.set('LIBZEPHYR_EXT', EXTERNAL_LIBZEPHYR)
    #######################################################################
    # Check for Kerberos (for Zephyr)
    @@ -904,26 +699,13 @@
    krb4 = []
    endif
    -#######################################################################
    -# Check for external libzephyr
    -#######################################################################
    -zephyr = get_option('zephyr')
    -if zephyr
    - ext_zephyr = dependency('zephyr', required : force_deps)
    - zephyr = ext_zephyr.found()
    -else
    - ext_zephyr = []
    -endif
    -EXTERNAL_LIBZEPHYR = zephyr
    -conf.set('LIBZEPHYR_EXT', EXTERNAL_LIBZEPHYR)
    -
    #AC_MSG_CHECKING(for me pot o' gold)
    #AC_MSG_RESULT(no)
    -foreach func : ['gethostid', 'timegm']
    +foreach func : ['timegm']
    conf.set('HAVE_' + func.to_upper(),
    compiler.has_function(func))
    endforeach
    -foreach header : 'paths.h sgtty.h sys/cdefs.h sys/file.h sys/filio.h sys/ioctl.h sys/msgbuf.h sys/select.h sys/uio.h sys/utsname.h sys/wait.h termios.h'.split()
    +foreach header : 'sgtty.h sys/cdefs.h sys/file.h sys/filio.h sys/ioctl.h sys/msgbuf.h sys/select.h sys/wait.h termios.h'.split()
    conf.set('HAVE_' + header.to_upper().underscorify(),
    compiler.has_header(header))
    endforeach
    @@ -953,14 +735,6 @@
    # check for gtk-doc
    ENABLE_DOC = get_option('doc')
    -if ENABLE_DOC
    - if meson.version().version_compare('<0.41.2')
    - if force_deps
    - error('Meson 0.41.2 or newer is required to build documentation.')
    - endif
    - ENABLE_DOC = false
    - endif
    -endif
    enable_debug = get_option('console-logging')
    conf.set('DEBUG', enable_debug)
    @@ -983,27 +757,28 @@
    message('')
    message('Build GTK+ UI................. : ' + get_option('gtkui').to_string())
    message('Build console UI.............. : ' + enable_consoleui.to_string())
    -message('Build for X11................. : ' + with_x.to_string())
    +message('Build for X11................. : ' + x11.found().to_string())
    message('')
    message('Enable Gestures............... : ' + enable_gestures.to_string())
    message('Protocols to build dynamically : @0@'.format(DYNAMIC_PRPLS))
    message('')
    -message('Build with GStreamer support.. : ' + enable_gst.to_string())
    +message('Build with GStreamer support.. : ' + gstreamer.found().to_string())
    message('Build with voice and video.... : ' + enable_vv.to_string())
    -message('Build with GNU Libidn......... : ' + get_option('idn').to_string())
    -message('Build with Nettle support..... : ' + enable_nettle.to_string())
    -message('Build with Cyrus SASL support. : ' + enable_cyrus_sasl.to_string())
    +message('Build with GNU Libidn......... : ' + idn.found().to_string())
    +message('Build with Nettle support..... : ' + nettle.found().to_string())
    +message('Build with Cyrus SASL support. : ' + sasl.found().to_string())
    +message('Use external libzephyr........ : ' + EXTERNAL_LIBZEPHYR.to_string())
    +if not EXTERNAL_LIBZEPHYR
    message('Use kerberos 4 with zephyr.... : ' + kerberos.to_string())
    -message('Use external libzephyr........ : ' + zephyr.to_string())
    +endif
    message('Install pixmaps............... : ' + INSTALL_PIXMAPS.to_string())
    message('Install translations.......... : ' + INSTALL_I18N.to_string())
    message('Has you....................... : yes')
    message('')
    -message('Build with Enchant support.... : ' + enable_enchant.to_string())
    message('Build Unity integration plugin.: ' + enable_unity.to_string())
    message('')
    -message('Build with KWallet............ : ' + enable_kwallet.to_string())
    -message('Build with Secret Service..... : ' + enable_secret_service.to_string())
    +message('Build with KWallet............ : ' + kwallet.found().to_string())
    +message('Build with Secret Service..... : ' + secretservice.found().to_string())
    message('')
    message('Build plugins................. : ' + PLUGINS.to_string())
    message('Enable Introspection...........: ' + enable_introspection.to_string())
    --- a/meson_options.txt Tue Oct 08 21:47:58 2019 -0500
    +++ b/meson_options.txt Tue Oct 08 21:48:28 2019 -0500
    @@ -1,99 +1,106 @@
    -option('nls', type : 'boolean', value : true,
    - description : 'enable installation of translation files')
    +##############################################################################
    +# General Options
    option('extraversion', type : 'string',
    - description : 'extra version number to be displayed in Help->About and --help (for packagers)')
    -
    -option('missing-dependencies', type : 'boolean', value : false,
    - description : 'skip missing dependencies instead of aborting configuration')
    -
    -option('x', type : 'boolean', value : true)
    -
    -option('gtkui', type : 'boolean', value : true,
    - description : 'compile with GTK+ user interface')
    -option('consoleui', type : 'boolean', value : true,
    - description : 'compile with console user interface')
    + description : 'extra version number to be displayed in Help->About and --help (for packagers)')
    option('doc', type : 'boolean', value : false,
    description : 'build documentation with gtk-doc')
    -option('enchant', type : 'boolean', value : true,
    - description : 'compile with Enchant spell checking support')
    +option('glib-errors-trace', type : 'boolean', value : false,
    + description : 'print backtraces for glib errors')
    +
    +option('introspection', type : 'feature',
    + description : 'build introspection data')
    +
    +option('nettle', type : 'feature',
    + description : 'enable Nettle support')
    +
    +option('nls', type : 'boolean', value : true,
    + description : 'enable installation of translation files')
    +
    +option('pixmaps-install', type : 'boolean', value : true,
    + description : 'enable installation of pixmap files - Pidgin still needs them!')
    +
    +##############################################################################
    +# Libpurple Options
    +
    +option('farstream', type : 'feature',
    + description : 'compile with farstream support')
    +
    +option('gstreamer', type : 'feature',
    + description : 'compile with GStreamer audio support')
    +
    +option('gstreamer-video', type : 'feature',
    + description : 'compile with GStreamer 1.0 Video Overlay support')
    +
    +option('kwallet', type : 'feature',
    + description : 'enable KWallet support')
    +
    +option('plugins', type : 'boolean', value : true,
    + description : 'compile plugins')
    +
    +option('secret-service', type : 'feature',
    + description : 'enable Secret Service support')
    -option('gevolution', type : 'boolean', value : false,
    - description : 'compile with the Evolution plugin')
    +option('vv', type : 'feature',
    + description : 'compile with voice and video support')
    +
    +##############################################################################
    +# Protocol Plugins
    +
    +option('dynamic-prpls', type : 'string', value: 'all',
    + description : 'specify which protocols to build dynamically')
    +
    +option('avahi', type : 'feature',
    + description : 'compile with avahi (required for Bonjour support)')
    +
    +option('cyrus-sasl', type : 'feature',
    + description : 'enable Cyrus SASL support for XMPP/IRC')
    +
    +option('idn', type : 'feature',
    + description : 'compile with IDN support')
    +
    +option('krb4', type : 'boolean', value : false,
    + description : 'compile Zephyr plugin with Kerberos 4 support')
    +
    +option('libgadu', type : 'feature',
    + description : 'compile with libgadu (required for GaduGadu support)')
    +
    +option('meanwhile', type : 'feature',
    + description : 'compile with meanwhile')
    +
    +option('silc', type : 'feature',
    + description : 'compile with SILC plugin')
    +
    +option('zephyr', type : 'feature',
    + description : 'compile Zephyr plugin against external libzephyr')
    +
    +##############################################################################
    +# Pidgin Options
    +
    +option('gtkui', type : 'boolean', value : true,
    + description : 'compile with GTK+ user interface')
    option('cap', type : 'boolean', value : false,
    description : 'compile with Contact Availability Prediction plugin')
    +option('console-logging', type : 'boolean', value : false,
    + description : 'compile with console logging support')
    +
    option('gestures', type : 'boolean', value : true,
    description : 'compile with the gestures plugin')
    -option('gstreamer', type : 'boolean', value : true,
    - description : 'compile with GStreamer audio support')
    -
    -option('gstreamer-video', type : 'boolean', value : true,
    - description : 'compile with GStreamer 1.0 Video Overlay support')
    -
    -option('farstream', type : 'boolean', value : true,
    - description : 'compile with farstream support')
    -
    -option('vv', type : 'boolean', value : true,
    - description : 'compile with voice and video support')
    -
    -option('idn', type : 'boolean', value : true,
    - description : 'compile with IDN support')
    -
    -option('meanwhile', type : 'boolean', value : true,
    - description : 'compile with meanwhile')
    -
    -option('avahi', type : 'boolean', value : true,
    - description : 'compile with avahi (required for Bonjour support)')
    -
    -option('libgadu', type : 'boolean', value : true,
    - description : 'compile with libgadu (required for GaduGadu support)')
    +option('gevolution', type : 'feature',
    + description : 'compile with the Evolution plugin')
    -option('silc', type : 'boolean', value : false,
    - description : 'compile with SILC plugin')
    -
    -option('dynamic-prpls', type : 'string', value: 'all',
    - description : 'specify which protocols to build dynamically')
    -
    -option('plugins', type : 'boolean', value : true,
    - description : 'compile plugins')
    -
    -option('introspection', type : 'boolean', value : true,
    - description : 'build introspection data')
    -
    -option('krb4', type : 'boolean', value : false,
    - description : 'compile Zephyr plugin with Kerberos 4 support')
    -
    -option('zephyr', type : 'boolean', value : false,
    - description : 'compile Zephyr plugin against external libzephyr')
    -
    -option('fortify', type : 'boolean', value : true,
    - description : 'compile with FORTIFY_SOURCE support')
    -
    -option('glib-errors-trace', type : 'boolean', value : false,
    - description : 'print backtraces for glib errors')
    -
    -option('unity-integration', type : 'boolean', value : false,
    +option('unity-integration', type : 'feature',
    description : 'compile with support for unity integration plugin')
    -option('secret-service', type : 'boolean', value : true,
    - description : 'enable Secret Service support')
    -
    -option('kwallet', type : 'boolean', value : true,
    - description : 'enable KWallet support')
    -
    -option('nettle', type : 'boolean', value : true,
    - description : 'enable Nettle support')
    +##############################################################################
    +# Finch Options
    -option('cyrus-sasl', type : 'boolean', value : false,
    - description : 'enable Cyrus SASL support for XMPP/IRC')
    +option('consoleui', type : 'boolean', value : true,
    + description : 'compile with console user interface')
    -option('pixmaps-install', type : 'boolean', value : true,
    - description : 'enable installation of pixmap files - Pidgin still needs them!')
    -
    -option('console-logging', type : 'boolean', value : false,
    - description : 'compile with console logging support')
    +option('x', type : 'boolean', value : true)
    Binary file pidgin/data/icons/hicolor/16x16/apps/im.pidgin.Pidgin3.png has changed
    Binary file pidgin/data/icons/hicolor/16x16/apps/pidgin.png has changed
    Binary file pidgin/data/icons/hicolor/22x22/apps/im.pidgin.Pidgin3.png has changed
    Binary file pidgin/data/icons/hicolor/22x22/apps/pidgin.png has changed
    Binary file pidgin/data/icons/hicolor/24x24/apps/im.pidgin.Pidgin3.png has changed
    Binary file pidgin/data/icons/hicolor/24x24/apps/pidgin.png has changed
    Binary file pidgin/data/icons/hicolor/32x32/apps/im.pidgin.Pidgin3.png has changed
    Binary file pidgin/data/icons/hicolor/32x32/apps/pidgin.png has changed
    Binary file pidgin/data/icons/hicolor/48x48/apps/im.pidgin.Pidgin3.png has changed
    Binary file pidgin/data/icons/hicolor/48x48/apps/pidgin.png has changed
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/data/icons/hicolor/scalable/apps/im.pidgin.Pidgin3.svg Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,610 @@
    +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    +<!-- Created with Inkscape (http://www.inkscape.org/) -->
    +<svg
    + xmlns:dc="http://purl.org/dc/elements/1.1/"
    + xmlns:cc="http://creativecommons.org/ns#"
    + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    + xmlns:svg="http://www.w3.org/2000/svg"
    + xmlns="http://www.w3.org/2000/svg"
    + xmlns:xlink="http://www.w3.org/1999/xlink"
    + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    + width="48px"
    + height="48px"
    + id="svg4345"
    + sodipodi:version="0.32"
    + inkscape:version="0.46"
    + sodipodi:docbase="/home/hbons/Desktop/2.0.2/pidgin/pixmaps/icons/48/scalable"
    + sodipodi:docname="pidgin.svg"
    + inkscape:export-filename="/home/hbons/Desktop/pidgin48.png"
    + inkscape:export-xdpi="90"
    + inkscape:export-ydpi="90"
    + inkscape:output_extension="org.inkscape.output.svg.inkscape">
    + <defs
    + id="defs4347">
    + <linearGradient
    + id="linearGradient8744"
    + inkscape:collect="always">
    + <stop
    + id="stop8746"
    + offset="0"
    + style="stop-color:#7a1d90;stop-opacity:1" />
    + <stop
    + id="stop8748"
    + offset="1"
    + style="stop-color:#6b3678;stop-opacity:0;" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8732">
    + <stop
    + style="stop-color:#6b3678;stop-opacity:1;"
    + offset="0"
    + id="stop8734" />
    + <stop
    + style="stop-color:#6b3678;stop-opacity:0;"
    + offset="1"
    + id="stop8736" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8904">
    + <stop
    + style="stop-color:#729fcf;stop-opacity:1;"
    + offset="0"
    + id="stop8906" />
    + <stop
    + style="stop-color:#25486d;stop-opacity:1"
    + offset="1"
    + id="stop8908" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8884">
    + <stop
    + style="stop-color:#9a5ba8;stop-opacity:1;"
    + offset="0"
    + id="stop8886" />
    + <stop
    + style="stop-color:#6b3e75;stop-opacity:1"
    + offset="1"
    + id="stop8888" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8876">
    + <stop
    + style="stop-color:#9a5ba8;stop-opacity:1;"
    + offset="0"
    + id="stop8878" />
    + <stop
    + style="stop-color:#744380;stop-opacity:1"
    + offset="1"
    + id="stop8880" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8868">
    + <stop
    + style="stop-color:#3b1941;stop-opacity:1;"
    + offset="0"
    + id="stop8870" />
    + <stop
    + style="stop-color:#a646b7;stop-opacity:1"
    + offset="1"
    + id="stop8872" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8860">
    + <stop
    + style="stop-color:#3b1941;stop-opacity:1;"
    + offset="0"
    + id="stop8862" />
    + <stop
    + style="stop-color:#a949b9;stop-opacity:1"
    + offset="1"
    + id="stop8864" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8848">
    + <stop
    + style="stop-color:#000000;stop-opacity:1;"
    + offset="0"
    + id="stop8850" />
    + <stop
    + style="stop-color:#ffffff;stop-opacity:1"
    + offset="1"
    + id="stop8852" />
    + </linearGradient>
    + <linearGradient
    + id="linearGradient8820"
    + inkscape:collect="always">
    + <stop
    + id="stop8822"
    + offset="0"
    + style="stop-color:#522400;stop-opacity:1" />
    + <stop
    + id="stop8824"
    + offset="1"
    + style="stop-color:#6e3100;stop-opacity:0;" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8808">
    + <stop
    + style="stop-color:#6e3100;stop-opacity:1;"
    + offset="0"
    + id="stop8810" />
    + <stop
    + style="stop-color:#6e3100;stop-opacity:0;"
    + offset="1"
    + id="stop8812" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient8686">
    + <stop
    + style="stop-color:#fdb751;stop-opacity:1"
    + offset="0"
    + id="stop8688" />
    + <stop
    + style="stop-color:#ce5c00;stop-opacity:1"
    + offset="1"
    + id="stop8690" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient2834">
    + <stop
    + style="stop-color:#7e408d;stop-opacity:1"
    + offset="0"
    + id="stop2836" />
    + <stop
    + style="stop-color:#82508e;stop-opacity:0;"
    + offset="1"
    + id="stop2838" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient2826">
    + <stop
    + style="stop-color:#3b1941;stop-opacity:1;"
    + offset="0"
    + id="stop2828" />
    + <stop
    + style="stop-color:#3b1941;stop-opacity:0;"
    + offset="1"
    + id="stop2830" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient2816">
    + <stop
    + style="stop-color:#ffffff;stop-opacity:1"
    + offset="0"
    + id="stop2818" />
    + <stop
    + style="stop-color:#eeeeec;stop-opacity:0;"
    + offset="1"
    + id="stop2820" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient6537">
    + <stop
    + style="stop-color:white;stop-opacity:1;"
    + offset="0"
    + id="stop6539" />
    + <stop
    + style="stop-color:#d3e1f1;stop-opacity:1"
    + offset="1"
    + id="stop6541" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + id="linearGradient6506">
    + <stop
    + style="stop-color:#ffffff;stop-opacity:0.95477384"
    + offset="0"
    + id="stop6508" />
    + <stop
    + style="stop-color:#eeeeec;stop-opacity:0;"
    + offset="1"
    + id="stop6510" />
    + </linearGradient>
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient6506"
    + id="linearGradient6512"
    + x1="15.645709"
    + y1="40.668503"
    + x2="15.645709"
    + y2="47.022106"
    + gradientUnits="userSpaceOnUse"
    + gradientTransform="matrix(1,0,0,0.988192,1.5624997,-2.39645)" />
    + <radialGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient2816"
    + id="radialGradient2824"
    + cx="14.930223"
    + cy="25.801632"
    + fx="14.930223"
    + fy="25.801632"
    + r="16.390338"
    + gradientTransform="matrix(1.3364897,0,0,1.3894845,-0.759152,-10.711989)"
    + gradientUnits="userSpaceOnUse" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient2826"
    + id="linearGradient2832"
    + x1="13.191773"
    + y1="41.606163"
    + x2="13.191773"
    + y2="49.067719"
    + gradientUnits="userSpaceOnUse"
    + gradientTransform="matrix(1,0,0,0.988192,1.4473537,-2.486208)" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient2834"
    + id="linearGradient2840"
    + x1="11.373499"
    + y1="43.444576"
    + x2="11.373499"
    + y2="47.757988"
    + gradientUnits="userSpaceOnUse"
    + gradientTransform="matrix(1,0,0,0.988192,1.4473537,-2.486208)" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8686"
    + id="linearGradient8692"
    + x1="18.5"
    + y1="29.911009"
    + x2="19.985121"
    + y2="29.853554"
    + gradientUnits="userSpaceOnUse"
    + gradientTransform="matrix(1.0769231,0,0,1.1428571,-0.905101,-4.6800586)" />
    + <filter
    + inkscape:collect="always"
    + id="filter8730">
    + <feGaussianBlur
    + inkscape:collect="always"
    + stdDeviation="0.27197245"
    + id="feGaussianBlur8732" />
    + </filter>
    + <filter
    + inkscape:collect="always"
    + id="filter8792"
    + x="-0.095301818"
    + width="1.1906036"
    + y="-0.27704017"
    + height="1.5540803">
    + <feGaussianBlur
    + inkscape:collect="always"
    + stdDeviation="0.31022727"
    + id="feGaussianBlur8794" />
    + </filter>
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8820"
    + id="linearGradient8814"
    + x1="18.339697"
    + y1="29.338558"
    + x2="18.031723"
    + y2="30.431053"
    + gradientUnits="userSpaceOnUse" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8808"
    + id="linearGradient8818"
    + gradientUnits="userSpaceOnUse"
    + x1="17.969458"
    + y1="29.494703"
    + x2="18.143806"
    + y2="30.188351" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8848"
    + id="linearGradient8854"
    + x1="10.48653"
    + y1="25.21174"
    + x2="9.7512932"
    + y2="23.675837"
    + gradientUnits="userSpaceOnUse" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8848"
    + id="linearGradient8858"
    + gradientUnits="userSpaceOnUse"
    + x1="10.498732"
    + y1="24.936121"
    + x2="9.6415968"
    + y2="23.675837" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8860"
    + id="linearGradient8866"
    + x1="13.061977"
    + y1="10.027351"
    + x2="16.545418"
    + y2="12.891665"
    + gradientUnits="userSpaceOnUse"
    + gradientTransform="matrix(1.50247,0,0,1,-6.5946403,-2.139701)" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8868"
    + id="linearGradient8874"
    + x1="12.409452"
    + y1="10.602999"
    + x2="16.140554"
    + y2="13.895189"
    + gradientUnits="userSpaceOnUse"
    + gradientTransform="matrix(1.236264,0,0,0.549587,-5.4828863,3.775206)" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8876"
    + id="linearGradient8882"
    + x1="10.46875"
    + y1="25.3125"
    + x2="9.53125"
    + y2="19.6875"
    + gradientUnits="userSpaceOnUse" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8884"
    + id="linearGradient8890"
    + x1="23.881994"
    + y1="24.343237"
    + x2="24.973602"
    + y2="19.216713"
    + gradientUnits="userSpaceOnUse" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8904"
    + id="linearGradient8910"
    + x1="26.125"
    + y1="1.8037834"
    + x2="41.875"
    + y2="33.678783"
    + gradientUnits="userSpaceOnUse" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8744"
    + id="linearGradient8738"
    + x1="4.0852318"
    + y1="39.097038"
    + x2="4.0852318"
    + y2="44.321774"
    + gradientUnits="userSpaceOnUse" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient8732"
    + id="linearGradient8742"
    + gradientUnits="userSpaceOnUse"
    + x1="4.0852318"
    + y1="40.416641"
    + x2="4.0852318"
    + y2="43.352409"
    + gradientTransform="matrix(-1,0,0,1,37.022732,0)" />
    + <linearGradient
    + inkscape:collect="always"
    + xlink:href="#linearGradient6537"
    + id="linearGradient7977"
    + gradientUnits="userSpaceOnUse"
    + x1="30.5"
    + y1="4.8871226"
    + x2="30.5"
    + y2="22.781603" />
    + </defs>
    + <sodipodi:namedview
    + id="base"
    + pagecolor="#ffffff"
    + bordercolor="#666666"
    + borderopacity="1.0"
    + inkscape:pageopacity="0.0"
    + inkscape:pageshadow="2"
    + inkscape:zoom="2.828427"
    + inkscape:cx="63.722923"
    + inkscape:cy="33.120105"
    + inkscape:current-layer="layer1"
    + showgrid="true"
    + inkscape:grid-bbox="true"
    + inkscape:document-units="px"
    + inkscape:window-width="1434"
    + inkscape:window-height="840"
    + inkscape:window-x="-2"
    + inkscape:window-y="0"
    + showguides="true"
    + inkscape:guide-bbox="true"
    + inkscape:grid-points="true"
    + inkscape:snap-bbox="true"
    + inkscape:snap-nodes="false"
    + objecttolerance="10"
    + gridtolerance="10">
    + <inkscape:grid
    + type="xygrid"
    + id="grid7914"
    + visible="true"
    + enabled="true" />
    + </sodipodi:namedview>
    + <metadata
    + id="metadata4350">
    + <rdf:RDF>
    + <cc:Work
    + rdf:about="">
    + <dc:format>image/svg+xml</dc:format>
    + <dc:type
    + rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    + </cc:Work>
    + </rdf:RDF>
    + </metadata>
    + <g
    + id="layer1"
    + inkscape:label="Layer 1"
    + inkscape:groupmode="layer">
    + <path
    + style="fill:#efefef;fill-opacity:1;stroke:url(#linearGradient8910);stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 20.53125,1.5 C 17.192693,1.5 14.5,4.611235 14.5,8.46875 L 14.5,20.53125 C 14.5,24.388765 17.192693,27.5 20.53125,27.5 L 34.530203,27.5 C 34.530203,27.5 34.406442,30.680041 32.92887,32.534344 C 38.120414,32.534344 39.353553,27.485509 39.353553,27.485509 L 41.5,27.5 C 45.512737,27.5 46.5,24.38319 46.5,20.53125 L 46.5,8.46875 C 46.5,4.6112353 43.807307,1.5 40.46875,1.5 L 20.53125,1.5 z"
    + id="rect5498"
    + sodipodi:nodetypes="cccccccccccc" />
    + <path
    + style="fill:url(#linearGradient7977);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 20.53125,2.5 C 17.815701,2.5 15.5,5.0778932 15.5,8.46875 L 15.5,20.53125 C 15.5,23.922107 17.815701,26.5 20.53125,26.5 L 34.53125,26.5 C 35.082875,26.501589 35.529661,26.948375 35.53125,27.5 C 35.53125,27.5 35.367799,30.212738 34.75,31.34218 C 36.852551,31.34218 38.53125,26.6875 38.53125,26.6875 C 38.704261,26.563588 38.912203,26.497922 39.125,26.5 L 41.5,26.5 C 43.246736,26.5 44.065452,25.932701 44.65625,24.9375 C 45.247048,23.942299 45.5,22.371547 45.5,20.53125 L 45.5,8.46875 C 45.5,5.0778935 43.184299,2.5 40.46875,2.5 L 20.53125,2.5 z"
    + id="path6535"
    + sodipodi:nodetypes="ccccccccccscccc" />
    + <rect
    + style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    + id="rect8734"
    + width="7"
    + height="1.8602936"
    + x="15.642976"
    + y="26.409245"
    + rx="0.81387848"
    + ry="0.93014681" />
    + <path
    + style="fill:url(#linearGradient8866);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 10.968089,7.6821669 C 13.825874,5.8764931 18.683474,9.095739 19.201853,10.751964 L 15.25649,10.751964 C 15.19399,8.408214 10.968089,7.6821669 10.968089,7.6821669 z"
    + id="rect5189"
    + sodipodi:nodetypes="cccc" />
    + <path
    + style="fill:url(#linearGradient8874);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 8.9209727,9.9271812 C 10.543462,8.0699992 15.5336,9.5097671 15.5336,10.860297 L 11.743475,11.411821 C 12.958815,9.7987339 8.9209727,9.9271812 8.9209727,9.9271812 z"
    + id="path5192"
    + sodipodi:nodetypes="cccc" />
    + <path
    + style="opacity:1;fill:url(#linearGradient2840);fill-opacity:1;stroke:url(#linearGradient2832);stroke-width:0.99999994000000003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    + d="M 16.447354,10.481822 C 9.8233516,10.481822 3.9308558,15.93012 4.5098537,22.712688 C 5.7768579,37.554787 1.9403503,35.543378 1.5,42.316391 C 1.5,44.375904 2.4854201,45.355587 4.5920502,45.355587 C 5.7795388,45.355587 30.807389,45.5 33.498388,45.5 C 35.612515,45.5 35.447354,43.46472 35.447354,43.46472 C 35.447354,40.830782 29.045256,38.620654 27.260807,36.687231 C 25.432389,34.709838 27.447354,27.653648 27.447354,27.653648 C 28.08866,26.141912 28.447354,24.466812 28.447354,22.712688 C 28.447354,15.894164 23.071356,10.481822 16.447354,10.481822 z"
    + id="path5176"
    + sodipodi:nodetypes="cscccszcsc" />
    + <path
    + style="opacity:0.55000000000000004;fill:url(#radialGradient2824);fill-opacity:1;stroke:url(#linearGradient6512);stroke-width:0.99999994000000003;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 16.25,11.5 C 10.237704,11.5 5.0276636,16.611038 5.4999997,22.834065 C 6.0644244,30.27036 5.8175038,32.821583 4.0236672,37.541192 C 1.1437418,42.286379 3.1235207,44.533069 5.4658637,44.533069 C 7.4024447,44.533069 25.520621,44.503627 31.346594,44.503627 C 32.513801,44.503627 34.5,45.001748 34.5,43.554478 C 34.5,43.335354 34.300249,42.923343 33.75,42.411881 C 33.199751,41.90042 32.37676,41.351214 31.46875,40.806069 C 30.56074,40.260924 29.59071,39.709456 28.71875,39.169376 C 27.84679,38.629297 27.074154,38.125147 26.53125,37.409159 C 25.921245,36.604678 25.641306,35.658991 25.586842,34.629869 C 25.532378,33.600747 25.670221,32.49665 25.787061,31.449126 C 26.020741,29.354077 26.53125,27.527239 26.53125,27.527239 C 26.54624,27.4637 26.567163,27.401673 26.59375,27.341953 C 27.181231,25.957097 27.5,24.414481 27.5,22.802446 C 27.5,16.531779 22.272436,11.5 16.25,11.5 z"
    + id="path5241"
    + sodipodi:nodetypes="cscccssssssscssc" />
    + <path
    + sodipodi:type="inkscape:offset"
    + inkscape:radius="1.0174263"
    + inkscape:original="M 9.96875 19 C 8.022221 19 6.9374998 21.081214 6.9375 23 C 6.9375 25.208 8.5222219 27 10.46875 27 C 11.889191 27 14 25.489251 14 23.28125 C 14 21.536852 11.915281 19 9.96875 19 z "
    + style="opacity:1;fill:url(#linearGradient8882);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + id="path8838"
    + d="M 9.96875,17.96875 C 8.6328774,17.96875 7.5286409,18.753017 6.875,19.71875 C 6.2213591,20.684483 5.9062499,21.848884 5.90625,23 C 5.90625,25.694817 7.8977844,28.03125 10.46875,28.03125 C 11.539674,28.03125 12.596335,27.527372 13.46875,26.71875 C 14.341165,25.910128 15.03125,24.709464 15.03125,23.28125 C 15.03125,22.003744 14.362496,20.808851 13.46875,19.8125 C 12.575004,18.816149 11.384009,17.96875 9.96875,17.96875 z"
    + transform="translate(1.09375,-0.96875)" />
    + <path
    + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 15.09375,22.321339 C 15.09375,24.52934 12.987869,26.03125 11.567428,26.03125 C 9.6208995,26.03125 8.0411075,24.239249 8.0411075,22.031249 C 8.0411075,20.112462 9.1240375,18.03125 11.070566,18.03125 C 13.017097,18.03125 15.09375,20.576941 15.09375,22.321339 z"
    + id="path5157"
    + sodipodi:nodetypes="csssc" />
    + <path
    + sodipodi:type="arc"
    + style="opacity:1;fill:#6b1a80;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    + id="path5162"
    + sodipodi:cx="10.169642"
    + sodipodi:cy="24.3125"
    + sodipodi:rx="1.2410715"
    + sodipodi:ry="1.2946428"
    + d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    + transform="matrix(1.237779,0,0,1.544828,-1.0301916,-15.38768)" />
    + <path
    + sodipodi:type="arc"
    + style="opacity:1;fill:url(#linearGradient8854);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    + id="path5164"
    + sodipodi:cx="10.169642"
    + sodipodi:cy="24.3125"
    + sodipodi:rx="1.2410715"
    + sodipodi:ry="1.2946428"
    + d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    + transform="matrix(0.4028775,0,0,0.772414,7.4966298,3.2519345)" />
    + <path
    + sodipodi:type="inkscape:offset"
    + inkscape:radius="1.0250763"
    + inkscape:original="M 24.71875 19 C 23.921039 19.030682 23.197708 19.355158 22.78125 19.8125 C 21.639847 21.065961 21.758036 23.254972 23.0625 24.6875 C 24.196099 25.932389 26.077347 26.565958 27.21875 25.3125 C 28.360153 24.05904 28.061824 21.069247 27.03125 19.9375 C 26.379019 19.221236 25.516461 18.969318 24.71875 19 z "
    + style="opacity:1;fill:url(#linearGradient8890);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + id="path8846"
    + d="M 24.6875,17.96875 C 23.636213,18.009185 22.683031,18.409233 22.03125,19.125 C 20.466122,20.843786 20.678782,23.580894 22.3125,25.375 C 23.011072,26.142154 23.917332,26.724837 24.9375,26.9375 C 25.957668,27.150163 27.151401,26.89759 27.96875,26 C 28.842555,25.04041 29.039766,23.753975 28.96875,22.53125 C 28.897734,21.308525 28.565932,20.111716 27.78125,19.25 C 26.906665,18.289553 25.728905,17.928695 24.6875,17.96875 z"
    + transform="translate(1.0348213,-0.9657729)" />
    + <path
    + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 28.07086,18.984912 C 26.766398,17.552385 24.644242,17.934034 23.811326,18.848719 C 22.669923,20.102179 22.802261,22.282108 24.106725,23.714636 C 25.240323,24.959524 27.104889,25.612445 28.246292,24.358987 C 29.387696,23.105527 29.101434,20.116659 28.07086,18.984912 z"
    + id="path5169"
    + sodipodi:nodetypes="csssc" />
    + <path
    + sodipodi:type="arc"
    + style="opacity:1;fill:#6b1a80;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    + id="path5171"
    + sodipodi:cx="10.169642"
    + sodipodi:cy="24.3125"
    + sodipodi:rx="1.2410715"
    + sodipodi:ry="1.2946428"
    + d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    + transform="matrix(1.208632,0,0,1.598777,13.243467,-16.766194)" />
    + <path
    + transform="matrix(0.9902622,0,0,1.0468412,0.6803699,-1.6322179)"
    + style="fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1;filter:url(#filter8730)"
    + d="M 19.0625,25.21875 C 18.601377,25.21875 18.273569,25.504007 18.09375,25.71875 C 17.913931,25.933493 17.816385,26.151947 17.6875,26.375 C 17.42973,26.821106 17.161718,27.281061 16.8125,27.625 C 16.463282,27.968939 15.753081,28.431077 15.090677,28.457564 C 13.768981,28.152971 12.567412,27.574567 11.44337,27.388802 C 11.190855,27.626888 11.124903,28.002654 11.28125,28.3125 C 12.447615,30.480389 13.704893,31.652553 16.158709,32.98462 C 17.36658,33.640319 19.551532,34.28873 20.662703,34.736506 C 22.03071,33.835346 25.543472,30.338576 25.75,28.125 C 25.844084,27.808258 25.729503,27.466527 25.463497,27.270522 C 24.478872,27.816677 23.360045,28.165794 22.250307,28.426314 C 21.786763,28.408951 21.509989,27.99255 21.21875,27.65625 C 20.927511,27.31995 20.680079,26.828945 20.4375,26.375 C 20.31621,26.148027 20.208768,25.937142 20.03125,25.71875 C 19.853732,25.500358 19.525474,25.21875 19.0625,25.21875 z"
    + id="path8696"
    + sodipodi:nodetypes="cssscccsccccsssc" />
    + <path
    + sodipodi:type="inkscape:offset"
    + inkscape:radius="0.36336106"
    + inkscape:original="M 15.8125 27 C 15.361611 27 15 27.422198 15 27.9375 C 15 28.452801 15.361612 28.874999 15.8125 28.875 L 21.1875 28.875 C 21.638389 28.875 22 28.452801 22 27.9375 C 22 27.422199 21.638389 27 21.1875 27 L 15.8125 27 z "
    + style="opacity:1;fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter8792)"
    + id="path8738"
    + d="M 15.8125,26.625 C 15.134785,26.625 14.625,27.252207 14.625,27.9375 C 14.625,28.622793 15.134785,29.249998 15.8125,29.25 L 21.1875,29.25 C 21.865216,29.25 22.375,28.622793 22.375,27.9375 C 22.375,27.252207 21.865216,26.625 21.1875,26.625 L 15.8125,26.625 z"
    + transform="translate(1.0492259,-0.5907729)" />
    + <rect
    + style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    + id="rect5215"
    + width="7"
    + height="1.8602936"
    + x="16.017977"
    + y="26.173933"
    + rx="0.81387848"
    + ry="0.93014681" />
    + <path
    + style="fill:url(#linearGradient8692);fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
    + d="M 12.017976,27.284466 C 18.332186,30.947891 18.535242,25.034227 19.622035,25.034227 C 20.693875,25.034227 20.889384,30.946219 26.017976,27.230273 C 25.73271,30.311439 22.068246,33.065477 21.020058,34.065477 C 18.988153,32.971727 14.03875,31.7541 12.017976,27.284466 z"
    + id="rect5187"
    + sodipodi:nodetypes="czccc" />
    + <path
    + sodipodi:type="arc"
    + style="opacity:0.61111109;fill:url(#linearGradient8814);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    + id="path8806"
    + sodipodi:cx="18.097515"
    + sodipodi:cy="29.72571"
    + sodipodi:rx="0.28726214"
    + sodipodi:ry="0.6408155"
    + d="M 18.384777,29.72571 A 0.28726214,0.6408155 0 1 1 17.810253,29.72571 A 0.28726214,0.6408155 0 1 1 18.384777,29.72571 z"
    + transform="matrix(-1.0945752,0.9356227,0.6067766,1.5209514,23.290203,-33.120817)" />
    + <path
    + sodipodi:type="arc"
    + style="opacity:0.61111109;fill:url(#linearGradient8818);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    + id="path8816"
    + sodipodi:cx="18.097515"
    + sodipodi:cy="29.72571"
    + sodipodi:rx="0.28726214"
    + sodipodi:ry="0.6408155"
    + d="M 18.384777,29.72571 A 0.28726214,0.6408155 0 1 1 17.810253,29.72571 A 0.28726214,0.6408155 0 1 1 18.384777,29.72571 z"
    + transform="matrix(1.0945741,0.9356227,-0.606776,1.5209514,16.745752,-33.098342)" />
    + <path
    + sodipodi:type="arc"
    + style="opacity:1;fill:url(#linearGradient8858);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    + id="path8856"
    + sodipodi:cx="10.169642"
    + sodipodi:cy="24.3125"
    + sodipodi:rx="1.2410715"
    + sodipodi:ry="1.2946428"
    + d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    + transform="matrix(0.4028775,0,0,0.772414,21.437701,3.254912)" />
    + <path
    + style="opacity:0.61111108999999997;fill:url(#linearGradient8738);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    + d="M 3.375,44.9375 C 1.75,44.5625 1.125,41.0625 4.1875,35.75 C 5.3980639,40.757973 11,44.375 11,44.375 L 3.375,44.9375 z"
    + id="rect7959"
    + sodipodi:nodetypes="cccc" />
    + <path
    + style="opacity:0.61111108999999997;fill:url(#linearGradient8742);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    + d="M 33.647731,44.9375 C 35.272732,44.5625 36.772732,42.5625 28.772731,38.5625 C 29.124668,42.695473 26.022731,44.375 26.022731,44.375 L 33.647731,44.9375 z"
    + id="path8740"
    + sodipodi:nodetypes="cccc" />
    + </g>
    +</svg>
    --- a/pidgin/data/icons/hicolor/scalable/apps/pidgin.svg Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,610 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8" standalone="no"?>
    -<!-- Created with Inkscape (http://www.inkscape.org/) -->
    -<svg
    - xmlns:dc="http://purl.org/dc/elements/1.1/"
    - xmlns:cc="http://creativecommons.org/ns#"
    - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    - xmlns:svg="http://www.w3.org/2000/svg"
    - xmlns="http://www.w3.org/2000/svg"
    - xmlns:xlink="http://www.w3.org/1999/xlink"
    - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    - width="48px"
    - height="48px"
    - id="svg4345"
    - sodipodi:version="0.32"
    - inkscape:version="0.46"
    - sodipodi:docbase="/home/hbons/Desktop/2.0.2/pidgin/pixmaps/icons/48/scalable"
    - sodipodi:docname="pidgin.svg"
    - inkscape:export-filename="/home/hbons/Desktop/pidgin48.png"
    - inkscape:export-xdpi="90"
    - inkscape:export-ydpi="90"
    - inkscape:output_extension="org.inkscape.output.svg.inkscape">
    - <defs
    - id="defs4347">
    - <linearGradient
    - id="linearGradient8744"
    - inkscape:collect="always">
    - <stop
    - id="stop8746"
    - offset="0"
    - style="stop-color:#7a1d90;stop-opacity:1" />
    - <stop
    - id="stop8748"
    - offset="1"
    - style="stop-color:#6b3678;stop-opacity:0;" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8732">
    - <stop
    - style="stop-color:#6b3678;stop-opacity:1;"
    - offset="0"
    - id="stop8734" />
    - <stop
    - style="stop-color:#6b3678;stop-opacity:0;"
    - offset="1"
    - id="stop8736" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8904">
    - <stop
    - style="stop-color:#729fcf;stop-opacity:1;"
    - offset="0"
    - id="stop8906" />
    - <stop
    - style="stop-color:#25486d;stop-opacity:1"
    - offset="1"
    - id="stop8908" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8884">
    - <stop
    - style="stop-color:#9a5ba8;stop-opacity:1;"
    - offset="0"
    - id="stop8886" />
    - <stop
    - style="stop-color:#6b3e75;stop-opacity:1"
    - offset="1"
    - id="stop8888" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8876">
    - <stop
    - style="stop-color:#9a5ba8;stop-opacity:1;"
    - offset="0"
    - id="stop8878" />
    - <stop
    - style="stop-color:#744380;stop-opacity:1"
    - offset="1"
    - id="stop8880" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8868">
    - <stop
    - style="stop-color:#3b1941;stop-opacity:1;"
    - offset="0"
    - id="stop8870" />
    - <stop
    - style="stop-color:#a646b7;stop-opacity:1"
    - offset="1"
    - id="stop8872" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8860">
    - <stop
    - style="stop-color:#3b1941;stop-opacity:1;"
    - offset="0"
    - id="stop8862" />
    - <stop
    - style="stop-color:#a949b9;stop-opacity:1"
    - offset="1"
    - id="stop8864" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8848">
    - <stop
    - style="stop-color:#000000;stop-opacity:1;"
    - offset="0"
    - id="stop8850" />
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1"
    - offset="1"
    - id="stop8852" />
    - </linearGradient>
    - <linearGradient
    - id="linearGradient8820"
    - inkscape:collect="always">
    - <stop
    - id="stop8822"
    - offset="0"
    - style="stop-color:#522400;stop-opacity:1" />
    - <stop
    - id="stop8824"
    - offset="1"
    - style="stop-color:#6e3100;stop-opacity:0;" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8808">
    - <stop
    - style="stop-color:#6e3100;stop-opacity:1;"
    - offset="0"
    - id="stop8810" />
    - <stop
    - style="stop-color:#6e3100;stop-opacity:0;"
    - offset="1"
    - id="stop8812" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient8686">
    - <stop
    - style="stop-color:#fdb751;stop-opacity:1"
    - offset="0"
    - id="stop8688" />
    - <stop
    - style="stop-color:#ce5c00;stop-opacity:1"
    - offset="1"
    - id="stop8690" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2834">
    - <stop
    - style="stop-color:#7e408d;stop-opacity:1"
    - offset="0"
    - id="stop2836" />
    - <stop
    - style="stop-color:#82508e;stop-opacity:0;"
    - offset="1"
    - id="stop2838" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2826">
    - <stop
    - style="stop-color:#3b1941;stop-opacity:1;"
    - offset="0"
    - id="stop2828" />
    - <stop
    - style="stop-color:#3b1941;stop-opacity:0;"
    - offset="1"
    - id="stop2830" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient2816">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:1"
    - offset="0"
    - id="stop2818" />
    - <stop
    - style="stop-color:#eeeeec;stop-opacity:0;"
    - offset="1"
    - id="stop2820" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient6537">
    - <stop
    - style="stop-color:white;stop-opacity:1;"
    - offset="0"
    - id="stop6539" />
    - <stop
    - style="stop-color:#d3e1f1;stop-opacity:1"
    - offset="1"
    - id="stop6541" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - id="linearGradient6506">
    - <stop
    - style="stop-color:#ffffff;stop-opacity:0.95477384"
    - offset="0"
    - id="stop6508" />
    - <stop
    - style="stop-color:#eeeeec;stop-opacity:0;"
    - offset="1"
    - id="stop6510" />
    - </linearGradient>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient6506"
    - id="linearGradient6512"
    - x1="15.645709"
    - y1="40.668503"
    - x2="15.645709"
    - y2="47.022106"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1,0,0,0.988192,1.5624997,-2.39645)" />
    - <radialGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2816"
    - id="radialGradient2824"
    - cx="14.930223"
    - cy="25.801632"
    - fx="14.930223"
    - fy="25.801632"
    - r="16.390338"
    - gradientTransform="matrix(1.3364897,0,0,1.3894845,-0.759152,-10.711989)"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2826"
    - id="linearGradient2832"
    - x1="13.191773"
    - y1="41.606163"
    - x2="13.191773"
    - y2="49.067719"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1,0,0,0.988192,1.4473537,-2.486208)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient2834"
    - id="linearGradient2840"
    - x1="11.373499"
    - y1="43.444576"
    - x2="11.373499"
    - y2="47.757988"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1,0,0,0.988192,1.4473537,-2.486208)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8686"
    - id="linearGradient8692"
    - x1="18.5"
    - y1="29.911009"
    - x2="19.985121"
    - y2="29.853554"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1.0769231,0,0,1.1428571,-0.905101,-4.6800586)" />
    - <filter
    - inkscape:collect="always"
    - id="filter8730">
    - <feGaussianBlur
    - inkscape:collect="always"
    - stdDeviation="0.27197245"
    - id="feGaussianBlur8732" />
    - </filter>
    - <filter
    - inkscape:collect="always"
    - id="filter8792"
    - x="-0.095301818"
    - width="1.1906036"
    - y="-0.27704017"
    - height="1.5540803">
    - <feGaussianBlur
    - inkscape:collect="always"
    - stdDeviation="0.31022727"
    - id="feGaussianBlur8794" />
    - </filter>
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8820"
    - id="linearGradient8814"
    - x1="18.339697"
    - y1="29.338558"
    - x2="18.031723"
    - y2="30.431053"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8808"
    - id="linearGradient8818"
    - gradientUnits="userSpaceOnUse"
    - x1="17.969458"
    - y1="29.494703"
    - x2="18.143806"
    - y2="30.188351" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8848"
    - id="linearGradient8854"
    - x1="10.48653"
    - y1="25.21174"
    - x2="9.7512932"
    - y2="23.675837"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8848"
    - id="linearGradient8858"
    - gradientUnits="userSpaceOnUse"
    - x1="10.498732"
    - y1="24.936121"
    - x2="9.6415968"
    - y2="23.675837" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8860"
    - id="linearGradient8866"
    - x1="13.061977"
    - y1="10.027351"
    - x2="16.545418"
    - y2="12.891665"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1.50247,0,0,1,-6.5946403,-2.139701)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8868"
    - id="linearGradient8874"
    - x1="12.409452"
    - y1="10.602999"
    - x2="16.140554"
    - y2="13.895189"
    - gradientUnits="userSpaceOnUse"
    - gradientTransform="matrix(1.236264,0,0,0.549587,-5.4828863,3.775206)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8876"
    - id="linearGradient8882"
    - x1="10.46875"
    - y1="25.3125"
    - x2="9.53125"
    - y2="19.6875"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8884"
    - id="linearGradient8890"
    - x1="23.881994"
    - y1="24.343237"
    - x2="24.973602"
    - y2="19.216713"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8904"
    - id="linearGradient8910"
    - x1="26.125"
    - y1="1.8037834"
    - x2="41.875"
    - y2="33.678783"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8744"
    - id="linearGradient8738"
    - x1="4.0852318"
    - y1="39.097038"
    - x2="4.0852318"
    - y2="44.321774"
    - gradientUnits="userSpaceOnUse" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient8732"
    - id="linearGradient8742"
    - gradientUnits="userSpaceOnUse"
    - x1="4.0852318"
    - y1="40.416641"
    - x2="4.0852318"
    - y2="43.352409"
    - gradientTransform="matrix(-1,0,0,1,37.022732,0)" />
    - <linearGradient
    - inkscape:collect="always"
    - xlink:href="#linearGradient6537"
    - id="linearGradient7977"
    - gradientUnits="userSpaceOnUse"
    - x1="30.5"
    - y1="4.8871226"
    - x2="30.5"
    - y2="22.781603" />
    - </defs>
    - <sodipodi:namedview
    - id="base"
    - pagecolor="#ffffff"
    - bordercolor="#666666"
    - borderopacity="1.0"
    - inkscape:pageopacity="0.0"
    - inkscape:pageshadow="2"
    - inkscape:zoom="2.828427"
    - inkscape:cx="63.722923"
    - inkscape:cy="33.120105"
    - inkscape:current-layer="layer1"
    - showgrid="true"
    - inkscape:grid-bbox="true"
    - inkscape:document-units="px"
    - inkscape:window-width="1434"
    - inkscape:window-height="840"
    - inkscape:window-x="-2"
    - inkscape:window-y="0"
    - showguides="true"
    - inkscape:guide-bbox="true"
    - inkscape:grid-points="true"
    - inkscape:snap-bbox="true"
    - inkscape:snap-nodes="false"
    - objecttolerance="10"
    - gridtolerance="10">
    - <inkscape:grid
    - type="xygrid"
    - id="grid7914"
    - visible="true"
    - enabled="true" />
    - </sodipodi:namedview>
    - <metadata
    - id="metadata4350">
    - <rdf:RDF>
    - <cc:Work
    - rdf:about="">
    - <dc:format>image/svg+xml</dc:format>
    - <dc:type
    - rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    - </cc:Work>
    - </rdf:RDF>
    - </metadata>
    - <g
    - id="layer1"
    - inkscape:label="Layer 1"
    - inkscape:groupmode="layer">
    - <path
    - style="fill:#efefef;fill-opacity:1;stroke:url(#linearGradient8910);stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 20.53125,1.5 C 17.192693,1.5 14.5,4.611235 14.5,8.46875 L 14.5,20.53125 C 14.5,24.388765 17.192693,27.5 20.53125,27.5 L 34.530203,27.5 C 34.530203,27.5 34.406442,30.680041 32.92887,32.534344 C 38.120414,32.534344 39.353553,27.485509 39.353553,27.485509 L 41.5,27.5 C 45.512737,27.5 46.5,24.38319 46.5,20.53125 L 46.5,8.46875 C 46.5,4.6112353 43.807307,1.5 40.46875,1.5 L 20.53125,1.5 z"
    - id="rect5498"
    - sodipodi:nodetypes="cccccccccccc" />
    - <path
    - style="fill:url(#linearGradient7977);fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 20.53125,2.5 C 17.815701,2.5 15.5,5.0778932 15.5,8.46875 L 15.5,20.53125 C 15.5,23.922107 17.815701,26.5 20.53125,26.5 L 34.53125,26.5 C 35.082875,26.501589 35.529661,26.948375 35.53125,27.5 C 35.53125,27.5 35.367799,30.212738 34.75,31.34218 C 36.852551,31.34218 38.53125,26.6875 38.53125,26.6875 C 38.704261,26.563588 38.912203,26.497922 39.125,26.5 L 41.5,26.5 C 43.246736,26.5 44.065452,25.932701 44.65625,24.9375 C 45.247048,23.942299 45.5,22.371547 45.5,20.53125 L 45.5,8.46875 C 45.5,5.0778935 43.184299,2.5 40.46875,2.5 L 20.53125,2.5 z"
    - id="path6535"
    - sodipodi:nodetypes="ccccccccccscccc" />
    - <rect
    - style="opacity:1;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect8734"
    - width="7"
    - height="1.8602936"
    - x="15.642976"
    - y="26.409245"
    - rx="0.81387848"
    - ry="0.93014681" />
    - <path
    - style="fill:url(#linearGradient8866);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 10.968089,7.6821669 C 13.825874,5.8764931 18.683474,9.095739 19.201853,10.751964 L 15.25649,10.751964 C 15.19399,8.408214 10.968089,7.6821669 10.968089,7.6821669 z"
    - id="rect5189"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="fill:url(#linearGradient8874);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 8.9209727,9.9271812 C 10.543462,8.0699992 15.5336,9.5097671 15.5336,10.860297 L 11.743475,11.411821 C 12.958815,9.7987339 8.9209727,9.9271812 8.9209727,9.9271812 z"
    - id="path5192"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="opacity:1;fill:url(#linearGradient2840);fill-opacity:1;stroke:url(#linearGradient2832);stroke-width:0.99999994000000003;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - d="M 16.447354,10.481822 C 9.8233516,10.481822 3.9308558,15.93012 4.5098537,22.712688 C 5.7768579,37.554787 1.9403503,35.543378 1.5,42.316391 C 1.5,44.375904 2.4854201,45.355587 4.5920502,45.355587 C 5.7795388,45.355587 30.807389,45.5 33.498388,45.5 C 35.612515,45.5 35.447354,43.46472 35.447354,43.46472 C 35.447354,40.830782 29.045256,38.620654 27.260807,36.687231 C 25.432389,34.709838 27.447354,27.653648 27.447354,27.653648 C 28.08866,26.141912 28.447354,24.466812 28.447354,22.712688 C 28.447354,15.894164 23.071356,10.481822 16.447354,10.481822 z"
    - id="path5176"
    - sodipodi:nodetypes="cscccszcsc" />
    - <path
    - style="opacity:0.55000000000000004;fill:url(#radialGradient2824);fill-opacity:1;stroke:url(#linearGradient6512);stroke-width:0.99999994000000003;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 16.25,11.5 C 10.237704,11.5 5.0276636,16.611038 5.4999997,22.834065 C 6.0644244,30.27036 5.8175038,32.821583 4.0236672,37.541192 C 1.1437418,42.286379 3.1235207,44.533069 5.4658637,44.533069 C 7.4024447,44.533069 25.520621,44.503627 31.346594,44.503627 C 32.513801,44.503627 34.5,45.001748 34.5,43.554478 C 34.5,43.335354 34.300249,42.923343 33.75,42.411881 C 33.199751,41.90042 32.37676,41.351214 31.46875,40.806069 C 30.56074,40.260924 29.59071,39.709456 28.71875,39.169376 C 27.84679,38.629297 27.074154,38.125147 26.53125,37.409159 C 25.921245,36.604678 25.641306,35.658991 25.586842,34.629869 C 25.532378,33.600747 25.670221,32.49665 25.787061,31.449126 C 26.020741,29.354077 26.53125,27.527239 26.53125,27.527239 C 26.54624,27.4637 26.567163,27.401673 26.59375,27.341953 C 27.181231,25.957097 27.5,24.414481 27.5,22.802446 C 27.5,16.531779 22.272436,11.5 16.25,11.5 z"
    - id="path5241"
    - sodipodi:nodetypes="cscccssssssscssc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="1.0174263"
    - inkscape:original="M 9.96875 19 C 8.022221 19 6.9374998 21.081214 6.9375 23 C 6.9375 25.208 8.5222219 27 10.46875 27 C 11.889191 27 14 25.489251 14 23.28125 C 14 21.536852 11.915281 19 9.96875 19 z "
    - style="opacity:1;fill:url(#linearGradient8882);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - id="path8838"
    - d="M 9.96875,17.96875 C 8.6328774,17.96875 7.5286409,18.753017 6.875,19.71875 C 6.2213591,20.684483 5.9062499,21.848884 5.90625,23 C 5.90625,25.694817 7.8977844,28.03125 10.46875,28.03125 C 11.539674,28.03125 12.596335,27.527372 13.46875,26.71875 C 14.341165,25.910128 15.03125,24.709464 15.03125,23.28125 C 15.03125,22.003744 14.362496,20.808851 13.46875,19.8125 C 12.575004,18.816149 11.384009,17.96875 9.96875,17.96875 z"
    - transform="translate(1.09375,-0.96875)" />
    - <path
    - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 15.09375,22.321339 C 15.09375,24.52934 12.987869,26.03125 11.567428,26.03125 C 9.6208995,26.03125 8.0411075,24.239249 8.0411075,22.031249 C 8.0411075,20.112462 9.1240375,18.03125 11.070566,18.03125 C 13.017097,18.03125 15.09375,20.576941 15.09375,22.321339 z"
    - id="path5157"
    - sodipodi:nodetypes="csssc" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#6b1a80;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path5162"
    - sodipodi:cx="10.169642"
    - sodipodi:cy="24.3125"
    - sodipodi:rx="1.2410715"
    - sodipodi:ry="1.2946428"
    - d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    - transform="matrix(1.237779,0,0,1.544828,-1.0301916,-15.38768)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:url(#linearGradient8854);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path5164"
    - sodipodi:cx="10.169642"
    - sodipodi:cy="24.3125"
    - sodipodi:rx="1.2410715"
    - sodipodi:ry="1.2946428"
    - d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    - transform="matrix(0.4028775,0,0,0.772414,7.4966298,3.2519345)" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="1.0250763"
    - inkscape:original="M 24.71875 19 C 23.921039 19.030682 23.197708 19.355158 22.78125 19.8125 C 21.639847 21.065961 21.758036 23.254972 23.0625 24.6875 C 24.196099 25.932389 26.077347 26.565958 27.21875 25.3125 C 28.360153 24.05904 28.061824 21.069247 27.03125 19.9375 C 26.379019 19.221236 25.516461 18.969318 24.71875 19 z "
    - style="opacity:1;fill:url(#linearGradient8890);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - id="path8846"
    - d="M 24.6875,17.96875 C 23.636213,18.009185 22.683031,18.409233 22.03125,19.125 C 20.466122,20.843786 20.678782,23.580894 22.3125,25.375 C 23.011072,26.142154 23.917332,26.724837 24.9375,26.9375 C 25.957668,27.150163 27.151401,26.89759 27.96875,26 C 28.842555,25.04041 29.039766,23.753975 28.96875,22.53125 C 28.897734,21.308525 28.565932,20.111716 27.78125,19.25 C 26.906665,18.289553 25.728905,17.928695 24.6875,17.96875 z"
    - transform="translate(1.0348213,-0.9657729)" />
    - <path
    - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 28.07086,18.984912 C 26.766398,17.552385 24.644242,17.934034 23.811326,18.848719 C 22.669923,20.102179 22.802261,22.282108 24.106725,23.714636 C 25.240323,24.959524 27.104889,25.612445 28.246292,24.358987 C 29.387696,23.105527 29.101434,20.116659 28.07086,18.984912 z"
    - id="path5169"
    - sodipodi:nodetypes="csssc" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:#6b1a80;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path5171"
    - sodipodi:cx="10.169642"
    - sodipodi:cy="24.3125"
    - sodipodi:rx="1.2410715"
    - sodipodi:ry="1.2946428"
    - d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    - transform="matrix(1.208632,0,0,1.598777,13.243467,-16.766194)" />
    - <path
    - transform="matrix(0.9902622,0,0,1.0468412,0.6803699,-1.6322179)"
    - style="fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1;filter:url(#filter8730)"
    - d="M 19.0625,25.21875 C 18.601377,25.21875 18.273569,25.504007 18.09375,25.71875 C 17.913931,25.933493 17.816385,26.151947 17.6875,26.375 C 17.42973,26.821106 17.161718,27.281061 16.8125,27.625 C 16.463282,27.968939 15.753081,28.431077 15.090677,28.457564 C 13.768981,28.152971 12.567412,27.574567 11.44337,27.388802 C 11.190855,27.626888 11.124903,28.002654 11.28125,28.3125 C 12.447615,30.480389 13.704893,31.652553 16.158709,32.98462 C 17.36658,33.640319 19.551532,34.28873 20.662703,34.736506 C 22.03071,33.835346 25.543472,30.338576 25.75,28.125 C 25.844084,27.808258 25.729503,27.466527 25.463497,27.270522 C 24.478872,27.816677 23.360045,28.165794 22.250307,28.426314 C 21.786763,28.408951 21.509989,27.99255 21.21875,27.65625 C 20.927511,27.31995 20.680079,26.828945 20.4375,26.375 C 20.31621,26.148027 20.208768,25.937142 20.03125,25.71875 C 19.853732,25.500358 19.525474,25.21875 19.0625,25.21875 z"
    - id="path8696"
    - sodipodi:nodetypes="cssscccsccccsssc" />
    - <path
    - sodipodi:type="inkscape:offset"
    - inkscape:radius="0.36336106"
    - inkscape:original="M 15.8125 27 C 15.361611 27 15 27.422198 15 27.9375 C 15 28.452801 15.361612 28.874999 15.8125 28.875 L 21.1875 28.875 C 21.638389 28.875 22 28.452801 22 27.9375 C 22 27.422199 21.638389 27 21.1875 27 L 15.8125 27 z "
    - style="opacity:1;fill:#5c3566;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter8792)"
    - id="path8738"
    - d="M 15.8125,26.625 C 15.134785,26.625 14.625,27.252207 14.625,27.9375 C 14.625,28.622793 15.134785,29.249998 15.8125,29.25 L 21.1875,29.25 C 21.865216,29.25 22.375,28.622793 22.375,27.9375 C 22.375,27.252207 21.865216,26.625 21.1875,26.625 L 15.8125,26.625 z"
    - transform="translate(1.0492259,-0.5907729)" />
    - <rect
    - style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="rect5215"
    - width="7"
    - height="1.8602936"
    - x="16.017977"
    - y="26.173933"
    - rx="0.81387848"
    - ry="0.93014681" />
    - <path
    - style="fill:url(#linearGradient8692);fill-opacity:1;stroke:none;stroke-width:1.0283047;stroke-miterlimit:4;stroke-opacity:1"
    - d="M 12.017976,27.284466 C 18.332186,30.947891 18.535242,25.034227 19.622035,25.034227 C 20.693875,25.034227 20.889384,30.946219 26.017976,27.230273 C 25.73271,30.311439 22.068246,33.065477 21.020058,34.065477 C 18.988153,32.971727 14.03875,31.7541 12.017976,27.284466 z"
    - id="rect5187"
    - sodipodi:nodetypes="czccc" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.61111109;fill:url(#linearGradient8814);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="path8806"
    - sodipodi:cx="18.097515"
    - sodipodi:cy="29.72571"
    - sodipodi:rx="0.28726214"
    - sodipodi:ry="0.6408155"
    - d="M 18.384777,29.72571 A 0.28726214,0.6408155 0 1 1 17.810253,29.72571 A 0.28726214,0.6408155 0 1 1 18.384777,29.72571 z"
    - transform="matrix(-1.0945752,0.9356227,0.6067766,1.5209514,23.290203,-33.120817)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:0.61111109;fill:url(#linearGradient8818);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - id="path8816"
    - sodipodi:cx="18.097515"
    - sodipodi:cy="29.72571"
    - sodipodi:rx="0.28726214"
    - sodipodi:ry="0.6408155"
    - d="M 18.384777,29.72571 A 0.28726214,0.6408155 0 1 1 17.810253,29.72571 A 0.28726214,0.6408155 0 1 1 18.384777,29.72571 z"
    - transform="matrix(1.0945741,0.9356227,-0.606776,1.5209514,16.745752,-33.098342)" />
    - <path
    - sodipodi:type="arc"
    - style="opacity:1;fill:url(#linearGradient8858);fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
    - id="path8856"
    - sodipodi:cx="10.169642"
    - sodipodi:cy="24.3125"
    - sodipodi:rx="1.2410715"
    - sodipodi:ry="1.2946428"
    - d="M 11.410714,24.3125 A 1.2410715,1.2946428 0 1 1 8.928571,24.3125 A 1.2410715,1.2946428 0 1 1 11.410714,24.3125 z"
    - transform="matrix(0.4028775,0,0,0.772414,21.437701,3.254912)" />
    - <path
    - style="opacity:0.61111108999999997;fill:url(#linearGradient8738);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - d="M 3.375,44.9375 C 1.75,44.5625 1.125,41.0625 4.1875,35.75 C 5.3980639,40.757973 11,44.375 11,44.375 L 3.375,44.9375 z"
    - id="rect7959"
    - sodipodi:nodetypes="cccc" />
    - <path
    - style="opacity:0.61111108999999997;fill:url(#linearGradient8742);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
    - d="M 33.647731,44.9375 C 35.272732,44.5625 36.772732,42.5625 28.772731,38.5625 C 29.124668,42.695473 26.022731,44.375 26.022731,44.375 L 33.647731,44.9375 z"
    - id="path8740"
    - sodipodi:nodetypes="cccc" />
    - </g>
    -</svg>
    --- a/pidgin/data/icons/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/data/icons/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -1,5 +1,5 @@
    ICONS = [
    - 'pidgin'
    + 'im.pidgin.Pidgin3'
    ]
    foreach icon : ICONS
    @@ -14,10 +14,10 @@
    endforeach
    EXCLUDE_FILES = [
    - '16x16/apps/pidgin.png',
    - '22x22/apps/pidgin.png',
    - '48x48/apps/pidgin.png',
    - 'scalable/apps/pidgin.svg',
    + '16x16/apps/im.pidgin.Pidgin3.png',
    + '22x22/apps/im.pidgin.Pidgin3.png',
    + '48x48/apps/im.pidgin.Pidgin3.png',
    + 'scalable/apps/im.pidgin.Pidgin3.svg',
    ]
    EXCLUDE_DIRS = [
    --- a/pidgin/data/im.pidgin.Pidgin3.desktop.in.in Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/data/im.pidgin.Pidgin3.desktop.in.in Tue Oct 08 21:48:28 2019 -0500
    @@ -3,7 +3,7 @@
    GenericName=Internet Messenger
    Comment=Chat over IM. Supports AIM, Google Talk, Jabber/XMPP, and more
    Exec=pidgin3 %U
    -Icon=pidgin
    +Icon=im.pidgin.Pidgin3
    StartupNotify=true
    Terminal=false
    Type=Application
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/glade/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,6 @@
    +pidgin3_xml = configure_file(
    + input : 'pidgin3.xml.in',
    + output : 'pidgin3.xml',
    + configuration : version_conf,
    + install : true,
    + install_dir : join_paths(get_option('datadir'), 'glade', 'catalogs'))
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/glade/pidgin3.xml.in Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,15 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<glade-catalog name="pidgin" version="@PURPLE_MAJOR_VERSION@.@PURPLE_MINOR_VERSION@" library="pidgin3">
    + <glade-widget-classes>
    + <glade-widget-class name="PidginAccountChooser" generic-name="account_chooser" title="AccountChooser"/>
    + <glade-widget-class name="PidginInviteDialog" generic-name="invite_dialog" title="InviteDialog"/>
    + <glade-widget-class name="PidginMenuTray" generic-name="menu_tray" title="MenuTray"/>
    + <glade-widget-class name="PidginScrollBook" generic-name="scroll_book" title="ScrollBook"/>
    + </glade-widget-classes>
    + <glade-widget-group name="pidgin" title="Pidgin">
    + <glade-widget-class-ref name="PidginAccountChooser"/>
    + <glade-widget-class-ref name="PidginInviteDialog"/>
    + <glade-widget-class-ref name="PidginMenuTray"/>
    + <glade-widget-class-ref name="PidginScrollBook"/>
    + </glade-widget-group>
    +</glade-catalog>
    --- a/pidgin/gtk3compat.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtk3compat.h Tue Oct 08 21:48:28 2019 -0500
    @@ -43,22 +43,18 @@
    event ? event->button : 0, gdk_event_get_time(event));
    }
    -#if !GTK_CHECK_VERSION(3,16,0)
    -
    static inline void
    -gtk_label_set_xalign(GtkLabel *label, gfloat xalign)
    +gtk_popover_popup(GtkPopover *popover)
    {
    - g_object_set(label, "xalign", xalign, NULL);
    + gtk_widget_show(GTK_WIDGET(popover));
    }
    static inline void
    -gtk_label_set_yalign(GtkLabel *label, gfloat yalign)
    +gtk_popover_popdown(GtkPopover *popover)
    {
    - g_object_set(label, "yalign", yalign, NULL);
    + gtk_widget_hide(GTK_WIDGET(popover));
    }
    -#endif /* 3.16.0 */
    -
    #endif /* 3.22.0 */
    #endif /* _PIDGINGTK3COMPAT_H_ */
    --- a/pidgin/gtkaccount.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkaccount.c Tue Oct 08 21:48:28 2019 -0500
    @@ -125,7 +125,7 @@
    GtkWidget *icon_hbox;
    GtkWidget *icon_check;
    GtkWidget *icon_entry;
    - GtkWidget *icon_filesel;
    + GtkFileChooserNative *icon_filesel;
    GtkWidget *icon_preview;
    GtkWidget *icon_text;
    PurpleImage *icon_img;
    @@ -258,13 +258,13 @@
    gtk_widget_grab_focus(dialog->protocol_menu);
    - if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER_IFACE, register_user)) {
    + if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER, register_user)) {
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
    dialog->register_button), FALSE);
    gtk_widget_hide(dialog->register_button);
    } else {
    - if (dialog->protocol != NULL &&
    - (purple_protocol_get_options(dialog->protocol) & OPT_PROTO_REGISTER_NOSCREENNAME)) {
    + if (purple_protocol_get_options(dialog->protocol) &
    + OPT_PROTO_REGISTER_NOSCREENNAME) {
    gtk_widget_set_sensitive(dialog->register_button, TRUE);
    } else {
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
    @@ -340,14 +340,14 @@
    set_dialog_icon(dialog, data, len, g_strdup(filename));
    }
    - dialog->icon_filesel = NULL;
    + g_clear_object(&dialog->icon_filesel);
    }
    static void
    icon_select_cb(GtkWidget *button, AccountPrefsDialog *dialog)
    {
    dialog->icon_filesel = pidgin_buddy_icon_chooser_new(GTK_WINDOW(dialog->window), icon_filesel_choose_cb, dialog);
    - gtk_widget_show_all(dialog->icon_filesel);
    + gtk_native_dialog_show(GTK_NATIVE_DIALOG(dialog->icon_filesel));
    }
    static void
    @@ -506,7 +506,7 @@
    username = g_strdup(purple_account_get_username(dialog->account));
    if (!username && dialog->protocol
    - && PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, CLIENT_IFACE, get_account_text_table)) {
    + && PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, CLIENT, get_account_text_table)) {
    GHashTable *table;
    const char *label;
    table = purple_protocol_client_iface_get_account_text_table(dialog->protocol, NULL);
    @@ -1204,7 +1204,7 @@
    add_voice_options(AccountPrefsDialog *dialog)
    {
    #ifdef USE_VV
    - if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, MEDIA_IFACE, initiate_session)) {
    + if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, MEDIA, initiate_session)) {
    if (dialog->voice_frame) {
    gtk_widget_destroy(dialog->voice_frame);
    dialog->voice_frame = NULL;
    @@ -1258,8 +1258,7 @@
    if (dialog->icon_img)
    g_object_unref(dialog->icon_img);
    - if (dialog->icon_filesel)
    - gtk_widget_destroy(dialog->icon_filesel);
    + g_clear_object(&dialog->icon_filesel);
    purple_signals_disconnect_by_handle(dialog);
    @@ -1672,7 +1671,7 @@
    if (dialog->account == NULL)
    gtk_widget_set_sensitive(button, FALSE);
    - if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER_IFACE, register_user))
    + if (!dialog->protocol || !PURPLE_PROTOCOL_IMPLEMENTS(dialog->protocol, SERVER, register_user))
    gtk_widget_hide(button);
    /* Setup the page with 'Advanced' (protocol options). */
    --- a/pidgin/gtkblist-theme.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkblist-theme.c Tue Oct 08 21:48:28 2019 -0500
    @@ -148,8 +148,9 @@
    copy = g_new0(PidginThemeFont, 1);
    copy->font = g_strdup(pair->font);
    strncpy(copy->color, pair->color, sizeof(copy->color) - 1);
    - if (pair->gdkcolor)
    - copy->gdkcolor = pair->gdkcolor ? gdk_rgba_copy(pair->gdkcolor) : NULL;
    + if (pair->gdkcolor) {
    + copy->gdkcolor = gdk_rgba_copy(pair->gdkcolor);
    + }
    return copy;
    }
    --- a/pidgin/gtkblist.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkblist.c Tue Oct 08 21:48:28 2019 -0500
    @@ -59,6 +59,7 @@
    #include "gtkutils.h"
    #include "pidgin/minidialog.h"
    #include "pidgin/pidginabout.h"
    +#include "pidgin/pidginaccountchooser.h"
    #include "pidgin/pidgindebug.h"
    #include "pidgin/pidgindebugplugininfo.h"
    #include "pidgin/pidgingdkpixbuf.h"
    @@ -129,8 +130,8 @@
    } PidginBuddyListPrivate;
    -#define PIDGIN_BUDDY_LIST_GET_PRIVATE(list) \
    - ((PidginBuddyListPrivate *)((list)->priv))
    +G_DEFINE_TYPE_WITH_PRIVATE(PidginBuddyList, pidgin_buddy_list,
    + PURPLE_TYPE_BUDDY_LIST)
    #define PIDGIN_WINDOW_ICONIFIED(x) \
    (gdk_window_get_state(gtk_widget_get_window(GTK_WIDGET(x))) & \
    @@ -203,7 +204,7 @@
    old_state != GDK_VISIBILITY_FULLY_OBSCURED) {
    /* no longer fully obscured */
    - pidgin_blist_refresh_timer(purple_blist_get_buddy_list());
    + pidgin_blist_refresh_timer(purple_blist_get_default());
    }
    /* continue to handle event normally */
    @@ -217,7 +218,7 @@
    purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", FALSE);
    else {
    purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/blist/list_visible", TRUE);
    - pidgin_blist_refresh_timer(purple_blist_get_buddy_list());
    + pidgin_blist_refresh_timer(purple_blist_get_default());
    }
    }
    @@ -231,7 +232,7 @@
    /* Refresh gtkblist if un-iconifying */
    if (event->changed_mask & GDK_WINDOW_STATE_ICONIFIED){
    if (!(event->new_window_state & GDK_WINDOW_STATE_ICONIFIED))
    - pidgin_blist_refresh_timer(purple_blist_get_buddy_list());
    + pidgin_blist_refresh_timer(purple_blist_get_default());
    }
    return FALSE;
    @@ -249,14 +250,14 @@
    }
    static void
    -gtk_blist_hide_cb(GtkWidget *widget, gpointer data)
    +gtk_blist_hide_cb(GtkWidget *widget, PidginBuddyList *gtkblist)
    {
    purple_signal_emit(pidgin_blist_get_handle(),
    "gtkblist-hiding", gtkblist);
    }
    static void
    -gtk_blist_show_cb(GtkWidget *widget, gpointer data)
    +gtk_blist_show_cb(GtkWidget *widget, PidginBuddyList *gtkblist)
    {
    purple_signal_emit(pidgin_blist_get_handle(),
    "gtkblist-unhiding", gtkblist);
    @@ -407,6 +408,7 @@
    gchar *path_str,
    gpointer user_data)
    {
    + PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data);
    GtkTreeIter iter;
    GtkTreePath *path = NULL;
    PurpleBlistNode *node;
    @@ -548,6 +550,7 @@
    static void gtk_blist_renderer_edited_cb(GtkCellRendererText *text_rend, char *arg1,
    char *arg2, PurpleBuddyList *list)
    {
    + PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(list);
    GtkTreeIter iter;
    GtkTreePath *path;
    PurpleBlistNode *node;
    @@ -754,7 +757,7 @@
    {
    purple_blist_node_set_bool(node, "show_offline",
    !purple_blist_node_get_bool(node, "show_offline"));
    - pidgin_blist_update(purple_blist_get_buddy_list(), node);
    + pidgin_blist_update(purple_blist_get_default(), node);
    }
    else if (PURPLE_IS_CONTACT(node))
    {
    @@ -767,7 +770,7 @@
    bnode = purple_blist_node_get_sibling_next(bnode))
    {
    purple_blist_node_set_bool(bnode, "show_offline", setting);
    - pidgin_blist_update(purple_blist_get_buddy_list(), bnode);
    + pidgin_blist_update(purple_blist_get_default(), bnode);
    }
    } else if (PURPLE_IS_GROUP(node)) {
    PurpleBlistNode *cnode, *bnode;
    @@ -784,7 +787,8 @@
    bnode = purple_blist_node_get_sibling_next(bnode))
    {
    purple_blist_node_set_bool(bnode, "show_offline", setting);
    - pidgin_blist_update(purple_blist_get_buddy_list(), bnode);
    + pidgin_blist_update(purple_blist_get_default(),
    + bnode);
    }
    }
    }
    @@ -886,7 +890,7 @@
    gc = purple_account_get_connection(data->rq_data.account);
    protocol = (gc != NULL) ? purple_connection_get_protocol(gc) : NULL;
    - sensitive = (protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, get_list));
    + sensitive = (protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, get_list));
    gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), 1, sensitive);
    }
    @@ -912,7 +916,8 @@
    PidginBlistNode *ui_data = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
    if (ui_data == NULL || ui_data->row == NULL)
    return;
    - pidgin_blist_update_buddy(purple_blist_get_buddy_list(), PURPLE_BLIST_NODE(buddy), TRUE);
    + pidgin_blist_update_buddy(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy), TRUE);
    }
    static gboolean
    @@ -921,7 +926,7 @@
    PurpleConnection *gc = purple_account_get_connection(account);
    PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    - return PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddy);
    + return PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, add_buddy);
    }
    static gboolean
    @@ -935,7 +940,7 @@
    protocol = purple_connection_get_protocol(gc);
    - return (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info));
    + return (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info));
    }
    gboolean
    @@ -972,7 +977,7 @@
    img = gtk_image_new_from_icon_name("dialog-question",
    GTK_ICON_SIZE_DIALOG);
    - gtkblist = PIDGIN_BLIST(purple_blist_get_buddy_list());
    + gtkblist = PIDGIN_BUDDY_LIST(purple_blist_get_default());
    blist_window = gtkblist ? GTK_WINDOW(gtkblist->window) : NULL;
    data->window = gtk_dialog_new();
    @@ -1007,9 +1012,12 @@
    data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    - data->account_menu = pidgin_account_option_menu_new(account, FALSE,
    - callback_func, filter_func, data);
    + data->account_menu = pidgin_account_chooser_new(account, FALSE);
    + pidgin_account_chooser_set_filter_func(
    + PIDGIN_ACCOUNT_CHOOSER(data->account_menu), filter_func);
    pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_ccount"), data->sg, data->account_menu, TRUE, NULL);
    + g_signal_connect(data->account_menu, "changed",
    + G_CALLBACK(callback_func), data);
    data->vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 5));
    gtk_container_set_border_width(GTK_CONTAINER(data->vbox), 0);
    @@ -1102,10 +1110,11 @@
    }
    static void
    -chat_select_account_cb(GObject *w, PurpleAccount *account,
    - PidginChatData *data)
    -{
    - g_return_if_fail(w != NULL);
    +chat_select_account_cb(GObject *w, PidginChatData *data)
    +{
    + PurpleAccount *account =
    + pidgin_account_chooser_get_selected(GTK_WIDGET(w));
    +
    g_return_if_fail(data != NULL);
    g_return_if_fail(account != NULL);
    @@ -1141,7 +1150,8 @@
    gtk_dialog_set_default_response(GTK_DIALOG(data->rq_data.window),
    GTK_RESPONSE_OK);
    data->default_chat_name = NULL;
    - data->rq_data.account = pidgin_account_option_menu_get_selected(data->rq_data.account_menu);
    + data->rq_data.account =
    + pidgin_account_chooser_get_selected(data->rq_data.account_menu);
    rebuild_chat_entries(data, NULL);
    @@ -1150,6 +1160,7 @@
    static void gtk_blist_row_expanded_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data)
    {
    + PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data);
    PurpleBlistNode *node;
    gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1);
    @@ -1172,6 +1183,7 @@
    static void gtk_blist_row_collapsed_cb(GtkTreeView *tv, GtkTreeIter *iter, GtkTreePath *path, gpointer user_data)
    {
    + PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(user_data);
    PurpleBlistNode *node;
    gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), iter, NODE_COLUMN, &node, -1);
    @@ -1207,6 +1219,7 @@
    }
    static void gtk_blist_row_activated_cb(GtkTreeView *tv, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data) {
    + PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(data);
    PurpleBlistNode *node;
    GtkTreeIter iter;
    @@ -1385,7 +1398,7 @@
    else
    purple_account_privacy_allow(account, name);
    - pidgin_blist_update(purple_blist_get_buddy_list(), node);
    + pidgin_blist_update(purple_blist_get_default(), node);
    }
    void pidgin_append_blist_node_privacy_menu(GtkWidget *menu, PurpleBlistNode *node)
    @@ -1409,7 +1422,7 @@
    GList *l, *ll;
    PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    - if(!protocol || !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, blist_node_menu))
    + if(!protocol || !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, blist_node_menu))
    return;
    for(l = ll = purple_protocol_client_iface_blist_node_menu(protocol, node); l; l = l->next) {
    @@ -1447,7 +1460,8 @@
    submenu = gtk_menu_new();
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
    - for (group = purple_blist_get_root(); group; group = purple_blist_node_get_sibling_next(group)) {
    + for (group = purple_blist_get_default_root(); group;
    + group = purple_blist_node_get_sibling_next(group)) {
    if (!PURPLE_IS_GROUP(group))
    continue;
    if (group == purple_blist_node_get_parent(node))
    @@ -1484,7 +1498,7 @@
    contact_expanded = node->contact_expanded;
    }
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) {
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info)) {
    pidgin_new_menu_item(menu, _("Get _Info"), PIDGIN_STOCK_TOOLBAR_USER_INFO,
    G_CALLBACK(gtk_blist_menu_info_cb), buddy);
    }
    @@ -1492,7 +1506,7 @@
    G_CALLBACK(gtk_blist_menu_im_cb), buddy);
    #ifdef USE_VV
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, MEDIA_IFACE, get_caps)) {
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, MEDIA, get_caps)) {
    PurpleAccount *account = purple_buddy_get_account(buddy);
    const gchar *who = purple_buddy_get_name(buddy);
    PurpleMediaCaps caps = purple_protocol_get_media_caps(account, who);
    @@ -1666,15 +1680,21 @@
    purple_buddy_icons_node_set_custom_icon_from_file(node,
    filename);
    }
    + g_object_set_data(G_OBJECT(data), "buddy-icon-chooser", NULL);
    }
    static void
    set_node_custom_icon(GtkWidget *w, PurpleBlistNode *node)
    {
    - /* This doesn't keep track of the returned dialog (so that successive
    - * calls could be made to re-display that dialog). Do we want that? */
    - GtkWidget *win = pidgin_buddy_icon_chooser_new(NULL, set_node_custom_icon_cb, node);
    - gtk_widget_show_all(win);
    + GtkFileChooserNative *win =
    + g_object_get_data(G_OBJECT(node), "buddy-icon-chooser");
    + if (win == NULL) {
    + win = pidgin_buddy_icon_chooser_new(NULL, set_node_custom_icon_cb,
    + node);
    + g_object_set_data_full(G_OBJECT(node), "buddy-icon-chooser", win,
    + g_object_unref);
    + }
    + gtk_native_dialog_show(GTK_NATIVE_DIALOG(win));
    }
    static void
    @@ -1957,7 +1977,7 @@
    protocol = purple_protocols_find(purple_account_get_protocol_id(purple_buddy_get_account(b)));
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info))
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info))
    pidgin_retrieve_user_info(purple_account_get_connection(purple_buddy_get_account(b)), purple_buddy_get_name(b));
    handled = TRUE;
    }
    @@ -2113,8 +2133,7 @@
    }
    }
    - g_list_foreach(list, (GFunc)g_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_free);
    }
    static gboolean
    @@ -3526,7 +3545,7 @@
    PurpleConnection *gc = NULL;
    PurpleProtocol *protocol = NULL;
    PurpleMood *mood;
    - PurpleMood *global_moods = get_global_moods();
    + PurpleMood *global_moods = NULL;
    if (account) {
    PurplePresence *presence = purple_account_get_presence(account);
    @@ -3549,15 +3568,17 @@
    /* TODO: rlaager wants this sorted. */
    /* TODO: darkrain wants it sorted post-translation */
    - if (account && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_moods))
    + if (account && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods)) {
    mood = purple_protocol_client_iface_get_moods(protocol, account);
    - else
    - mood = global_moods;
    + } else {
    + mood = global_moods = get_global_moods();
    + }
    for ( ; mood->mood != NULL ; mood++) {
    char *path;
    - if (mood->mood == NULL || mood->description == NULL)
    + if (mood->description == NULL) {
    continue;
    + }
    path = get_mood_icon_path(mood->mood);
    purple_request_field_list_add_icon(f, _(mood->description),
    @@ -3749,7 +3770,7 @@
    conv = PURPLE_CHAT_CONVERSATION(bnode->conv.conv);
    } else {
    char *chat_name;
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, get_name))
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, get_name))
    chat_name = purple_protocol_chat_iface_get_name(protocol, purple_chat_get_components(chat));
    else
    chat_name = g_strdup(purple_chat_get_name(chat));
    @@ -4296,7 +4317,7 @@
    /* Status Info */
    protocol = purple_protocols_find(purple_account_get_protocol_id(purple_buddy_get_account(b)));
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, status_text) &&
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, status_text) &&
    purple_account_get_connection(purple_buddy_get_account(b))) {
    char *tmp = purple_protocol_client_iface_status_text(protocol, b);
    const char *end;
    @@ -4470,7 +4491,7 @@
    || !gtk_widget_get_visible(gtkblist->window))
    return TRUE;
    - for(gnode = list->root; gnode; gnode = gnode->next) {
    + for (gnode = purple_blist_get_root(list); gnode; gnode = gnode->next) {
    if(!PURPLE_IS_GROUP(gnode))
    continue;
    for(cnode = gnode->child; cnode; cnode = cnode->next) {
    @@ -4561,7 +4582,7 @@
    static void
    sign_on_off_cb(PurpleConnection *gc, PurpleBuddyList *blist)
    {
    - PidginBuddyList *gtkblist = PIDGIN_BLIST(blist);
    + PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(blist);
    update_menu_bar(gtkblist);
    }
    @@ -4735,7 +4756,7 @@
    if (PURPLE_IS_CHAT_CONVERSATION(conv) && (purple_message_get_flags(msg) & PURPLE_MESSAGE_NICK))
    ui->conv.flags |= PIDGIN_BLIST_CHAT_HAS_PENDING_MESSAGE_WITH_NICK;
    - pidgin_blist_update(purple_blist_get_buddy_list(), node);
    + pidgin_blist_update(purple_blist_get_default(), node);
    }
    static void
    @@ -4746,7 +4767,7 @@
    return;
    ui->conv.flags &= ~(PIDGIN_BLIST_NODE_HAS_PENDING_MESSAGE |
    PIDGIN_BLIST_CHAT_HAS_PENDING_MESSAGE_WITH_NICK);
    - pidgin_blist_update(purple_blist_get_buddy_list(), node);
    + pidgin_blist_update(purple_blist_get_default(), node);
    }
    static void
    @@ -4790,66 +4811,12 @@
    }
    }
    -/**************************************************************************
    - * GTK Buddy list GBoxed code
    - **************************************************************************/
    -static PidginBuddyList *
    -pidgin_buddy_list_ref(PidginBuddyList *gtkblist)
    -{
    - PidginBuddyListPrivate *priv;
    -
    - g_return_val_if_fail(gtkblist != NULL, NULL);
    -
    - priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    - priv->box_count++;
    -
    - return gtkblist;
    -}
    -
    -static void
    -pidgin_buddy_list_unref(PidginBuddyList *gtkblist)
    -{
    - PidginBuddyListPrivate *priv;
    -
    - g_return_if_fail(gtkblist != NULL);
    -
    - priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    -
    - g_return_if_fail(priv->box_count >= 0);
    -
    - if (!priv->box_count--)
    - purple_core_quit();
    -}
    -
    -GType
    -pidgin_buddy_list_get_type(void)
    -{
    - static GType type = 0;
    -
    - if (type == 0) {
    - type = g_boxed_type_register_static("PidginBuddyList",
    - (GBoxedCopyFunc)pidgin_buddy_list_ref,
    - (GBoxedFreeFunc)pidgin_buddy_list_unref);
    - }
    -
    - return type;
    -}
    -
    /**********************************************************************************
    * Public API Functions *
    **********************************************************************************/
    -static void pidgin_blist_new_list(PurpleBuddyList *blist)
    -{
    - PidginBuddyList *gtkblist;
    -
    - gtkblist = g_new0(PidginBuddyList, 1);
    - gtkblist->priv = g_new0(PidginBuddyListPrivate, 1);
    -
    - blist->ui_data = gtkblist;
    -}
    -
    -static void pidgin_blist_new_node(PurpleBlistNode *node)
    +static void
    +pidgin_blist_new_node(PurpleBuddyList *list, PurpleBlistNode *node)
    {
    purple_blist_node_set_ui_data(node, g_new0(PidginBlistNode, 1));
    }
    @@ -4906,7 +4873,7 @@
    gtk_tree_model_get(GTK_TREE_MODEL(gtkblist->treemodel), &iter, NODE_COLUMN, &node, -1);
    }
    - redo_buddy_list(purple_blist_get_buddy_list(), FALSE, FALSE);
    + redo_buddy_list(purple_blist_get_default(), FALSE, FALSE);
    gtk_tree_view_columns_autosize(GTK_TREE_VIEW(gtkblist->treeview));
    if (node)
    @@ -4939,7 +4906,7 @@
    GList *list = NULL;
    PidginBuddyListPrivate *priv;
    - priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + priv = pidgin_buddy_list_get_instance_private(gtkblist);
    priv->select_notebook_page_timeout = 0;
    @@ -4959,7 +4926,8 @@
    static void pidgin_blist_select_notebook_page(PidginBuddyList *gtkblist)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    priv->select_notebook_page_timeout = g_timeout_add(0,
    pidgin_blist_select_notebook_page_cb, gtkblist);
    }
    @@ -5092,7 +5060,8 @@
    add_error_dialog(PidginBuddyList *gtkblist,
    GtkWidget *dialog)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    gtk_container_add(GTK_CONTAINER(priv->error_scrollbook), dialog);
    }
    @@ -5209,7 +5178,8 @@
    static void
    remove_generic_error_dialog(PurpleAccount *account)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    remove_child_widget_by_account(
    GTK_CONTAINER(priv->error_scrollbook), account);
    }
    @@ -5219,7 +5189,8 @@
    update_generic_error_message(PurpleAccount *account,
    const char *description)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    GtkWidget *mini_dialog = find_child_widget_by_account(
    GTK_CONTAINER(priv->error_scrollbook), account);
    pidgin_mini_dialog_set_description(PIDGIN_MINI_DIALOG(mini_dialog),
    @@ -5277,7 +5248,8 @@
    static void
    ensure_signed_on_elsewhere_minidialog(PidginBuddyList *gtkblist)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    PidginMiniDialog *mini_dialog;
    if(priv->signed_on_elsewhere)
    @@ -5305,7 +5277,8 @@
    static void
    update_signed_on_elsewhere_minidialog_title(void)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    PidginMiniDialog *mini_dialog = priv->signed_on_elsewhere;
    guint accounts;
    char *title;
    @@ -5359,7 +5332,8 @@
    static void
    add_to_signed_on_elsewhere(PurpleAccount *account)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    PidginMiniDialog *mini_dialog;
    GtkWidget *account_label;
    @@ -5379,7 +5353,8 @@
    static void
    remove_from_signed_on_elsewhere(PurpleAccount *account)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    PidginMiniDialog *mini_dialog = priv->signed_on_elsewhere;
    if(mini_dialog == NULL)
    return;
    @@ -5394,7 +5369,8 @@
    update_signed_on_elsewhere_tooltip(PurpleAccount *account,
    const char *description)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    GtkContainer *c = GTK_CONTAINER(priv->signed_on_elsewhere->contents);
    GtkWidget *label = find_child_widget_by_account(c, account);
    gtk_widget_set_tooltip_text(label, description);
    @@ -5487,7 +5463,7 @@
    gpointer data)
    {
    PurpleBuddyList *list = data;
    - PurpleBlistNode *node = list->root;
    + PurpleBlistNode *node = purple_blist_get_root(list);
    while (node) {
    pidgin_blist_update_group(list, node);
    node = node->next;
    @@ -5621,7 +5597,10 @@
    "cell-background-rgba", BGCOLOR_COLUMN,
    "markup", NAME_COLUMN,
    NULL);
    - g_signal_connect(G_OBJECT(rend), "editing-started", G_CALLBACK(gtk_blist_renderer_editing_started_cb), NULL);
    + g_signal_connect(
    + G_OBJECT(rend), "editing-started",
    + G_CALLBACK(gtk_blist_renderer_editing_started_cb),
    + list);
    g_signal_connect(G_OBJECT(rend), "editing-canceled", G_CALLBACK(gtk_blist_renderer_editing_cancelled_cb), list);
    g_signal_connect(G_OBJECT(rend), "edited", G_CALLBACK(gtk_blist_renderer_edited_cb), list);
    g_object_set(rend, "ypad", 0, "yalign", 0.5, NULL);
    @@ -5746,8 +5725,8 @@
    return;
    }
    - gtkblist = PIDGIN_BLIST(list);
    - priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + gtkblist = PIDGIN_BUDDY_LIST(list);
    + priv = pidgin_buddy_list_get_instance_private(gtkblist);
    if (priv->current_theme)
    g_object_unref(priv->current_theme);
    @@ -5773,9 +5752,9 @@
    g_signal_connect(G_OBJECT(gtkblist->window), "delete_event", G_CALLBACK(gtk_blist_delete_cb), NULL);
    g_signal_connect(G_OBJECT(gtkblist->window), "hide",
    - G_CALLBACK(gtk_blist_hide_cb), NULL);
    + G_CALLBACK(gtk_blist_hide_cb), gtkblist);
    g_signal_connect(G_OBJECT(gtkblist->window), "show",
    - G_CALLBACK(gtk_blist_show_cb), NULL);
    + G_CALLBACK(gtk_blist_show_cb), gtkblist);
    g_signal_connect(G_OBJECT(gtkblist->window), "size-allocate",
    G_CALLBACK(gtk_blist_size_allocate_cb), NULL);
    g_signal_connect(G_OBJECT(gtkblist->window), "visibility_notify_event", G_CALLBACK(gtk_blist_visibility_cb), NULL);
    @@ -5953,9 +5932,12 @@
    gtk_tree_view_append_column(GTK_TREE_VIEW(gtkblist->treeview), gtkblist->text_column);
    pidgin_blist_build_layout(list);
    - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated", G_CALLBACK(gtk_blist_row_activated_cb), NULL);
    - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded", G_CALLBACK(gtk_blist_row_expanded_cb), NULL);
    - g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed", G_CALLBACK(gtk_blist_row_collapsed_cb), NULL);
    + g_signal_connect(G_OBJECT(gtkblist->treeview), "row-activated",
    + G_CALLBACK(gtk_blist_row_activated_cb), gtkblist);
    + g_signal_connect(G_OBJECT(gtkblist->treeview), "row-expanded",
    + G_CALLBACK(gtk_blist_row_expanded_cb), gtkblist);
    + g_signal_connect(G_OBJECT(gtkblist->treeview), "row-collapsed",
    + G_CALLBACK(gtk_blist_row_collapsed_cb), gtkblist);
    g_signal_connect(G_OBJECT(gtkblist->treeview), "button-press-event", G_CALLBACK(gtk_blist_button_press_cb), NULL);
    g_signal_connect(G_OBJECT(gtkblist->treeview), "key-press-event", G_CALLBACK(gtk_blist_key_press_cb), NULL);
    g_signal_connect(G_OBJECT(gtkblist->treeview), "popup-menu", G_CALLBACK(pidgin_blist_popup_menu_cb), NULL);
    @@ -6109,11 +6091,11 @@
    {
    PurpleBlistNode *node;
    - gtkblist = PIDGIN_BLIST(list);
    + gtkblist = PIDGIN_BUDDY_LIST(list);
    if(!gtkblist || !gtkblist->treeview)
    return;
    - node = list->root;
    + node = purple_blist_get_root(list);
    while (node)
    {
    @@ -6145,8 +6127,8 @@
    PurpleBuddyList *blist;
    PidginBuddyList *gtkblist;
    - blist = purple_blist_get_buddy_list();
    - gtkblist = PIDGIN_BLIST(purple_blist_get_buddy_list());
    + blist = purple_blist_get_default();
    + gtkblist = PIDGIN_BUDDY_LIST(blist);
    gtkblist->refresh_timer = g_timeout_add_seconds(30,(GSourceFunc)pidgin_blist_refresh_timer, blist);
    }
    @@ -6274,7 +6256,7 @@
    if(gtknode != NULL) {
    gtk_tree_row_reference_free(gtknode->row);
    } else {
    - pidgin_blist_new_node(node);
    + pidgin_blist_new_node(list, node);
    gtknode = purple_blist_node_get_ui_data(node);
    }
    @@ -6832,12 +6814,12 @@
    static void pidgin_blist_update(PurpleBuddyList *list, PurpleBlistNode *node)
    {
    if (list)
    - gtkblist = PIDGIN_BLIST(list);
    + gtkblist = PIDGIN_BUDDY_LIST(list);
    if(!gtkblist || !gtkblist->treeview || !node)
    return;
    if (purple_blist_node_get_ui_data(node) == NULL)
    - pidgin_blist_new_node(node);
    + pidgin_blist_new_node(list, node);
    if (PURPLE_IS_GROUP(node))
    pidgin_blist_update_group(list, node);
    @@ -6849,51 +6831,6 @@
    pidgin_blist_update_chat(list, node);
    }
    -static void pidgin_blist_destroy(PurpleBuddyList *list)
    -{
    - PidginBuddyListPrivate *priv;
    -
    - if (!list || !list->ui_data)
    - return;
    -
    - g_return_if_fail(list->ui_data == gtkblist);
    -
    - purple_signals_disconnect_by_handle(gtkblist);
    -
    - gtk_widget_destroy(gtkblist->window);
    -
    - pidgin_blist_tooltip_destroy();
    -
    - if (gtkblist->refresh_timer)
    - g_source_remove(gtkblist->refresh_timer);
    - if (gtkblist->timeout)
    - g_source_remove(gtkblist->timeout);
    - if (gtkblist->drag_timeout)
    - g_source_remove(gtkblist->drag_timeout);
    -
    - gtkblist->refresh_timer = 0;
    - gtkblist->timeout = 0;
    - gtkblist->drag_timeout = 0;
    - gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL;
    - g_object_unref(G_OBJECT(gtkblist->treemodel));
    - gtkblist->treemodel = NULL;
    - g_object_unref(G_OBJECT(gtkblist->ui));
    - g_object_unref(G_OBJECT(gtkblist->empty_avatar));
    -
    - priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    -
    - if (priv->current_theme)
    - g_object_unref(priv->current_theme);
    - if (priv->select_notebook_page_timeout)
    - g_source_remove(priv->select_notebook_page_timeout);
    - g_free(priv);
    -
    - g_free(gtkblist);
    - accountmenu = NULL;
    - gtkblist = NULL;
    - purple_prefs_disconnect_by_handle(pidgin_blist_get_handle());
    -}
    -
    static void pidgin_blist_set_visible(PurpleBuddyList *list, gboolean show)
    {
    if (!(gtkblist && gtkblist->window))
    @@ -6922,17 +6859,12 @@
    g_list_free(list);
    list = NULL;
    - if (purple_blist_get_buddy_list()->root == NULL)
    - {
    + gnode = purple_blist_get_default_root();
    + if (gnode == NULL) {
    list = g_list_append(list,
    (gpointer)PURPLE_BLIST_DEFAULT_GROUP_NAME);
    - }
    - else
    - {
    - for (gnode = purple_blist_get_buddy_list()->root;
    - gnode != NULL;
    - gnode = gnode->next)
    - {
    + } else {
    + for (; gnode != NULL; gnode = gnode->next) {
    if (PURPLE_IS_GROUP(gnode))
    {
    g = (PurpleGroup *)gnode;
    @@ -6945,9 +6877,10 @@
    }
    static void
    -add_buddy_select_account_cb(GObject *w, PurpleAccount *account,
    - PidginAddBuddyData *data)
    -{
    +add_buddy_select_account_cb(GObject *w, PidginAddBuddyData *data)
    +{
    + PurpleAccount *account =
    + pidgin_account_chooser_get_selected(GTK_WIDGET(w));
    PurpleConnection *pc = NULL;
    PurpleProtocol *protocol = NULL;
    gboolean invite_enabled = TRUE;
    @@ -7050,15 +6983,17 @@
    }
    static void
    -pidgin_blist_request_add_buddy(PurpleAccount *account, const char *username,
    - const char *group, const char *alias)
    +pidgin_blist_request_add_buddy(PurpleBuddyList *list, PurpleAccount *account,
    + const char *username, const char *group,
    + const char *alias)
    {
    PidginAddBuddyData *data = g_new0(PidginAddBuddyData, 1);
    + PidginBlistRequestData *blist_req_data = &data->rq_data;
    if (account == NULL)
    account = purple_connection_get_account(purple_connections_get_all()->data);
    - make_blist_request_dialog((PidginBlistRequestData *)data,
    + make_blist_request_dialog(blist_req_data,
    account,
    _("Add Buddy"), "add_buddy",
    _("Add a buddy.\n"),
    @@ -7115,7 +7050,8 @@
    gtk_widget_show_all(data->rq_data.window);
    /* Force update of invite message entry sensitivity */
    - add_buddy_select_account_cb(NULL, account, data);
    + pidgin_account_chooser_set_selected(blist_req_data->account_menu,
    + account);
    }
    static void
    @@ -7202,8 +7138,9 @@
    }
    static void
    -pidgin_blist_request_add_chat(PurpleAccount *account, PurpleGroup *group,
    - const char *alias, const char *name)
    +pidgin_blist_request_add_chat(PurpleBuddyList *list, PurpleAccount *account,
    + PurpleGroup *group, const char *alias,
    + const char *name)
    {
    PidginAddChatData *data;
    GList *l;
    @@ -7215,7 +7152,7 @@
    gc = purple_account_get_connection(account);
    protocol = purple_connection_get_protocol(gc);
    - if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join)) {
    + if (!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, join)) {
    purple_notify_error(gc, NULL, _("This protocol does not"
    " support chat rooms."), NULL,
    purple_request_cpar_from_account(account));
    @@ -7227,7 +7164,7 @@
    gc = (PurpleConnection *)l->data;
    protocol = purple_connection_get_protocol(gc);
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join)) {
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, join)) {
    account = purple_connection_get_account(gc);
    break;
    }
    @@ -7297,7 +7234,7 @@
    }
    static void
    -pidgin_blist_request_add_group(void)
    +pidgin_blist_request_add_group(PurpleBuddyList *list)
    {
    purple_request_input(NULL, _("Add Group"), NULL,
    _("Please enter the name of the group to be added."),
    @@ -7381,32 +7318,8 @@
    pidgin_set_urgent(GTK_WINDOW(gtkblist->window), TRUE);
    }
    -static PurpleBlistUiOps blist_ui_ops =
    -{
    - pidgin_blist_new_list,
    - pidgin_blist_new_node,
    - pidgin_blist_show,
    - pidgin_blist_update,
    - pidgin_blist_remove,
    - pidgin_blist_destroy,
    - pidgin_blist_set_visible,
    - pidgin_blist_request_add_buddy,
    - pidgin_blist_request_add_chat,
    - pidgin_blist_request_add_group,
    - NULL,
    - NULL,
    - NULL,
    - NULL, NULL, NULL, NULL
    -};
    -
    -
    -PurpleBlistUiOps *
    -pidgin_blist_get_ui_ops(void)
    -{
    - return &blist_ui_ops;
    -}
    -
    -PidginBuddyList *pidgin_blist_get_default_gtk_blist()
    +PidginBuddyList *
    +pidgin_blist_get_default_gtk_blist(void)
    {
    return gtkblist;
    }
    @@ -7415,8 +7328,8 @@
    {
    PurpleAccount *account = purple_connection_get_account(gc);
    PurpleBlistNode *gnode, *cnode;
    - for(gnode = purple_blist_get_buddy_list()->root; gnode; gnode = gnode->next)
    - {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = gnode->next) {
    if(!PURPLE_IS_GROUP(gnode))
    continue;
    for(cnode = gnode->child; cnode; cnode = cnode->next)
    @@ -7465,7 +7378,8 @@
    PidginBlistNode *gtknode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
    if(!gtknode) {
    - pidgin_blist_new_node(PURPLE_BLIST_NODE(buddy));
    + pidgin_blist_new_node(purple_blist_get_default(),
    + PURPLE_BLIST_NODE(buddy));
    }
    gtknode = purple_blist_node_get_ui_data(PURPLE_BLIST_NODE(buddy));
    @@ -7483,8 +7397,9 @@
    void
    pidgin_blist_set_theme(PidginBlistTheme *theme)
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    - PurpleBuddyList *list = purple_blist_get_buddy_list();
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    + PurpleBuddyList *list = purple_blist_get_default();
    if (theme != NULL)
    purple_prefs_set_string(PIDGIN_PREFS_ROOT "/blist/theme",
    @@ -7506,7 +7421,8 @@
    PidginBlistTheme *
    pidgin_blist_get_theme()
    {
    - PidginBuddyListPrivate *priv = PIDGIN_BUDDY_LIST_GET_PRIVATE(gtkblist);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    return priv->current_theme;
    }
    @@ -7574,6 +7490,77 @@
    purple_signals_unregister_by_instance(pidgin_blist_get_handle());
    purple_signals_disconnect_by_handle(pidgin_blist_get_handle());
    +
    + accountmenu = NULL;
    + gtkblist = NULL;
    +}
    +
    +/**************************************************************************
    + * GTK Buddy list GObject code
    + **************************************************************************/
    +static void
    +pidgin_buddy_list_init(PidginBuddyList *self)
    +{
    +}
    +
    +static void
    +pidgin_buddy_list_finalize(GObject *obj)
    +{
    + PidginBuddyList *gtkblist = PIDGIN_BUDDY_LIST(obj);
    + PidginBuddyListPrivate *priv =
    + pidgin_buddy_list_get_instance_private(gtkblist);
    +
    + purple_signals_disconnect_by_handle(gtkblist);
    +
    + gtk_widget_destroy(gtkblist->window);
    +
    + pidgin_blist_tooltip_destroy();
    +
    + if (gtkblist->refresh_timer) {
    + g_source_remove(gtkblist->refresh_timer);
    + gtkblist->refresh_timer = 0;
    + }
    + if (gtkblist->timeout) {
    + g_source_remove(gtkblist->timeout);
    + gtkblist->timeout = 0;
    + }
    + if (gtkblist->drag_timeout) {
    + g_source_remove(gtkblist->drag_timeout);
    + gtkblist->drag_timeout = 0;
    + }
    +
    + gtkblist->window = gtkblist->vbox = gtkblist->treeview = NULL;
    + g_clear_object(&gtkblist->treemodel);
    + g_object_unref(G_OBJECT(gtkblist->ui));
    + g_object_unref(G_OBJECT(gtkblist->empty_avatar));
    +
    + g_clear_object(&priv->current_theme);
    + if (priv->select_notebook_page_timeout) {
    + g_source_remove(priv->select_notebook_page_timeout);
    + }
    +
    + purple_prefs_disconnect_by_handle(pidgin_blist_get_handle());
    +
    + G_OBJECT_CLASS(pidgin_buddy_list_parent_class)->finalize(obj);
    +}
    +
    +static void
    +pidgin_buddy_list_class_init(PidginBuddyListClass *klass)
    +{
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + PurpleBuddyListClass *purple_blist_class;
    +
    + obj_class->finalize = pidgin_buddy_list_finalize;
    +
    + purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);
    + purple_blist_class->new_node = pidgin_blist_new_node;
    + purple_blist_class->show = pidgin_blist_show;
    + purple_blist_class->update = pidgin_blist_update;
    + purple_blist_class->remove = pidgin_blist_remove;
    + purple_blist_class->set_visible = pidgin_blist_set_visible;
    + purple_blist_class->request_add_buddy = pidgin_blist_request_add_buddy;
    + purple_blist_class->request_add_chat = pidgin_blist_request_add_chat;
    + purple_blist_class->request_add_group = pidgin_blist_request_add_group;
    }
    /*********************************************************************
    @@ -7637,9 +7624,9 @@
    return;
    }
    if (purple_strequal(id, "none")) {
    - redo_buddy_list(purple_blist_get_buddy_list(), TRUE, FALSE);
    + redo_buddy_list(purple_blist_get_default(), TRUE, FALSE);
    } else {
    - redo_buddy_list(purple_blist_get_buddy_list(), FALSE, FALSE);
    + redo_buddy_list(purple_blist_get_default(), FALSE, FALSE);
    }
    }
    @@ -8133,9 +8120,9 @@
    purple_connection_get_protocol(gc) : NULL;
    if (protocol &&
    - (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_moods) ||
    - PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_actions))) {
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_moods) &&
    + (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods) ||
    + PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions))) {
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_moods) &&
    (purple_connection_get_flags(gc) & PURPLE_CONNECTION_FLAG_SUPPORT_MOODS)) {
    if (purple_account_get_status(account, "mood")) {
    @@ -8146,7 +8133,7 @@
    }
    }
    - if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT_IFACE, get_actions)) {
    + if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CLIENT, get_actions)) {
    GtkWidget *menuitem;
    PurpleProtocolAction *action = NULL;
    GList *actions, *l;
    @@ -8242,7 +8229,8 @@
    }
    menuitem = gtk_menu_item_new_with_mnemonic(
    - _(purple_plugin_info_get_name(info)));
    + _(gplugin_plugin_info_get_name(
    + GPLUGIN_PLUGIN_INFO(info))));
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
    gtk_widget_show(menuitem);
    plugin_menu_items = g_slist_prepend(plugin_menu_items,
    --- a/pidgin/gtkblist.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkblist.h Tue Oct 08 21:48:28 2019 -0500
    @@ -106,6 +106,8 @@
    * Like, everything you need to know about the gtk buddy list
    */
    struct _PidginBuddyList {
    + PurpleBuddyList parent;
    +
    GtkWidget *window;
    GtkWidget *notebook;
    GtkWidget *main_vbox;
    @@ -144,14 +146,8 @@
    GtkWidget *statusbox;
    GdkPixbuf *empty_avatar;
    -
    - gpointer priv;
    };
    -#define PIDGIN_BLIST(list) ((PidginBuddyList *)purple_blist_get_ui_data())
    -#define PIDGIN_IS_PIDGIN_BLIST(list) \
    - (purple_blist_get_ui_ops() == pidgin_blist_get_ui_ops())
    -
    G_BEGIN_DECLS
    /**************************************************************************
    @@ -163,7 +159,8 @@
    *
    * Returns: The #GType for the #PidginBuddyList boxed structure.
    */
    -GType pidgin_buddy_list_get_type(void);
    +G_DECLARE_FINAL_TYPE(PidginBuddyList, pidgin_buddy_list, PIDGIN, BUDDY_LIST,
    + PurpleBuddyList)
    /**
    * pidgin_blist_get_handle:
    @@ -189,15 +186,6 @@
    void pidgin_blist_uninit(void);
    /**
    - * pidgin_blist_get_ui_ops:
    - *
    - * Returns the UI operations structure for the buddy list.
    - *
    - * Returns: The GTK+ list operations structure.
    - */
    -PurpleBlistUiOps *pidgin_blist_get_ui_ops(void);
    -
    -/**
    * pidgin_blist_get_default_gtk_blist:
    *
    * Returns the default gtk buddy list
    @@ -206,7 +194,7 @@
    * returns the PidginBuddyList we're most likely wanting to work with. This is slightly
    * cleaner than an externed global.
    *
    - * Returns: The default GTK+ buddy list
    + * Returns: (transfer none): The default GTK+ buddy list.
    */
    PidginBuddyList *pidgin_blist_get_default_gtk_blist(void);
    --- a/pidgin/gtkcellrendererexpander.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkcellrendererexpander.c Tue Oct 08 21:48:28 2019 -0500
    @@ -245,17 +245,10 @@
    width -= xpad*2;
    height -= ypad*2;
    -#if GTK_CHECK_VERSION(3,14,0)
    if (is_expanded)
    state |= GTK_STATE_FLAG_CHECKED;
    else
    state &= ~GTK_STATE_FLAG_CHECKED;
    -#else
    - if (is_expanded)
    - state |= GTK_STATE_FLAG_ACTIVE;
    - else
    - state &= ~GTK_STATE_FLAG_ACTIVE;
    -#endif
    context = gtk_widget_get_style_context(widget);
    gtk_style_context_add_class(context, GTK_STYLE_CLASS_VIEW);
    --- a/pidgin/gtkconv-theme-loader.c Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,306 +0,0 @@
    -/*
    - * PidginConvThemeLoader for Pidgin
    - *
    - * Pidgin is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#include "pidgin.h"
    -#include "gtkconv-theme-loader.h"
    -#include "gtkconv-theme.h"
    -
    -#include "xmlnode.h"
    -#include "debug.h"
    -#include "prefs.h"
    -
    -/**
    - * PidginConvThemeLoader:
    - *
    - * A pidgin conversation theme loader. Extends PurpleThemeLoader (theme-loader.h)
    - * This is a class designed to build conversation themes
    - */
    -struct _PidginConvThemeLoader
    -{
    - PurpleThemeLoader parent;
    -};
    -
    -/*****************************************************************************
    - * Conversation Theme Builder
    - *****************************************************************************/
    -
    -static GHashTable *
    -read_info_plist(PurpleXmlNode *plist)
    -{
    - GHashTable *info;
    - PurpleXmlNode *key, *value;
    - gboolean fail = FALSE;
    -
    - info = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    -
    - for (key = purple_xmlnode_get_child(plist, "dict/key");
    - key;
    - key = purple_xmlnode_get_next_twin(key)) {
    - char *keyname;
    - GValue *val;
    -
    - ;
    - for (value = key->next; value && value->type != PURPLE_XMLNODE_TYPE_TAG; value = value->next)
    - ;
    - if (!value) {
    - fail = TRUE;
    - break;
    - }
    -
    - val = g_new0(GValue, 1);
    - if (g_str_equal(value->name, "string")) {
    - g_value_init(val, G_TYPE_STRING);
    - g_value_take_string(val, purple_xmlnode_get_data_unescaped(value));
    -
    - } else if (g_str_equal(value->name, "true")) {
    - g_value_init(val, G_TYPE_BOOLEAN);
    - g_value_set_boolean(val, TRUE);
    -
    - } else if (g_str_equal(value->name, "false")) {
    - g_value_init(val, G_TYPE_BOOLEAN);
    - g_value_set_boolean(val, FALSE);
    -
    - } else if (g_str_equal(value->name, "real")) {
    - char *temp = purple_xmlnode_get_data_unescaped(value);
    - g_value_init(val, G_TYPE_FLOAT);
    - g_value_set_float(val, atof(temp));
    - g_free(temp);
    -
    - } else if (g_str_equal(value->name, "integer")) {
    - char *temp = purple_xmlnode_get_data_unescaped(value);
    - g_value_init(val, G_TYPE_INT);
    - g_value_set_int(val, atoi(temp));
    - g_free(temp);
    -
    - } else {
    - /* NOTE: We don't support array, data, date, or dict as values,
    - since they don't seem to be needed for styles. */
    - g_free(val);
    - fail = TRUE;
    - break;
    - }
    -
    - keyname = purple_xmlnode_get_data_unescaped(key);
    - g_hash_table_insert(info, keyname, val);
    - }
    -
    - if (fail) {
    - g_hash_table_destroy(info);
    - info = NULL;
    - }
    -
    - return info;
    -}
    -
    -static gboolean
    -pidgin_conv_loader_probe(const gchar *dir)
    -{
    - gboolean result;
    - gchar *plist_file;
    -
    - plist_file = g_build_filename(dir, "Contents", "Info.plist", NULL);
    - result = g_file_test(plist_file, G_FILE_TEST_IS_REGULAR);
    - g_free(plist_file);
    -
    - return result;
    -}
    -
    -static PurpleTheme *
    -pidgin_conv_loader_build(const gchar *dir)
    -{
    - PidginConvTheme *theme = NULL;
    - char *contents;
    - PurpleXmlNode *plist;
    - GHashTable *info;
    - GValue *val;
    - int MessageViewVersion;
    - const char *CFBundleName;
    - const char *CFBundleIdentifier;
    - GDir *variants;
    - char *variant_dir;
    -
    - g_return_val_if_fail(dir != NULL, NULL);
    -
    - /* Load Info.plist for theme information */
    - contents = g_build_filename(dir, "Contents", NULL);
    - plist = purple_xmlnode_from_file(contents, "Info.plist", "Info.plist", "gtkconv-theme-loader");
    - g_free(contents);
    - if (plist == NULL) {
    - purple_debug_error("gtkconv-theme-loader",
    - "Failed to load Contents/Info.plist in %s\n", dir);
    - return NULL;
    - }
    -
    - info = read_info_plist(plist);
    - purple_xmlnode_free(plist);
    - if (info == NULL) {
    - purple_debug_error("gtkconv-theme-loader",
    - "Failed to load Contents/Info.plist in %s\n", dir);
    - return NULL;
    - }
    -
    - /* Check for required keys: CFBundleName */
    - val = g_hash_table_lookup(info, "CFBundleName");
    - if (!val || !G_VALUE_HOLDS_STRING(val)) {
    - purple_debug_error("gtkconv-theme-loader",
    - "%s/Contents/Info.plist missing required string key CFBundleName.\n",
    - dir);
    - g_hash_table_destroy(info);
    - return NULL;
    - }
    - CFBundleName = g_value_get_string(val);
    -
    - /* Check for required keys: CFBundleIdentifier */
    - val = g_hash_table_lookup(info, "CFBundleIdentifier");
    - if (!val || !G_VALUE_HOLDS_STRING(val)) {
    - purple_debug_error("gtkconv-theme-loader",
    - "%s/Contents/Info.plist missing required string key CFBundleIdentifier.\n",
    - dir);
    - g_hash_table_destroy(info);
    - return NULL;
    - }
    - CFBundleIdentifier = g_value_get_string(val);
    -
    - /* Check for required keys: MessageViewVersion */
    - val = g_hash_table_lookup(info, "MessageViewVersion");
    - if (!val || !G_VALUE_HOLDS_INT(val)) {
    - purple_debug_error("gtkconv-theme-loader",
    - "%s/Contents/Info.plist missing required integer key MessageViewVersion.\n",
    - dir);
    - g_hash_table_destroy(info);
    - return NULL;
    - }
    -
    - MessageViewVersion = g_value_get_int(val);
    - if (MessageViewVersion < 3) {
    - purple_debug_error("gtkconv-theme-loader",
    - "%s is a legacy style (version %d) and will not be loaded.\n",
    - CFBundleName, MessageViewVersion);
    - g_hash_table_destroy(info);
    - return NULL;
    - }
    -
    - theme = g_object_new(PIDGIN_TYPE_CONV_THEME,
    - "type", "conversation",
    - "name", CFBundleName,
    - "directory", dir,
    - "info", info, NULL);
    -
    - /* Read list of variants */
    - variant_dir = g_build_filename(dir, "Contents", "Resources", "Variants", NULL);
    - variants = g_dir_open(variant_dir, 0, NULL);
    - g_free(variant_dir);
    -
    - if (variants) {
    - char *prefname;
    - const char *default_variant = NULL;
    - const char *file;
    -
    - /* Make sure prefs exist */
    - prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s",
    - CFBundleIdentifier);
    - purple_prefs_add_none(prefname);
    - g_free(prefname);
    -
    - /* Try user-set variant */
    - prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant",
    - CFBundleIdentifier);
    - if (purple_prefs_exists(prefname))
    - default_variant = purple_prefs_get_string(prefname);
    - g_free(prefname);
    -
    - if (default_variant && *default_variant) {
    - pidgin_conversation_theme_set_variant(theme, default_variant);
    -
    - } else {
    - /* Try theme default */
    - val = g_hash_table_lookup(info, "DefaultVariant");
    - if (val && G_VALUE_HOLDS_STRING(val)) {
    - default_variant = g_value_get_string(val);
    - if (default_variant && *default_variant)
    - pidgin_conversation_theme_set_variant(theme, default_variant);
    - else
    - default_variant = NULL;
    - } else
    - default_variant = NULL;
    - }
    -
    - while ((file = g_dir_read_name(variants)) != NULL) {
    - const char *end = g_strrstr(file, ".css");
    - char *name;
    -
    - if ((end == NULL) || (*(end + 4) != '\0'))
    - continue;
    -
    - name = g_strndup(file, end - file);
    - pidgin_conversation_theme_add_variant(theme, name);
    -
    - /* Set variant with first found */
    - if (!default_variant) {
    - pidgin_conversation_theme_set_variant(theme, name);
    - default_variant = name;
    - }
    - }
    -
    - g_dir_close(variants);
    - }
    -
    - return PURPLE_THEME(theme);
    -}
    -
    -/******************************************************************************
    - * GObject Stuff
    - *****************************************************************************/
    -
    -static void
    -pidgin_conv_theme_loader_class_init(PidginConvThemeLoaderClass *klass)
    -{
    - PurpleThemeLoaderClass *loader_klass = PURPLE_THEME_LOADER_CLASS(klass);
    -
    - loader_klass->purple_theme_loader_build = pidgin_conv_loader_build;
    - loader_klass->probe_directory = pidgin_conv_loader_probe;
    -}
    -
    -
    -GType
    -pidgin_conversation_theme_loader_get_type(void)
    -{
    - static GType type = 0;
    - if (type == 0) {
    - static const GTypeInfo info = {
    - sizeof(PidginConvThemeLoaderClass),
    - NULL, /* base_init */
    - NULL, /* base_finalize */
    - (GClassInitFunc)pidgin_conv_theme_loader_class_init, /* class_init */
    - NULL, /* class_finalize */
    - NULL, /* class_data */
    - sizeof (PidginConvThemeLoader),
    - 0, /* n_preallocs */
    - NULL, /* instance_init */
    - NULL, /* value table */
    - };
    - type = g_type_register_static(PURPLE_TYPE_THEME_LOADER,
    - "PidginConvThemeLoader", &info, 0);
    - }
    - return type;
    -}
    -
    --- a/pidgin/gtkconv-theme-loader.h Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,53 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#ifndef PIDGIN_CONV_THEME_LOADER_H
    -#define PIDGIN_CONV_THEME_LOADER_H
    -/**
    - * SECTION:gtkconv-theme-loader
    - * @section_id: pidgin-gtkconv-theme-loader
    - * @short_description: <filename>gtkconv-theme-loader.h</filename>
    - * @title: Conversation Theme Loader Class
    - */
    -
    -#include <glib.h>
    -#include <glib-object.h>
    -#include "theme-loader.h"
    -
    -#define PIDGIN_TYPE_CONV_THEME_LOADER pidgin_conversation_theme_loader_get_type()
    -
    -/**************************************************************************/
    -/* Pidgin Conversation Theme-Loader API */
    -/**************************************************************************/
    -G_BEGIN_DECLS
    -
    -/**
    - * pidgin_conversation_theme_loader_get_type:
    - *
    - * Returns: The #GType for a conversation theme loader.
    - */
    -G_DECLARE_FINAL_TYPE(PidginConvThemeLoader, pidgin_conversation_theme_loader,
    - PIDGIN, CONV_THEME_LOADER, PurpleThemeLoader)
    -
    -G_END_DECLS
    -
    -#endif /* PIDGIN_CONV_THEME_LOADER_H */
    -
    --- a/pidgin/gtkconv-theme.c Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,766 +0,0 @@
    -/*
    - * Conversation Themes for Pidgin
    - *
    - * Pidgin is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#include "internal.h"
    -#include "glibcompat.h"
    -
    -#include "gtkconv-theme.h"
    -
    -#include "conversation.h"
    -#include "debug.h"
    -#include "prefs.h"
    -#include "xmlnode.h"
    -
    -#include "pidgin.h"
    -#include "gtkconv.h"
    -
    -#include <stdlib.h>
    -#include <string.h>
    -
    -/******************************************************************************
    - * Structs
    - *****************************************************************************/
    -
    -/**
    - * PidginConvTheme:
    - *
    - * extends PurpleTheme (theme.h)
    - * A pidgin icon theme.
    - * This object represents a Pidgin icon theme.
    - *
    - * PidginConvTheme is a PurpleTheme Object.
    - */
    -struct _PidginConvTheme
    -{
    - PurpleTheme parent;
    -};
    -
    -typedef struct {
    - /* current config options */
    - char *variant; /* allowed to be NULL if there are no variants */
    - GList *variants;
    -
    - /* Info.plist keys/values */
    - GHashTable *info;
    -
    - /* caches */
    - char *template_html;
    - char *header_html;
    - char *footer_html;
    - char *topic_html;
    - char *status_html;
    - char *content_html;
    - char *incoming_content_html;
    - char *outgoing_content_html;
    - char *incoming_next_content_html;
    - char *outgoing_next_content_html;
    - char *incoming_context_html;
    - char *outgoing_context_html;
    - char *incoming_next_context_html;
    - char *outgoing_next_context_html;
    - char *basestyle_css;
    -
    - GArray *nick_colors;
    -} PidginConvThemePrivate;
    -
    -/******************************************************************************
    - * Enums
    - *****************************************************************************/
    -
    -enum {
    - PROP_ZERO = 0,
    - PROP_INFO,
    - PROP_VARIANT,
    - PROP_LAST
    -};
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -
    -static GParamSpec *properties[PROP_LAST];
    -
    -G_DEFINE_TYPE_WITH_PRIVATE(PidginConvTheme, pidgin_conversation_theme,
    - PURPLE_TYPE_THEME);
    -
    -/******************************************************************************
    - * Helper Functions
    - *****************************************************************************/
    -
    -static const GValue *
    -get_key(PidginConvThemePrivate *priv, const char *key, gboolean specific)
    -{
    - GValue *val = NULL;
    -
    - /* Try variant-specific key */
    - if (specific && priv->variant) {
    - char *name = g_strdup_printf("%s:%s", key, priv->variant);
    - val = g_hash_table_lookup(priv->info, name);
    - g_free(name);
    - }
    -
    - /* Try generic key */
    - if (!val) {
    - val = g_hash_table_lookup(priv->info, key);
    - }
    -
    - return val;
    -}
    -
    -/* The template path can either come from the theme, or can
    - * be stock Template.html that comes with Pidgin */
    -static char *
    -get_template_path(const char *dir)
    -{
    - char *file;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Template.html", NULL);
    -
    - if (!g_file_test(file, G_FILE_TEST_EXISTS)) {
    - g_free(file);
    - file = g_build_filename(PURPLE_DATADIR,
    - "pidgin", "theme", "Template.html", NULL);
    - }
    -
    - return file;
    -}
    -
    -static const char *
    -get_template_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->template_html)
    - return priv->template_html;
    -
    - file = get_template_path(dir);
    -
    - if (!g_file_get_contents(file, &priv->template_html, NULL, NULL)) {
    - purple_debug_error("webkit", "Could not locate a Template.html (%s)\n", file);
    - priv->template_html = g_strdup("");
    - }
    - g_free(file);
    -
    - return priv->template_html;
    -}
    -
    -static const char *
    -get_basestyle_css(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->basestyle_css)
    - return priv->basestyle_css;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "main.css", NULL);
    - if (!g_file_get_contents(file, &priv->basestyle_css, NULL, NULL))
    - priv->basestyle_css = g_strdup("");
    - g_free(file);
    -
    - return priv->basestyle_css;
    -}
    -
    -static const char *
    -get_header_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->header_html)
    - return priv->header_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Header.html", NULL);
    - if (!g_file_get_contents(file, &priv->header_html, NULL, NULL))
    - priv->header_html = g_strdup("");
    - g_free(file);
    -
    - return priv->header_html;
    -}
    -
    -static const char *
    -get_footer_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->footer_html)
    - return priv->footer_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Footer.html", NULL);
    - if (!g_file_get_contents(file, &priv->footer_html, NULL, NULL))
    - priv->footer_html = g_strdup("");
    - g_free(file);
    -
    - return priv->footer_html;
    -}
    -
    -static const char *
    -get_topic_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->topic_html)
    - return priv->topic_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Topic.html", NULL);
    - if (!g_file_get_contents(file, &priv->topic_html, NULL, NULL)) {
    - purple_debug_info("webkit", "%s could not find Resources/Topic.html\n", dir);
    - priv->topic_html = g_strdup("");
    - }
    - g_free(file);
    -
    - return priv->topic_html;
    -}
    -
    -static const char *
    -get_content_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->content_html)
    - return priv->content_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Content.html", NULL);
    - if (!g_file_get_contents(file, &priv->content_html, NULL, NULL)) {
    - purple_debug_info("webkit", "%s did not have a Content.html\n", dir);
    - priv->content_html = g_strdup("");
    - }
    - g_free(file);
    -
    - return priv->content_html;
    -}
    -
    -static const char *
    -get_status_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->status_html)
    - return priv->status_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Status.html", NULL);
    - if (!g_file_get_contents(file, &priv->status_html, NULL, NULL)) {
    - purple_debug_info("webkit", "%s could not find Resources/Status.html\n", dir);
    - priv->status_html = g_strdup(get_content_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->status_html;
    -}
    -
    -static const char *
    -get_incoming_content_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->incoming_content_html)
    - return priv->incoming_content_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Content.html", NULL);
    - if (!g_file_get_contents(file, &priv->incoming_content_html, NULL, NULL)) {
    - purple_debug_info("webkit", "%s did not have a Incoming/Content.html\n", dir);
    - priv->incoming_content_html = g_strdup(get_content_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->incoming_content_html;
    -}
    -
    -static const char *
    -get_incoming_next_content_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->incoming_next_content_html)
    - return priv->incoming_next_content_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContent.html", NULL);
    - if (!g_file_get_contents(file, &priv->incoming_next_content_html, NULL, NULL)) {
    - priv->incoming_next_content_html = g_strdup(get_incoming_content_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->incoming_next_content_html;
    -}
    -
    -static const char *
    -get_incoming_context_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->incoming_context_html)
    - return priv->incoming_context_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Incoming", "Context.html", NULL);
    - if (!g_file_get_contents(file, &priv->incoming_context_html, NULL, NULL)) {
    - purple_debug_info("webkit", "%s did not have a Incoming/Context.html\n", dir);
    - priv->incoming_context_html = g_strdup(get_incoming_content_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->incoming_context_html;
    -}
    -
    -static const char *
    -get_incoming_next_context_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->incoming_next_context_html)
    - return priv->incoming_next_context_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Incoming", "NextContext.html", NULL);
    - if (!g_file_get_contents(file, &priv->incoming_next_context_html, NULL, NULL)) {
    - priv->incoming_next_context_html = g_strdup(get_incoming_context_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->incoming_next_context_html;
    -}
    -
    -static const char *
    -get_outgoing_content_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->outgoing_content_html)
    - return priv->outgoing_content_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Content.html", NULL);
    - if (!g_file_get_contents(file, &priv->outgoing_content_html, NULL, NULL)) {
    - priv->outgoing_content_html = g_strdup(get_incoming_content_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->outgoing_content_html;
    -}
    -
    -static const char *
    -get_outgoing_next_content_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->outgoing_next_content_html)
    - return priv->outgoing_next_content_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContent.html", NULL);
    - if (!g_file_get_contents(file, &priv->outgoing_next_content_html, NULL, NULL)) {
    - priv->outgoing_next_content_html = g_strdup(get_outgoing_content_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->outgoing_next_content_html;
    -}
    -
    -static const char *
    -get_outgoing_context_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->outgoing_context_html)
    - return priv->outgoing_context_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "Context.html", NULL);
    - if (!g_file_get_contents(file, &priv->outgoing_context_html, NULL, NULL)) {
    - priv->outgoing_context_html = g_strdup(get_incoming_context_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->outgoing_context_html;
    -}
    -
    -static const char *
    -get_outgoing_next_context_html(PidginConvThemePrivate *priv, const char *dir)
    -{
    - char *file;
    -
    - if (priv->outgoing_next_context_html)
    - return priv->outgoing_next_context_html;
    -
    - file = g_build_filename(dir, "Contents", "Resources", "Outgoing", "NextContext.html", NULL);
    - if (!g_file_get_contents(file, &priv->outgoing_next_context_html, NULL, NULL)) {
    - priv->outgoing_next_context_html = g_strdup(get_outgoing_context_html(priv, dir));
    - }
    - g_free(file);
    -
    - return priv->outgoing_next_context_html;
    -}
    -
    -/******************************************************************************
    - * GObject Stuff
    - *****************************************************************************/
    -
    -static void
    -pidgin_conv_theme_get_property(GObject *obj, guint param_id, GValue *value,
    - GParamSpec *psec)
    -{
    - PidginConvTheme *theme = PIDGIN_CONV_THEME(obj);
    -
    - switch (param_id) {
    - case PROP_INFO:
    - g_value_set_boxed(value, (gpointer)pidgin_conversation_theme_get_info(theme));
    - break;
    -
    - case PROP_VARIANT:
    - g_value_set_string(value, pidgin_conversation_theme_get_variant(theme));
    - break;
    -
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
    - break;
    - }
    -}
    -
    -static void
    -pidgin_conv_theme_set_property(GObject *obj, guint param_id, const GValue *value,
    - GParamSpec *psec)
    -{
    - PidginConvTheme *theme = PIDGIN_CONV_THEME(obj);
    -
    - switch (param_id) {
    - case PROP_INFO:
    - pidgin_conversation_theme_set_info(theme, g_value_get_boxed(value));
    - break;
    -
    - case PROP_VARIANT:
    - pidgin_conversation_theme_set_variant(theme, g_value_get_string(value));
    - break;
    -
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, psec);
    - break;
    - }
    -}
    -
    -static void
    -pidgin_conversation_theme_init(PidginConvTheme *theme)
    -{
    -}
    -
    -static void
    -pidgin_conv_theme_finalize(GObject *obj)
    -{
    - PidginConvThemePrivate *priv;
    - GList *list;
    -
    - priv = pidgin_conversation_theme_get_instance_private(
    - PIDGIN_CONV_THEME(obj));
    -
    - g_free(priv->template_html);
    - g_free(priv->header_html);
    - g_free(priv->footer_html);
    - g_free(priv->topic_html);
    - g_free(priv->status_html);
    - g_free(priv->content_html);
    - g_free(priv->incoming_content_html);
    - g_free(priv->outgoing_content_html);
    - g_free(priv->incoming_next_content_html);
    - g_free(priv->outgoing_next_content_html);
    - g_free(priv->incoming_context_html);
    - g_free(priv->outgoing_context_html);
    - g_free(priv->incoming_next_context_html);
    - g_free(priv->outgoing_next_context_html);
    - g_free(priv->basestyle_css);
    -
    - if (priv->info)
    - g_hash_table_destroy(priv->info);
    -
    - list = priv->variants;
    - while (list) {
    - g_free(list->data);
    - list = g_list_delete_link(list, list);
    - }
    - g_free(priv->variant);
    -
    - if (priv->nick_colors)
    - g_array_unref(priv->nick_colors);
    -
    - G_OBJECT_CLASS(pidgin_conversation_theme_parent_class)->finalize(obj);
    -}
    -
    -static void
    -pidgin_conversation_theme_class_init(PidginConvThemeClass *klass)
    -{
    - GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    -
    - obj_class->get_property = pidgin_conv_theme_get_property;
    - obj_class->set_property = pidgin_conv_theme_set_property;
    - obj_class->finalize = pidgin_conv_theme_finalize;
    -
    - /* INFO */
    - properties[PROP_INFO] = g_param_spec_boxed("info", "Info",
    - "The information about this theme",
    - G_TYPE_HASH_TABLE,
    - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
    -
    - /* VARIANT */
    - properties[PROP_VARIANT] = g_param_spec_string("variant", "Variant",
    - "The current variant for this theme",
    - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
    -
    - g_object_class_install_properties(obj_class, PROP_LAST, properties);
    -}
    -
    -/*****************************************************************************
    - * Public API functions
    - *****************************************************************************/
    -
    -const GHashTable *
    -pidgin_conversation_theme_get_info(PidginConvTheme *theme)
    -{
    - PidginConvThemePrivate *priv;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    - return priv->info;
    -}
    -
    -void
    -pidgin_conversation_theme_set_info(PidginConvTheme *theme, GHashTable *info)
    -{
    - PidginConvThemePrivate *priv;
    -
    - g_return_if_fail(theme != NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - if (priv->info)
    - g_hash_table_destroy(priv->info);
    -
    - priv->info = info;
    -
    - g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_INFO]);
    -}
    -
    -const GValue *
    -pidgin_conversation_theme_lookup(PidginConvTheme *theme, const char *key, gboolean specific)
    -{
    - PidginConvThemePrivate *priv;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - return get_key(priv, key, specific);
    -}
    -
    -const char *
    -pidgin_conversation_theme_get_template(PidginConvTheme *theme, PidginConvThemeTemplateType type)
    -{
    - PidginConvThemePrivate *priv;
    - const char *dir;
    - const char *html;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    - dir = purple_theme_get_dir(PURPLE_THEME(theme));
    -
    - switch (type) {
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN:
    - html = get_template_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER:
    - html = get_header_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER:
    - html = get_footer_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_TOPIC:
    - html = get_topic_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS:
    - html = get_status_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_CONTENT:
    - html = get_content_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT:
    - html = get_incoming_content_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT:
    - html = get_incoming_next_content_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTEXT:
    - html = get_incoming_context_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTEXT:
    - html = get_incoming_next_context_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT:
    - html = get_outgoing_content_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT:
    - html = get_outgoing_next_content_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTEXT:
    - html = get_outgoing_context_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTEXT:
    - html = get_outgoing_next_context_html(priv, dir);
    - break;
    - case PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS:
    - html = get_basestyle_css(priv, dir);
    - break;
    - default:
    - purple_debug_error("gtkconv-theme",
    - "Requested invalid template type (%d) for theme %s.\n",
    - type, purple_theme_get_name(PURPLE_THEME(theme)));
    - html = NULL;
    - }
    -
    - return html;
    -}
    -
    -void
    -pidgin_conversation_theme_add_variant(PidginConvTheme *theme, char *variant)
    -{
    - PidginConvThemePrivate *priv;
    -
    - g_return_if_fail(theme != NULL);
    - g_return_if_fail(variant != NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - priv->variants = g_list_prepend(priv->variants, variant);
    -}
    -
    -const char *
    -pidgin_conversation_theme_get_variant(PidginConvTheme *theme)
    -{
    - PidginConvThemePrivate *priv;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - return priv->variant;
    -}
    -
    -void
    -pidgin_conversation_theme_set_variant(PidginConvTheme *theme, const char *variant)
    -{
    - PidginConvThemePrivate *priv;
    - const GValue *val;
    - char *prefname;
    -
    - g_return_if_fail(theme != NULL);
    - g_return_if_fail(variant != NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - g_free(priv->variant);
    - priv->variant = g_strdup(variant);
    -
    - val = get_key(priv, "CFBundleIdentifier", FALSE);
    - prefname = g_strdup_printf(PIDGIN_PREFS_ROOT "/conversations/themes/%s/variant",
    - g_value_get_string(val));
    - purple_prefs_set_string(prefname, variant);
    - g_free(prefname);
    -
    - g_object_notify_by_pspec(G_OBJECT(theme), properties[PROP_VARIANT]);
    -}
    -
    -const GList *
    -pidgin_conversation_theme_get_variants(PidginConvTheme *theme)
    -{
    - PidginConvThemePrivate *priv;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - return priv->variants;
    -}
    -
    -char *
    -pidgin_conversation_theme_get_template_path(PidginConvTheme *theme)
    -{
    - const char *dir;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - dir = purple_theme_get_dir(PURPLE_THEME(theme));
    -
    - return get_template_path(dir);
    -}
    -
    -char *
    -pidgin_conversation_theme_get_css_path(PidginConvTheme *theme)
    -{
    - PidginConvThemePrivate *priv;
    - const char *dir;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - dir = purple_theme_get_dir(PURPLE_THEME(theme));
    - if (!priv->variant) {
    - return g_build_filename(dir, "Contents", "Resources", "main.css", NULL);
    - } else {
    - char *file = g_strdup_printf("%s.css", priv->variant);
    - char *ret = g_build_filename(dir, "Contents", "Resources", "Variants", file, NULL);
    - g_free(file);
    - return ret;
    - }
    -}
    -
    -GArray *
    -pidgin_conversation_theme_get_nick_colors(PidginConvTheme *theme)
    -{
    - PidginConvThemePrivate *priv;
    - const char *dir;
    -
    - g_return_val_if_fail(theme != NULL, NULL);
    -
    - priv = pidgin_conversation_theme_get_instance_private(theme);
    -
    - dir = purple_theme_get_dir(PURPLE_THEME(theme));
    - if (NULL == priv->nick_colors)
    - {
    - char *file = g_build_filename(dir, "Contents", "Resources", "Incoming", "SenderColors.txt", NULL);
    - char *contents;
    - priv->nick_colors = g_array_new(FALSE, FALSE, sizeof(GdkRGBA));
    - if (g_file_get_contents(file, &contents, NULL, NULL)) {
    - int i;
    - gchar ** color_strings = g_strsplit_set(contents, "\r\n:", -1);
    -
    - for(i=0; color_strings[i]; i++)
    - {
    - GdkRGBA color;
    - if (gdk_rgba_parse(&color, color_strings[i])) {
    - g_array_append_val(priv->nick_colors, color);
    - }
    - }
    -
    - g_strfreev(color_strings);
    - g_free(contents);
    - }
    - g_free(file);
    - }
    -
    - if(priv->nick_colors->len)
    - return g_array_ref(priv->nick_colors);
    - else
    - return NULL;
    -}
    -
    --- a/pidgin/gtkconv-theme.h Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,194 +0,0 @@
    -/* pidgin
    - *
    - * Pidgin is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#ifndef PIDGIN_CONV_THEME_H
    -#define PIDGIN_CONV_THEME_H
    -/**
    - * SECTION:gtkconv-theme
    - * @section_id: pidgin-gtkconv-theme
    - * @short_description: <filename>gtkconv-theme.h</filename>
    - * @title: Conversation Theme Class
    - */
    -
    -#include <glib.h>
    -#include <glib-object.h>
    -#include "conversation.h"
    -#include "theme.h"
    -
    -#define PIDGIN_TYPE_CONV_THEME pidgin_conversation_theme_get_type()
    -
    -typedef enum {
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_TOPIC,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_CONTENT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTEXT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTEXT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTEXT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTEXT,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS
    -
    -} PidginConvThemeTemplateType;
    -
    -/**************************************************************************/
    -/* Pidgin Conversation Theme API */
    -/**************************************************************************/
    -G_BEGIN_DECLS
    -
    -/**
    - * pidgin_conversation_theme_get_type:
    - *
    - * Returns: The #GType for a conversation theme.
    - */
    -G_DECLARE_FINAL_TYPE(PidginConvTheme, pidgin_conversation_theme, PIDGIN,
    - CONV_THEME, PurpleTheme)
    -
    -/**
    - * pidgin_conversation_theme_get_info:
    - * @theme: The conversation theme
    - *
    - * Get the Info.plist hash table from a conversation theme.
    - *
    - * Returns: The hash table. Keys are strings as outlined for message styles,
    - * values are GValue*s. This is an internal structure. Take a ref if
    - * necessary, but don't destroy it yourself.
    - */
    -const GHashTable *pidgin_conversation_theme_get_info(PidginConvTheme *theme);
    -
    -/**
    - * pidgin_conversation_theme_set_info:
    - * @theme: The conversation theme
    - * @info: The new hash table. The theme will take ownership of this hash
    - * table. Do not use it yourself afterwards with holding a ref.
    - * For key and value specifications, see pidgin_conversation_theme_get_info().
    - *
    - * Set the Info.plist hash table for a conversation theme.
    - */
    -void pidgin_conversation_theme_set_info(PidginConvTheme *theme, GHashTable *info);
    -
    -/**
    - * pidgin_conversation_theme_lookup:
    - * @theme: The conversation theme
    - * @key: The key to find
    - * @specific: Whether to search variant-specific keys
    - *
    - * Lookup a key in a theme
    - *
    - * Returns: The key information. If @specific is %TRUE, then keys are first
    - * searched by variant, then by general ones. Otherwise, only general
    - * key values are returned.
    - */
    -const GValue *pidgin_conversation_theme_lookup(PidginConvTheme *theme, const char *key, gboolean specific);
    -
    -/**
    - * pidgin_conversation_theme_get_template:
    - * @theme: The conversation theme
    - * @type: The type of template data
    - *
    - * Get the template data from a conversation theme.
    - *
    - * Returns: The template data requested. Fallback is made as required by styles.
    - * Subsequent calls to this function will return cached values.
    - */
    -const char *pidgin_conversation_theme_get_template(PidginConvTheme *theme, PidginConvThemeTemplateType type);
    -
    -/**
    - * pidgin_conversation_theme_add_variant:
    - * @theme: The conversation theme
    - * @variant: The name of the variant
    - *
    - * Add an available variant name to a conversation theme.
    - *
    - * Note: The conversation theme will take ownership of the variant name string.
    - * This function should normally only be called by the theme loader.
    - */
    -void pidgin_conversation_theme_add_variant(PidginConvTheme *theme, char *variant);
    -
    -/**
    - * pidgin_conversation_theme_get_variant:
    - * @theme: The conversation theme
    - *
    - * Get the currently set variant name for a conversation theme.
    - *
    - * Returns: The current variant name.
    - */
    -const char *pidgin_conversation_theme_get_variant(PidginConvTheme *theme);
    -
    -/**
    - * pidgin_conversation_theme_set_variant:
    - * @theme: The conversation theme
    - * @variant: The name of the variant
    - *
    - * Set the variant name for a conversation theme.
    - */
    -void pidgin_conversation_theme_set_variant(PidginConvTheme *theme, const char *variant);
    -
    -/**
    - * pidgin_conversation_theme_get_variants:
    - * @theme: The conversation theme
    - *
    - * Get a list of available variants for a conversation theme.
    - *
    - * Returns: (element-type utf8): The list of variants. This GList and the string data are owned by
    - * the theme and should not be freed by the caller.
    - */
    -const GList *pidgin_conversation_theme_get_variants(PidginConvTheme *theme);
    -
    -/**
    - * pidgin_conversation_theme_get_template_path:
    - * @theme: The conversation theme
    - *
    - * Get the path to the template HTML file.
    - *
    - * Returns: The path to the HTML file.
    - */
    -char *pidgin_conversation_theme_get_template_path(PidginConvTheme *theme);
    -
    -/**
    - * pidgin_conversation_theme_get_css_path:
    - * @theme: The conversation theme
    - *
    - * Get the path to the current variant CSS file.
    - *
    - * Returns: The path to the CSS file.
    - */
    -char *pidgin_conversation_theme_get_css_path(PidginConvTheme *theme);
    -
    -/**
    - * pidgin_conversation_theme_get_nick_colors:
    - * @theme: The conversation theme
    - *
    - * Get (and reference) the array of nick colors
    - *
    - * Returns: (transfer container) (element-type GdkRGBA): Pointer to GArray of nick colors, or NULL if no colors in theme
    - */
    -GArray *pidgin_conversation_theme_get_nick_colors(PidginConvTheme *theme);
    -
    -G_END_DECLS
    -
    -#endif /* PIDGIN_CONV_THEME_H */
    -
    --- a/pidgin/gtkconv.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkconv.c Tue Oct 08 21:48:28 2019 -0500
    @@ -19,7 +19,6 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    *
    */
    -#define _PIDGIN_GTKCONV_C_
    #include "internal.h"
    #include "pidgin.h"
    @@ -30,6 +29,8 @@
    #include <gdk/gdkkeysyms.h>
    +#include <talkatu.h>
    +
    #include "account.h"
    #include "attention.h"
    #include "action.h"
    @@ -45,8 +46,6 @@
    #include "protocol.h"
    #include "request.h"
    #include "smiley-parser.h"
    -#include "theme-loader.h"
    -#include "theme-manager.h"
    #include "util.h"
    #include "version.h"
    @@ -55,8 +54,6 @@
    #include "gtkblist.h"
    #include "gtkconv.h"
    #include "gtkconvwin.h"
    -#include "gtkconv-theme.h"
    -#include "gtkconv-theme-loader.h"
    #include "gtkdialogs.h"
    #include "gtkmenutray.h"
    #include "gtkpounce.h"
    @@ -64,10 +61,10 @@
    #include "gtkprivacy.h"
    #include "gtkstyle.h"
    #include "gtkutils.h"
    -#include "gtkwebview.h"
    #include "pidgingdkpixbuf.h"
    #include "pidgininvitedialog.h"
    #include "pidginlog.h"
    +#include "pidginmessage.h"
    #include "pidginstock.h"
    #include "pidgintooltip.h"
    @@ -154,14 +151,6 @@
    #define PIDGIN_DRAG_BLIST_NODE (1337)
    #define PIDGIN_DRAG_IM_CONTACT (31337)
    -static const GtkTargetEntry dnd_targets[] =
    -{
    - {"PURPLE_BLIST_NODE", GTK_TARGET_SAME_APP, PIDGIN_DRAG_BLIST_NODE},
    - {"application/x-im-contact", 0, PIDGIN_DRAG_IM_CONTACT}
    -};
    -
    -static GtkTargetList *webkit_dnd_targets = NULL;
    -
    static GtkWidget *invite_dialog = NULL;
    static GtkWidget *warn_close_dialog = NULL;
    @@ -177,18 +166,14 @@
    static GHashTable *protocol_lists = NULL;
    static GHashTable *e2ee_stock = NULL;
    -static PurpleTheme *default_conv_theme = NULL;
    -
    static gboolean update_send_to_selection(PidginConvWindow *win);
    static void generate_send_to_items(PidginConvWindow *win);
    /* Prototypes. <-- because Paco-Paco hates this comment. */
    -static void load_conv_theme(PidginConversation *gtkconv);
    static gboolean infopane_entry_activate(PidginConversation *gtkconv);
    static void got_typing_keypress(PidginConversation *gtkconv, gboolean first);
    static void gray_stuff_out(PidginConversation *gtkconv);
    static void add_chat_user_common(PurpleChatConversation *chat, PurpleChatUser *cb, const char *old_name);
    -static gboolean tab_complete(PurpleConversation *conv);
    static void pidgin_conv_updated(PurpleConversation *conv, PurpleConversationUpdateType type);
    static void conv_set_unseen(PurpleConversation *gtkconv, PidginUnseenState state);
    static void gtkconv_set_unseen(PidginConversation *gtkconv, PidginUnseenState state);
    @@ -251,8 +236,7 @@
    {
    PidginConversation *gtkconv = data;
    GList *list = g_list_copy(gtkconv->convs);
    - g_list_foreach(list, (GFunc)g_object_unref, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_object_unref);
    return FALSE;
    }
    @@ -293,36 +277,6 @@
    return FALSE;
    }
    -static void
    -default_formatize(PidginConversation *c)
    -{
    - PurpleConversation *conv = c->active_conv;
    - pidgin_webview_setup_entry(PIDGIN_WEBVIEW(c->entry), purple_conversation_get_features(conv));
    -}
    -
    -static void
    -conversation_entry_clear(PidginConversation *gtkconv)
    -{
    - PidginWebView *webview = PIDGIN_WEBVIEW(gtkconv->entry);
    -
    - //XXX: hotfix for not focused entry after sending a message
    - //pidgin_webview_load_html_string(webview, "");
    - pidgin_webview_load_html_string_with_selection(webview, "<div id='caret'></div>");
    -
    -#if 0
    - /* TODO WebKit */
    - gtk_source_undo_manager_begin_not_undoable_action(webview->undo_manager);
    - pidgin_webview_clear(webview);
    - gtk_source_undo_manager_end_not_undoable_action(webview->undo_manager);
    -#endif
    -}
    -
    -static void
    -clear_formatting_cb(PidginWebView *webview, PidginConversation *gtkconv)
    -{
    - default_formatize(gtkconv);
    -}
    -
    static const char *
    pidgin_get_cmd_prefix(void)
    {
    @@ -369,8 +323,12 @@
    const GList *plugins = purple_plugins_get_loaded();
    if (plugins) {
    for (; plugins; plugins = plugins->next) {
    - PurplePluginInfo *info = purple_plugin_get_info(PURPLE_PLUGIN(plugins->data));
    - str = g_string_append(str, purple_plugin_info_get_name(info));
    + GPluginPluginInfo *info = GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(
    + PURPLE_PLUGIN(plugins->data)));
    + str = g_string_append(
    + str,
    + gplugin_plugin_info_get_name(info));
    if (plugins->next)
    str = g_string_append(str, ", ");
    @@ -429,7 +387,6 @@
    PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    if (PIDGIN_CONVERSATION(conv)) {
    - load_conv_theme(gtkconv);
    gtkconv->last_flags = 0;
    }
    }
    @@ -508,20 +465,25 @@
    check_for_and_do_command(PurpleConversation *conv)
    {
    PidginConversation *gtkconv;
    - char *cmd;
    - const char *prefix;
    + GtkWidget *view = NULL;
    + GtkTextBuffer *buffer = NULL;
    + gchar *cmd;
    + const gchar *prefix;
    gboolean retval = FALSE;
    gtkconv = PIDGIN_CONVERSATION(conv);
    prefix = pidgin_get_cmd_prefix();
    - cmd = pidgin_webview_get_body_text(PIDGIN_WEBVIEW(gtkconv->entry));
    + view = talkatu_editor_get_view(TALKATU_EDITOR(gtkconv->editor));
    + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
    +
    + cmd = talkatu_buffer_get_plain_text(TALKATU_BUFFER(buffer));
    if (cmd && purple_str_has_prefix(cmd, prefix)) {
    PurpleCmdStatus status;
    char *error, *cmdline, *markup, *send_history;
    - send_history = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(gtkconv->entry));
    + send_history = talkatu_markup_get_html(buffer, NULL);
    send_history_add(gtkconv, send_history);
    g_free(send_history);
    @@ -534,8 +496,11 @@
    return TRUE;
    }
    - /* TODO WebKit: Cut out prefix for markup... */
    - markup = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(gtkconv->entry));
    + /* Docs are unclear on whether or not prefix should be removed from
    + * the markup so, ignoring for now. Notably if the markup is
    + * `<b>/foo arg1</b>` we now have to move the bold tag around?
    + * - gk 20190709 */
    + markup = talkatu_markup_get_html(buffer, NULL);
    status = purple_cmd_do_command(conv, cmdline, markup, &error);
    g_free(markup);
    @@ -612,66 +577,46 @@
    {
    PurpleConversation *conv = gtkconv->active_conv;
    PurpleAccount *account;
    -#if 0
    - PurpleConnection *gc;
    -#endif
    PurpleMessageFlags flags = 0;
    - char *buf;
    + GtkTextBuffer *buffer = NULL;
    + gchar *content;
    account = purple_conversation_get_account(conv);
    + buffer = talkatu_editor_get_buffer(TALKATU_EDITOR(gtkconv->editor));
    +
    if (check_for_and_do_command(conv)) {
    - conversation_entry_clear(gtkconv);
    + talkatu_buffer_clear(TALKATU_BUFFER(buffer));
    return;
    }
    if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
    - purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv)))
    + purple_chat_conversation_has_left(PURPLE_CHAT_CONVERSATION(conv))) {
    return;
    -
    - if (!purple_account_is_connected(account))
    + }
    +
    + if (!purple_account_is_connected(account)) {
    return;
    -
    - if (pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->entry)))
    + }
    +
    + content = talkatu_markup_get_html(buffer, NULL);
    + if (purple_strequal(content, "")) {
    + g_free(content);
    return;
    -
    - buf = pidgin_webview_get_body_html(PIDGIN_WEBVIEW(gtkconv->entry));
    - g_return_if_fail(buf != NULL);
    -
    - gtk_widget_grab_focus(gtkconv->entry);
    + }
    purple_idle_touch();
    /* XXX: is there a better way to tell if the message has images? */
    - if (strstr(buf, "<img ") != NULL)
    - flags |= PURPLE_MESSAGE_IMAGES;
    -
    -#if 0
    - gc = purple_account_get_connection(account);
    - if (gc && (purple_conversation_get_features(conv) & PURPLE_CONNECTION_FLAG_NO_NEWLINES)) {
    - /* TODO WebKit */
    - char **bufs;
    - int i;
    -
    - bufs = pidgin_webview_get_markup_lines(PIDGIN_WEBVIEW(gtkconv->entry));
    - for (i = 0; bufs[i]; i++) {
    - send_history_add(gtkconv, bufs[i]);
    - purple_conversation_send_with_flags(conv, bufs[i], flags);
    - }
    -
    - g_strfreev(bufs);
    - } else
    -#endif
    - {
    - send_history_add(gtkconv, buf);
    - purple_conversation_send_with_flags(conv, buf, flags);
    - }
    -
    - g_free(buf);
    -
    - conversation_entry_clear(gtkconv);
    + // if (strstr(buf, "<img ") != NULL)
    + // flags |= PURPLE_MESSAGE_IMAGES;
    +
    + purple_conversation_send_with_flags(conv, content, flags);
    +
    + g_free(content);
    +
    + talkatu_buffer_clear(TALKATU_BUFFER(buffer));
    gtkconv_set_unseen(gtkconv, PIDGIN_UNSEEN_NONE);
    - gtk_widget_grab_focus(gtkconv->entry); // XXX: doesn't work
    }
    static void
    @@ -701,8 +646,6 @@
    else if (account != NULL && purple_account_is_connected(account))
    purple_blist_request_add_chat(account, NULL, NULL, name);
    }
    -
    - gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry);
    }
    static void chat_do_info(PidginConversation *gtkconv, const char *who)
    @@ -724,7 +667,6 @@
    if (PURPLE_IS_IM_CONVERSATION(conv)) {
    pidgin_retrieve_user_info(purple_conversation_get_connection(conv),
    purple_conversation_get_name(conv));
    - gtk_widget_grab_focus(gtkconv->entry);
    } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
    /* Get info of the person currently selected in the GtkTreeView */
    PidginChatPane *gtkchat;
    @@ -758,8 +700,6 @@
    if (account != NULL && purple_account_is_connected(account))
    pidgin_request_add_block(account, purple_conversation_get_name(conv));
    -
    - gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry);
    }
    static void
    @@ -772,8 +712,6 @@
    if (account != NULL && purple_account_is_connected(account))
    pidgin_request_add_permit(account, purple_conversation_get_name(conv));
    -
    - gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry);
    }
    static void
    @@ -830,7 +768,8 @@
    savelog_writefile_cb(void *user_data, const char *filename)
    {
    PurpleConversation *conv = (PurpleConversation *)user_data;
    - PidginWebView *webview;
    + PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    + GtkTextBuffer *buffer = NULL;
    FILE *fp;
    const char *name;
    gchar *text;
    @@ -842,21 +781,18 @@
    return;
    }
    - webview = PIDGIN_WEBVIEW(PIDGIN_CONVERSATION(conv)->webview);
    name = purple_conversation_get_name(conv);
    +
    fprintf(fp, "<html>\n");
    -
    fprintf(fp, "<head>\n");
    fprintf(fp, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n");
    fprintf(fp, "<title>%s</title>\n", name);
    - text = pidgin_webview_get_head_html(webview);
    - fprintf(fp, "%s", text);
    - g_free(text);
    fprintf(fp, "</head>\n");
    fprintf(fp, "<body>\n");
    fprintf(fp, _("<h1>Conversation with %s</h1>\n"), name);
    - text = pidgin_webview_get_body_html(webview);
    + buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->history));
    + text = talkatu_markup_get_html(buffer, NULL);
    fprintf(fp, "%s", text);
    g_free(text);
    fprintf(fp, "\n</body>\n");
    @@ -1026,32 +962,6 @@
    }
    static void
    -menu_insert_link_cb(GtkAction *action, gpointer data)
    -{
    - PidginConvWindow *win = data;
    - PidginConversation *gtkconv;
    - PidginWebView *entry;
    -
    - gtkconv = pidgin_conv_window_get_active_gtkconv(win);
    - entry = PIDGIN_WEBVIEW(gtkconv->entry);
    -
    - pidgin_webview_activate_toolbar(entry, PIDGIN_WEBVIEW_ACTION_LINK);
    -}
    -
    -static void
    -menu_insert_image_cb(GtkAction *action, gpointer data)
    -{
    - PidginConvWindow *win = data;
    - PidginConversation *gtkconv;
    - PidginWebView *entry;
    -
    - gtkconv = pidgin_conv_window_get_active_gtkconv(win);
    - entry = PIDGIN_WEBVIEW(gtkconv->entry);
    -
    - pidgin_webview_activate_toolbar(entry, PIDGIN_WEBVIEW_ACTION_IMAGE);
    -}
    -
    -static void
    menu_alias_cb(GtkAction *action, gpointer data)
    {
    PidginConvWindow *win = data;
    @@ -1157,13 +1067,8 @@
    timer = g_timeout_add_seconds(CLOSE_CONV_TIMEOUT_SECS, close_already, conv);
    g_object_set_data(G_OBJECT(conv), "close-timer", GINT_TO_POINTER(timer));
    }
    -#if 0
    - /* I will miss you */
    - purple_conversation_set_ui_ops(conv, NULL);
    -#else
    pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv);
    pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
    -#endif
    }
    }
    @@ -1364,53 +1269,6 @@
    gtk_widget_grab_focus(PIDGIN_CONVERSATION(conv)->entry);
    }
    -static char *
    -get_class_for_user(const char *who)
    -{
    - return g_strconcat("-pidgin-user:", who, NULL);
    -}
    -
    -static WebKitDOMNode *
    -get_mark_for_user(PidginConversation *gtkconv, const char *who)
    -{
    - WebKitDOMDocument *doc;
    - WebKitDOMNodeList *nodes;
    - WebKitDOMNode *node = NULL;
    - gulong len;
    - char *tmp;
    -
    - doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(gtkconv->webview));
    -
    - tmp = get_class_for_user(who);
    - nodes = webkit_dom_document_get_elements_by_class_name(doc, tmp);
    - g_free(tmp);
    -
    - if (nodes != NULL) {
    - len = webkit_dom_node_list_get_length(nodes);
    - if (len > 0)
    - node = webkit_dom_node_list_item(nodes, len - 1);
    - }
    -
    - g_object_unref(nodes);
    -
    - return node;
    -}
    -
    -static void
    -menu_last_said_cb(GtkWidget *w, PidginConversation *gtkconv)
    -{
    - WebKitDOMNode *node;
    - const char *who;
    -
    - who = g_object_get_data(G_OBJECT(w), "user_data");
    - node = get_mark_for_user(gtkconv, who);
    -
    - if (node != NULL)
    - webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(node), TRUE);
    - else
    - g_return_if_reached();
    -}
    -
    static GtkWidget *
    create_chat_menu(PurpleChatConversation *chat, const char *who, PurpleConnection *gc)
    {
    @@ -1457,9 +1315,9 @@
    PIDGIN_STOCK_TOOLBAR_SEND_FILE, G_CALLBACK(menu_chat_send_file_cb),
    PIDGIN_CONVERSATION(conv));
    - if (gc == NULL || protocol == NULL)
    + if (gc == NULL) {
    can_receive_file = FALSE;
    - else {
    + } else {
    gchar *real_who = NULL;
    real_who = purple_protocol_chat_iface_get_user_real_name(protocol, gc,
    purple_chat_conversation_get_id(chat), who);
    @@ -1495,7 +1353,7 @@
    g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
    }
    - if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)) {
    + if (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info)) {
    button = pidgin_new_menu_item(menu, _("Info"),
    PIDGIN_STOCK_TOOLBAR_USER_INFO,
    G_CALLBACK(menu_chat_info_cb),
    @@ -1507,7 +1365,7 @@
    g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
    }
    - if (!is_me && protocol && !(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME) && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddy)) {
    + if (!is_me && protocol && !(purple_protocol_get_options(protocol) & OPT_PROTO_UNIQUE_CHATNAME) && PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, add_buddy)) {
    if ((buddy = purple_blist_find_buddy(account, who)) != NULL)
    button = pidgin_new_menu_item(menu, _("Remove"),
    GTK_STOCK_REMOVE,
    @@ -1525,12 +1383,6 @@
    g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
    }
    - button = pidgin_new_menu_item(menu, _("Last Said"), GTK_STOCK_INDEX,
    - G_CALLBACK(menu_last_said_cb), PIDGIN_CONVERSATION(conv));
    - g_object_set_data_full(G_OBJECT(button), "user_data", g_strdup(who), g_free);
    - if (!get_mark_for_user(PIDGIN_CONVERSATION(conv), who))
    - gtk_widget_set_sensitive(button, FALSE);
    -
    if (buddy != NULL)
    {
    if (purple_account_is_connected(account))
    @@ -1624,13 +1476,6 @@
    if (event->button == GDK_BUTTON_PRIMARY && event->type == GDK_2BUTTON_PRESS) {
    chat_do_im(gtkconv, who);
    - } else if (event->button == GDK_BUTTON_MIDDLE && event->type == GDK_BUTTON_PRESS) {
    - /* Move to user's anchor */
    - WebKitDOMNode *node = get_mark_for_user(gtkconv, who);
    -
    - if (node != NULL)
    - webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(node), TRUE);
    -
    } else if (gdk_event_triggers_context_menu((GdkEvent *)event)) {
    GtkWidget *menu = create_chat_menu (PURPLE_CHAT_CONVERSATION(conv), who, gc);
    gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
    @@ -1706,8 +1551,8 @@
    GtkWidget *from;
    GtkWidget *to;
    } transitions[] = {
    - {gtkconv->entry, gtkconv->webview},
    - {gtkconv->webview, chat ? gtkconv->u.chat->list : gtkconv->entry},
    + {gtkconv->entry, gtkconv->history},
    + {gtkconv->history, chat ? gtkconv->u.chat->list : gtkconv->entry},
    {chat ? gtkconv->u.chat->list : NULL, gtkconv->entry},
    {NULL, NULL}
    }, *ptr;
    @@ -1733,11 +1578,13 @@
    static void
    update_typing_inserting(PidginConversation *gtkconv)
    {
    - gboolean is_empty;
    + GtkTextBuffer *buffer = NULL;
    + gboolean is_empty = FALSE;
    g_return_if_fail(gtkconv != NULL);
    - is_empty = pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->entry));
    + buffer = talkatu_editor_get_buffer(TALKATU_EDITOR(gtkconv->editor));
    + is_empty = talkatu_buffer_get_is_empty(TALKATU_BUFFER(buffer));
    got_typing_keypress(gtkconv, is_empty);
    }
    @@ -1746,17 +1593,18 @@
    update_typing_deleting_cb(PidginConversation *gtkconv)
    {
    PurpleIMConversation *im = PURPLE_IM_CONVERSATION(gtkconv->active_conv);
    - gboolean is_empty = pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->entry));
    -
    - if (!is_empty) {
    + GtkTextBuffer *buffer = NULL;
    +
    + buffer = talkatu_editor_get_buffer(TALKATU_EDITOR(gtkconv->editor));
    +
    + if (!talkatu_buffer_get_is_empty(TALKATU_BUFFER(buffer))) {
    /* We deleted all the text, so turn off typing. */
    purple_im_conversation_stop_send_typed_timeout(im);
    purple_serv_send_typing(purple_conversation_get_connection(gtkconv->active_conv),
    purple_conversation_get_name(gtkconv->active_conv),
    PURPLE_IM_NOT_TYPING);
    - }
    - else {
    + } else {
    /* We're deleting, but not all of it, so it counts as typing. */
    got_typing_keypress(gtkconv, FALSE);
    }
    @@ -1767,14 +1615,15 @@
    static void
    update_typing_deleting(PidginConversation *gtkconv)
    {
    - gboolean is_empty;
    + GtkTextBuffer *buffer = NULL;
    g_return_if_fail(gtkconv != NULL);
    - is_empty = pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->entry));
    -
    - if (!is_empty)
    + buffer = talkatu_editor_get_buffer(TALKATU_EDITOR(gtkconv->editor));
    +
    + if (!talkatu_buffer_get_is_empty(TALKATU_BUFFER(buffer))) {
    g_timeout_add(0, (GSourceFunc)update_typing_deleting_cb, gtkconv);
    + }
    }
    static gboolean
    @@ -1890,108 +1739,9 @@
    /* If CTRL was held down... */
    if (event->state & GDK_CONTROL_MASK) {
    - switch (event->keyval) {
    - case GDK_KEY_Up:
    - if (!gtkconv->send_history)
    - break;
    -
    - if (gtkconv->entry != entry)
    - break;
    -
    - if (!gtkconv->send_history->prev) {
    - g_free(gtkconv->send_history->data);
    -
    - gtkconv->send_history->data =
    - pidgin_webview_get_body_html(PIDGIN_WEBVIEW(gtkconv->entry));
    - }
    -
    - if (gtkconv->send_history->next && gtkconv->send_history->next->data) {
    - GObject *object;
    -#if 0
    - /* TODO WebKit: maybe not necessary? */
    - GtkTextIter iter;
    - GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
    -#endif
    -
    - gtkconv->send_history = gtkconv->send_history->next;
    -
    - /* Block the signal to prevent application of default formatting. */
    - object = g_object_ref(G_OBJECT(gtkconv->entry));
    - g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
    - 0, 0, NULL, NULL, gtkconv);
    - /* Clear the formatting. */
    - pidgin_webview_clear_formatting(PIDGIN_WEBVIEW(gtkconv->entry));
    - /* Unblock the signal. */
    - g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
    - 0, 0, NULL, NULL, gtkconv);
    - g_object_unref(object);
    -
    - pidgin_webview_load_html_string(PIDGIN_WEBVIEW(gtkconv->entry),
    - gtkconv->send_history->data);
    - /* this is mainly just a hack so the formatting at the
    - * cursor gets picked up. */
    -#if 0
    - /* TODO WebKit: maybe not necessary? */
    - gtk_text_buffer_get_end_iter(buffer, &iter);
    - gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter);
    -#endif
    - }
    -
    - return TRUE;
    - break;
    -
    - case GDK_KEY_Down:
    - if (!gtkconv->send_history)
    - break;
    -
    - if (gtkconv->entry != entry)
    - break;
    -
    - if (gtkconv->send_history->prev && gtkconv->send_history->prev->data) {
    - GObject *object;
    -#if 0
    - /* TODO WebKit: maybe not necessary? */
    - GtkTextIter iter;
    - GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
    -#endif
    -
    - gtkconv->send_history = gtkconv->send_history->prev;
    -
    - /* Block the signal to prevent application of default formatting. */
    - object = g_object_ref(G_OBJECT(gtkconv->entry));
    - g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
    - 0, 0, NULL, NULL, gtkconv);
    - /* Clear the formatting. */
    - pidgin_webview_clear_formatting(PIDGIN_WEBVIEW(gtkconv->entry));
    - /* Unblock the signal. */
    - g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
    - 0, 0, NULL, NULL, gtkconv);
    - g_object_unref(object);
    -
    - pidgin_webview_load_html_string(PIDGIN_WEBVIEW(gtkconv->entry),
    - gtkconv->send_history->data);
    - /* this is mainly just a hack so the formatting at the
    - * cursor gets picked up. */
    - if (*(char *)gtkconv->send_history->data) {
    -#if 0
    - /* TODO WebKit: maybe not necessary? */
    - gtk_text_buffer_get_end_iter(buffer, &iter);
    - gtk_text_buffer_move_mark_by_name(buffer, "insert", &iter);
    -#endif
    - } else {
    - /* Restore the default formatting */
    - default_formatize(gtkconv);
    - }
    - }
    -
    - return TRUE;
    - break;
    - } /* End of switch */
    - }
    -
    + }
    /* If ALT (or whatever) was held down... */
    else if (event->state & GDK_MOD1_MASK) {
    -
    }
    /* If neither CTRL nor ALT were held down... */
    @@ -2007,19 +1757,19 @@
    plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
    pidgin_conversations_get_handle(), "chat-nick-autocomplete",
    conv, event->state & GDK_SHIFT_MASK));
    - return plugin_return ? TRUE : tab_complete(conv);
    + return plugin_return;
    }
    break;
    case GDK_KEY_Page_Up:
    case GDK_KEY_KP_Page_Up:
    - pidgin_webview_page_up(PIDGIN_WEBVIEW(gtkconv->webview));
    + talkatu_history_page_up(TALKATU_HISTORY(gtkconv->history));
    return TRUE;
    break;
    case GDK_KEY_Page_Down:
    case GDK_KEY_KP_Page_Down:
    - pidgin_webview_page_down(PIDGIN_WEBVIEW(gtkconv->webview));
    + talkatu_history_page_down(TALKATU_HISTORY(gtkconv->history));
    return TRUE;
    break;
    @@ -2050,27 +1800,6 @@
    }
    /*
    - * NOTE:
    - * This guy just kills a single right click from being propagated any
    - * further. I have no idea *why* we need this, but we do ... It
    - * prevents right clicks on the GtkTextView in a convo dialog from
    - * going all the way down to the notebook. I suspect a bug in
    - * GtkTextView, but I'm not ready to point any fingers yet.
    - */
    -static gboolean
    -entry_stop_rclick_cb(GtkWidget *widget, GdkEventButton *event, gpointer data)
    -{
    - if (event->button == GDK_BUTTON_SECONDARY && event->type == GDK_BUTTON_PRESS) {
    - /* Right single click */
    - g_signal_stop_emission_by_name(G_OBJECT(widget), "button_press_event");
    -
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -/*
    * If someone tries to type into the conversation backlog of a
    * conversation window then we yank focus from the conversation backlog
    * and give it to the text entry box so that people can type
    @@ -2079,6 +1808,7 @@
    static gboolean
    refocus_entry_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
    {
    + GtkWidget *view = NULL;
    PidginConversation *gtkconv = data;
    /* If we have a valid key for the conversation display, then exit */
    @@ -2110,8 +1840,9 @@
    return FALSE;
    }
    - gtk_widget_grab_focus(gtkconv->entry);
    - gtk_widget_event(gtkconv->entry, (GdkEvent *)event);
    + view = talkatu_editor_get_view(TALKATU_EDITOR(gtkconv->editor));
    + gtk_widget_grab_focus(view);
    + gtk_widget_event(view, (GdkEvent *)event);
    return TRUE;
    }
    @@ -2124,7 +1855,6 @@
    {
    PidginConversation *gtkconv;
    PurpleConversation *old_conv;
    - PidginWebView *entry;
    PurpleConnectionFlags features;
    g_return_if_fail(conv != NULL);
    @@ -2141,102 +1871,14 @@
    purple_conversation_close_logs(old_conv);
    gtkconv->active_conv = conv;
    - pidgin_webview_switch_active_conversation(
    - PIDGIN_WEBVIEW(gtkconv->entry), conv);
    - pidgin_webview_switch_active_conversation(
    - PIDGIN_WEBVIEW(gtkconv->webview), conv);
    purple_conversation_set_logging(conv,
    gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(gtkconv->win->menu->logging)));
    - entry = PIDGIN_WEBVIEW(gtkconv->entry);
    -
    - features = purple_conversation_get_features(conv);
    - if (!(features & PURPLE_CONNECTION_FLAG_HTML))
    - pidgin_webview_clear_formatting(PIDGIN_WEBVIEW(gtkconv->entry));
    - else if (features & PURPLE_CONNECTION_FLAG_FORMATTING_WBFO &&
    - !(purple_conversation_get_features(old_conv) & PURPLE_CONNECTION_FLAG_FORMATTING_WBFO))
    - {
    - /* The old conversation allowed formatting on parts of the
    - * buffer, but the new one only allows it on the whole
    - * buffer. This code saves the formatting from the current
    - * position of the cursor, clears the formatting, then
    - * applies the saved formatting to the entire buffer. */
    -
    - gboolean bold;
    - gboolean italic;
    - gboolean underline;
    - gboolean strike;
    - char *fontface = pidgin_webview_get_current_fontface(entry);
    - char *forecolor = pidgin_webview_get_current_forecolor(entry);
    - char *backcolor = pidgin_webview_get_current_backcolor(entry);
    -#if 0
    - /* TODO WebKit: Do we need this again? */
    - char *background = pidgin_webview_get_current_background(entry);
    -#endif
    - gint fontsize = pidgin_webview_get_current_fontsize(entry);
    - gboolean bold2;
    - gboolean italic2;
    - gboolean underline2;
    - gboolean strike2;
    -
    - pidgin_webview_get_current_format(entry, &bold, &italic, &underline, &strike);
    -
    - /* Clear existing formatting */
    - pidgin_webview_clear_formatting(entry);
    -
    - /* Apply saved formatting to the whole buffer. */
    -
    - pidgin_webview_get_current_format(entry, &bold2, &italic2, &underline2, &strike2);
    -
    - if (bold != bold2)
    - pidgin_webview_toggle_bold(entry);
    -
    - if (italic != italic2)
    - pidgin_webview_toggle_italic(entry);
    -
    - if (underline != underline2)
    - pidgin_webview_toggle_underline(entry);
    -
    - if (strike != strike2)
    - pidgin_webview_toggle_strike(entry);
    -
    - pidgin_webview_toggle_fontface(entry, fontface);
    -
    - if (!(features & PURPLE_CONNECTION_FLAG_NO_FONTSIZE))
    - pidgin_webview_font_set_size(entry, fontsize);
    -
    - pidgin_webview_toggle_forecolor(entry, forecolor);
    -
    - if (!(features & PURPLE_CONNECTION_FLAG_NO_BGCOLOR))
    - {
    - pidgin_webview_toggle_backcolor(entry, backcolor);
    -#if 0
    - pidgin_webview_toggle_background(entry, background);
    -#endif
    - }
    -
    - g_free(fontface);
    - g_free(forecolor);
    - g_free(backcolor);
    -#if 0
    - g_free(background);
    -#endif
    - }
    - else
    - {
    - /* This is done in default_formatize, which is called from clear_formatting_cb,
    - * which is (obviously) a clear_formatting signal handler. However, if we're
    - * here, we didn't call pidgin_webview_clear_formatting() (because we want to
    - * preserve the formatting exactly as it is), so we have to do this now. */
    - pidgin_webview_set_whole_buffer_formatting_only(entry,
    - (features & PURPLE_CONNECTION_FLAG_FORMATTING_WBFO));
    - }
    -
    purple_signal_emit(pidgin_conversations_get_handle(), "conversation-switched", conv);
    gray_stuff_out(gtkconv);
    update_typing_icon(gtkconv);
    - g_object_set_data(G_OBJECT(entry), "transient_buddy", NULL);
    + g_object_set_data(G_OBJECT(gtkconv->entry), "transient_buddy", NULL);
    regenerate_options_items(gtkconv->win);
    gtk_window_set_title(GTK_WINDOW(gtkconv->win->window),
    @@ -2355,7 +1997,6 @@
    const char *name = NULL;
    const char *stock = NULL;
    GdkPixbuf *status = NULL;
    - PurpleBlistUiOps *ops = purple_blist_get_ui_ops();
    GtkIconSize size;
    g_return_val_if_fail(conv != NULL, NULL);
    @@ -2373,8 +2014,7 @@
    /* I hate this hack. It fixes a bug where the pending message icon
    * displays in the conv tab even though it shouldn't.
    * A better solution would be great. */
    - if (ops && ops->update)
    - ops->update(NULL, (PurpleBlistNode*)b);
    + purple_blist_update_node(NULL, PURPLE_BLIST_NODE(b));
    }
    }
    @@ -2457,21 +2097,6 @@
    }
    }
    -#if 0
    -/* This gets added as an idle handler when doing something that
    - * redraws the icon. It sets the auto_resize gboolean to TRUE.
    - * This way, when the size_allocate callback gets triggered, it notices
    - * that this is an autoresize, and after the main loop iterates, it
    - * gets set back to FALSE
    - */
    -static gboolean reset_auto_resize_cb(gpointer data)
    -{
    - PidginConversation *gtkconv = (PidginConversation *)data;
    - gtkconv->auto_resize = FALSE;
    - return FALSE;
    -}
    -#endif
    -
    static gboolean
    redraw_icon(gpointer data)
    {
    @@ -2610,31 +2235,32 @@
    custom_icon_sel_cb(const char *filename, gpointer data)
    {
    if (filename) {
    - const gchar *name;
    - PurpleBuddy *buddy;
    - PurpleContact *contact;
    - PidginConversation *gtkconv = data;
    - PurpleConversation *conv = gtkconv->active_conv;
    - PurpleAccount *account = purple_conversation_get_account(conv);
    -
    - name = purple_conversation_get_name(conv);
    - buddy = purple_blist_find_buddy(account, name);
    - if (!buddy) {
    - purple_debug_info("custom-icon", "You can only set custom icons for people on your buddylist.\n");
    - return;
    - }
    - contact = purple_buddy_get_contact(buddy);
    + PurpleContact *contact = data;
    purple_buddy_icons_node_set_custom_icon_from_file((PurpleBlistNode*)contact, filename);
    }
    -}
    -
    -static void
    -set_custom_icon_cb(GtkWidget *widget, PidginConversation *gtkconv)
    -{
    - GtkWidget *win = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtkconv->win->window),
    - custom_icon_sel_cb, gtkconv);
    - gtk_widget_show_all(win);
    + g_object_set_data(G_OBJECT(data), "buddy-icon-chooser", NULL);
    +}
    +
    +static void
    +set_custom_icon_cb(GtkWidget *widget, PurpleContact *contact)
    +{
    + GtkFileChooserNative *win = NULL;
    +
    + /* Should not happen as menu item should be disabled. */
    + g_return_if_fail(contact != NULL);
    +
    + win = g_object_get_data(G_OBJECT(contact), "buddy-icon-chooser");
    + if (win == NULL) {
    + GtkMenu *menu = GTK_MENU(gtk_widget_get_parent(widget));
    + GtkWidget *toplevel =
    + gtk_widget_get_toplevel(gtk_menu_get_attach_widget(menu));
    + 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);
    + }
    + gtk_native_dialog_show(GTK_NATIVE_DIALOG(win));
    }
    static void
    @@ -2729,7 +2355,8 @@
    static gboolean
    icon_menu(GtkWidget *widget, GdkEventButton *e, PidginConversation *gtkconv)
    {
    - static GtkWidget *menu = NULL;
    + GtkWidget *menu = NULL;
    + GList *old_menus = NULL;
    PurpleConversation *conv;
    PurpleBuddy *buddy;
    @@ -2746,10 +2373,14 @@
    * If a menu already exists, destroy it before creating a new one,
    * thus freeing-up the memory it occupied.
    */
    - if (menu != NULL)
    + while ((old_menus = gtk_menu_get_for_attach_widget(widget)) != NULL) {
    + menu = old_menus->data;
    + gtk_menu_detach(GTK_MENU(menu));
    gtk_widget_destroy(menu);
    + }
    menu = gtk_menu_new();
    + gtk_menu_attach_to_widget(GTK_MENU(menu), widget, NULL);
    if (gtkconv->u.im->anim &&
    !(gdk_pixbuf_animation_is_static_image(gtkconv->u.im->anim)))
    @@ -2759,22 +2390,31 @@
    gtkconv->u.im->icon_timer);
    }
    + conv = gtkconv->active_conv;
    + buddy = purple_blist_find_buddy(purple_conversation_get_account(conv),
    + purple_conversation_get_name(conv));
    +
    pidgin_new_menu_item(menu, _("Hide Icon"), NULL,
    G_CALLBACK(remove_icon), gtkconv);
    pidgin_new_menu_item(menu, _("Save Icon As..."), GTK_STOCK_SAVE_AS,
    G_CALLBACK(icon_menu_save_cb), gtkconv);
    - pidgin_new_menu_item(menu, _("Set Custom Icon..."), NULL,
    - G_CALLBACK(set_custom_icon_cb), gtkconv);
    + if (buddy) {
    + PurpleContact *contact = purple_buddy_get_contact(buddy);
    + pidgin_new_menu_item(menu, _("Set Custom Icon..."), NULL,
    + G_CALLBACK(set_custom_icon_cb), contact);
    + } else {
    + GtkWidget *item =
    + pidgin_new_menu_item(menu, _("Set Custom Icon..."), NULL,
    + G_CALLBACK(set_custom_icon_cb), NULL);
    + gtk_widget_set_sensitive(item, FALSE);
    + }
    pidgin_new_menu_item(menu, _("Change Size"), NULL,
    G_CALLBACK(change_size_cb), gtkconv);
    /* Is there a custom icon for this person? */
    - conv = gtkconv->active_conv;
    - buddy = purple_blist_find_buddy(purple_conversation_get_account(conv),
    - purple_conversation_get_name(conv));
    if (buddy)
    {
    PurpleContact *contact = purple_buddy_get_contact(buddy);
    @@ -2827,9 +2467,8 @@
    if(gtkconv == NULL || gtkconv->active_conv != conv)
    continue;
    - if (gtkconv->unseen_state >= min_state
    - && (!hidden_only ||
    - (hidden_only && gtkconv->win == hidden_convwin))) {
    + if (gtkconv->unseen_state >= min_state &&
    + (!hidden_only || gtkconv->win == hidden_convwin)) {
    r = g_list_prepend(r, conv);
    c++;
    @@ -2966,8 +2605,8 @@
    { "Unblock", PIDGIN_STOCK_TOOLBAR_UNBLOCK, N_("_Unblock..."), NULL, NULL, G_CALLBACK(menu_unblock_cb) },
    { "Add", GTK_STOCK_ADD, N_("_Add..."), NULL, NULL, G_CALLBACK(menu_add_remove_cb) },
    { "Remove", GTK_STOCK_REMOVE, N_("_Remove..."), NULL, NULL, G_CALLBACK(menu_add_remove_cb) },
    - { "InsertLink", PIDGIN_STOCK_TOOLBAR_INSERT_LINK, N_("Insert Lin_k..."), NULL, NULL, G_CALLBACK(menu_insert_link_cb) },
    - { "InsertImage", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, N_("Insert Imag_e..."), NULL, NULL, G_CALLBACK(menu_insert_image_cb) },
    + { "InsertLink", PIDGIN_STOCK_TOOLBAR_INSERT_LINK, N_("Insert Lin_k..."), NULL, NULL, NULL },
    + { "InsertImage", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, N_("Insert Imag_e..."), NULL, NULL, NULL },
    { "Close", GTK_STOCK_CLOSE, N_("_Close"), "<control>W", NULL, G_CALLBACK(menu_close_conv_cb) },
    /* Options */
    @@ -3069,17 +2708,17 @@
    if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
    chat = purple_blist_find_chat(account, purple_conversation_get_name(conv));
    - if ((chat == NULL) && (gtkconv->webview != NULL)) {
    - chat = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
    + if ((chat == NULL) && (gtkconv->history != NULL)) {
    + chat = g_object_get_data(G_OBJECT(gtkconv->history), "transient_chat");
    }
    - if ((chat == NULL) && (gtkconv->webview != NULL)) {
    + if ((chat == NULL) && (gtkconv->history != NULL)) {
    GHashTable *components;
    PurpleAccount *account = purple_conversation_get_account(conv);
    PurpleProtocol *protocol =
    purple_protocols_find(purple_account_get_protocol_id(account));
    if (purple_account_get_connection(account) != NULL &&
    - PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info_defaults)) {
    + PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info_defaults)) {
    components = purple_protocol_chat_iface_info_defaults(protocol, purple_account_get_connection(account),
    purple_conversation_get_name(conv));
    } else {
    @@ -3090,7 +2729,7 @@
    }
    chat = purple_chat_new(account, NULL, components);
    purple_blist_node_set_transient((PurpleBlistNode *)chat, TRUE);
    - g_object_set_data_full(G_OBJECT(gtkconv->webview), "transient_chat",
    + g_object_set_data_full(G_OBJECT(gtkconv->history), "transient_chat",
    chat, (GDestroyNotify)purple_blist_remove_chat);
    }
    } else {
    @@ -3098,13 +2737,13 @@
    return FALSE;
    buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
    - if (!buddy && gtkconv->webview) {
    - buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy");
    + if (!buddy && gtkconv->history) {
    + buddy = g_object_get_data(G_OBJECT(gtkconv->history), "transient_buddy");
    if (!buddy) {
    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->webview), "transient_buddy",
    + g_object_set_data_full(G_OBJECT(gtkconv->history), "transient_buddy",
    buddy, (GDestroyNotify)g_object_unref);
    }
    }
    @@ -3164,7 +2803,7 @@
    * Check if account support voice and/or calls, and
    * if the current buddy supports it.
    */
    - if (account != NULL && PURPLE_IS_IM_CONVERSATION(conv)) {
    + if (PURPLE_IS_IM_CONVERSATION(conv)) {
    PurpleMediaCaps caps =
    purple_protocol_get_media_caps(account,
    purple_conversation_get_name(conv));
    @@ -4145,291 +3784,6 @@
    g_free(alias_key);
    }
    -/*
    - * tab_complete_process_item:
    - * @most_matched: Used internally by this function.
    - * @entered: The partial string that the user types before hitting the
    - * tab key.
    - * @entered_chars: The length of entered.
    - * @partial: This is a return variable. This will be set to a string
    - * containing the largest common string between all matches. This will
    - * be inserted into the input box at the start of the word that the
    - * user is tab completing. For example, if a chat room contains
    - * "AlfFan" and "AlfHater" and the user types "a<TAB>" then this will
    - * contain "Alf"
    - * @matches: This is a return variable. If the given name is a potential
    - * match for the entered string, then add a copy of the name to this
    - * list. The caller is responsible for g_free'ing the data in this
    - * list.
    - * @name: The buddy name or alias or slash command name that we're
    - * checking for a match.
    - */
    -static void
    -tab_complete_process_item(int *most_matched, const char *entered, gsize entered_chars, char **partial,
    - GList **matches, const char *name)
    -{
    - char *nick_partial;
    - gsize name_len = g_utf8_strlen(name, -1);
    -
    - if (entered_chars > name_len)
    - return;
    -
    - nick_partial = g_utf8_substring(name, 0, entered_chars);
    - if (purple_utf8_strcasecmp(nick_partial, entered)) {
    - g_free(nick_partial);
    - return;
    - }
    - g_free(nick_partial);
    -
    - /* if we're here, it's a possible completion */
    -
    - if (*most_matched == -1) {
    - /*
    - * this will only get called once, since from now
    - * on *most_matched is >= 0
    - */
    - *most_matched = name_len;
    - *partial = g_strdup(name);
    - }
    - else if (*most_matched) {
    - char *tmp = g_strdup(name);
    -
    - while (purple_utf8_strcasecmp(tmp, *partial)) {
    - *(g_utf8_offset_to_pointer(*partial, *most_matched)) = '\0';
    - if (*most_matched < (goffset)g_utf8_strlen(tmp, -1))
    - *(g_utf8_offset_to_pointer(tmp, *most_matched)) = '\0';
    - (*most_matched)--;
    - }
    - (*most_matched)++;
    -
    - g_free(tmp);
    - }
    -
    - *matches = g_list_insert_sorted(*matches, g_strdup(name),
    - (GCompareFunc)purple_utf8_strcasecmp);
    -}
    -
    -static gboolean
    -is_first_container(WebKitDOMNode *container)
    -{
    - gchar *name;
    - WebKitDOMNode *parent;
    -
    - while (container) {
    - parent = webkit_dom_node_get_parent_node(container);
    - if (parent) {
    - name = webkit_dom_node_get_node_name(parent);
    -
    - if (purple_strequal(name, "BODY")) {
    - g_free(name);
    -
    - if (webkit_dom_node_get_previous_sibling(container) == NULL)
    - return TRUE;
    - else
    - return FALSE;
    - }
    - g_free(name);
    - }
    - else
    - break;
    -
    - container = parent;
    - }
    -
    - return FALSE;
    -}
    -
    -static gboolean
    -tab_complete(PurpleConversation *conv)
    -{
    - PidginConversation *gtkconv;
    - WebKitDOMNode *container;
    - glong caret, word_start, content_len;
    - int most_matched = -1, colon = 0;
    - char *ch, *ch2 = NULL;
    - char *entered, *partial = NULL;
    - char *content, *sub1, *sub2, *modified;
    - const char *prefix;
    - GList *matches = NULL;
    - gboolean command = FALSE;
    - gsize entered_chars = 0;
    -
    - gtkconv = PIDGIN_CONVERSATION(conv);
    - pidgin_webview_get_caret(PIDGIN_WEBVIEW(gtkconv->entry), &container, &caret);
    -
    - /* if there's nothing there just return */
    - if (caret <= 0)
    - return PURPLE_IS_CHAT_CONVERSATION(conv);
    -
    - content = webkit_dom_node_get_node_value(container);
    - content_len = g_utf8_strlen(content, -1);
    -
    - /* if we're at the end of ":" or ": " we need to move back 1 or 2 spaces */
    - if (caret >= 2) {
    - ch = g_utf8_offset_to_pointer(content, caret - 2);
    - ch2 = g_utf8_find_next_char(ch, NULL);
    - }
    -
    - if (caret >= 2 && *ch == ':' && g_unichar_isspace(g_utf8_get_char(ch2)))
    - colon = 2;
    - else if (caret >= 1 && content[caret - 1] == ':')
    - colon = 1;
    -
    - caret -= colon;
    - word_start = caret;
    -
    - /* find the start of the word that we're tabbing. */
    - ch = g_utf8_offset_to_pointer(content, caret);
    - while ((ch = g_utf8_find_prev_char(content, ch))) {
    - if (!g_unichar_isspace(g_utf8_get_char(ch)))
    - --word_start;
    - else
    - break;
    - }
    -
    - prefix = pidgin_get_cmd_prefix();
    - if (word_start == 0 &&
    - ((gsize)caret >= strlen(prefix)) && !strncmp(content, prefix, strlen(prefix))) {
    - command = TRUE;
    - word_start += strlen(prefix);
    - }
    -
    - entered = g_utf8_substring(content, word_start, caret);
    - entered_chars = g_utf8_strlen(entered, -1);
    -
    - if (!entered_chars) {
    - g_free(content);
    - g_free(entered);
    - return PURPLE_IS_CHAT_CONVERSATION(conv);
    - }
    -
    - if (command) {
    - GList *list = purple_cmd_list(conv);
    - GList *l;
    -
    - /* Commands */
    - for (l = list; l != NULL; l = l->next) {
    - tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
    - &matches, l->data);
    - }
    - g_list_free(list);
    - } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
    - GList *l, *users;
    - GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(PIDGIN_CONVERSATION(conv)->u.chat->list));
    - GtkTreeIter iter;
    - int f;
    -
    - /* Users */
    - users = purple_chat_conversation_get_users(PURPLE_CHAT_CONVERSATION(conv));
    - for (l = users; l != NULL; l = l->next) {
    - tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
    - &matches, purple_chat_user_get_name((PurpleChatUser *)l->data));
    - }
    - g_list_free(users);
    -
    - /* Aliases */
    - if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter))
    - {
    - do {
    - char *name;
    - char *alias;
    -
    - gtk_tree_model_get(model, &iter,
    - CHAT_USERS_NAME_COLUMN, &name,
    - CHAT_USERS_ALIAS_COLUMN, &alias,
    - -1);
    -
    - if (name && alias && !purple_strequal(name, alias))
    - tab_complete_process_item(&most_matched, entered, entered_chars, &partial,
    - &matches, alias);
    - g_free(name);
    - g_free(alias);
    -
    - f = gtk_tree_model_iter_next(model, &iter);
    - } while (f != 0);
    - }
    - } else {
    - g_free(content);
    - g_free(entered);
    - return FALSE;
    - }
    -
    - /* if there weren't any matches, return */
    - if (!matches) {
    - /* if matches isn't set partials won't be either */
    - g_free(content);
    - g_free(entered);
    - return PURPLE_IS_CHAT_CONVERSATION(conv);
    - }
    -
    - sub1 = g_utf8_substring(content, 0, word_start);
    - sub2 = g_utf8_substring(content, caret, content_len);
    -
    - if (!matches->next) {
    - /* there was only one match. fill it in. */
    -
    - if (!colon && !word_start && is_first_container(container)) {
    - char *tmp = NULL;
    - if (caret < content_len) {
    - tmp = g_strdup_printf("%s: ", (char *)matches->data);
    - } else {
    - char nbsp[6] = {0};
    - g_unichar_to_utf8(0xA0, nbsp);
    - tmp = g_strdup_printf("%s:%s", (char *)matches->data, nbsp);
    - }
    -
    - modified = g_strdup_printf("%s%s", tmp, sub2);
    - webkit_dom_node_set_node_value(container, modified, NULL);
    - pidgin_webview_set_caret(PIDGIN_WEBVIEW(gtkconv->entry), container,
    - g_utf8_strlen(tmp, -1));
    - g_free(tmp);
    - g_free(modified);
    - }
    - else {
    - modified = g_strdup_printf("%s%s%s", sub1, (char *)matches->data, sub2);
    - webkit_dom_node_set_node_value(container, modified, NULL);
    - pidgin_webview_set_caret(PIDGIN_WEBVIEW(gtkconv->entry), container,
    - word_start + g_utf8_strlen(matches->data, -1) + colon);
    - g_free(modified);
    - }
    -
    - g_free(matches->data);
    - g_list_free(matches);
    - }
    - else {
    - /*
    - * there were lots of matches, fill in as much as possible
    - * and display all of them
    - */
    - char *addthis = g_malloc0(1);
    -
    - while (matches) {
    - char *tmp = addthis;
    - addthis = g_strconcat(tmp, matches->data, " ", NULL);
    - g_free(tmp);
    - g_free(matches->data);
    - matches = g_list_remove(matches, matches->data);
    - }
    -
    - purple_conversation_write_system_message(conv, addthis, PURPLE_MESSAGE_NO_LOG);
    -
    - modified = g_strdup_printf("%s%s%s", sub1, partial, sub2);
    - webkit_dom_node_set_node_value(container, modified, NULL);
    - pidgin_webview_set_caret(PIDGIN_WEBVIEW(gtkconv->entry), container,
    - word_start + g_utf8_strlen(partial, -1) + colon);
    - g_free(addthis);
    - g_free(modified);
    - }
    -
    - g_free(content);
    - g_free(entered);
    - g_free(partial);
    - g_free(sub1);
    - g_free(sub2);
    -
    - return TRUE;
    -}
    -
    static void topic_callback(GtkWidget *w, PidginConversation *gtkconv)
    {
    PurpleProtocol *protocol = NULL;
    @@ -4444,7 +3798,7 @@
    if(!gc || !(protocol = purple_connection_get_protocol(gc)))
    return;
    - if(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, set_topic))
    + if(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, set_topic))
    return;
    gtkconv = PIDGIN_CONVERSATION(conv);
    @@ -4687,92 +4041,12 @@
    }
    static void
    -entry_popup_menu_cb(PidginWebView *webview, GtkMenu *menu, gpointer data)
    -{
    - GtkWidget *menuitem;
    - PidginConversation *gtkconv = data;
    - gboolean is_empty;
    -
    - g_return_if_fail(menu != NULL);
    - g_return_if_fail(gtkconv != NULL);
    -
    - menuitem = pidgin_new_menu_item(NULL, _("_Send"), NULL,
    - G_CALLBACK(send_cb), gtkconv);
    - is_empty = pidgin_webview_is_empty(webview);
    - if (is_empty)
    - gtk_widget_set_sensitive(menuitem, FALSE);
    - gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 0);
    -
    - menuitem = gtk_separator_menu_item_new();
    - gtk_widget_show(menuitem);
    - gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menuitem, 1);
    -}
    -
    -static gboolean
    -resize_webview_cb(PidginConversation *gtkconv)
    -{
    - PidginWebView *webview;
    - gint min_lines;
    - gint max_height;
    - gint min_height;
    - gint font_size;
    - gint total_height;
    - gint height;
    - gint toolbar_size;
    - gint old_w;
    - gint old_h;
    - GtkAllocation webview_allocation;
    - GtkAllocation entry_allocation;
    -
    - webview = PIDGIN_WEBVIEW(gtkconv->entry);
    -
    - /* Get text height from the DOM */
    - height = pidgin_webview_get_DOM_height(webview);
    -
    - /* Find the height of the conversation window to calculate the maximum possible entry
    - * size (1/2 of the window)
    - */
    - gtk_widget_get_allocation(gtkconv->webview, &webview_allocation);
    - gtk_widget_get_allocation(gtkconv->entry, &entry_allocation);
    - total_height = webview_allocation.height + entry_allocation.height;
    - max_height = total_height / 2;
    -
    - /* Get size of the characters to calculate initial minimum space for the entry */
    - font_size = pidgin_webview_get_font_size(webview);
    -
    - /* Allow to have a minimum of "min_lines" height as defined in the preference */
    - min_lines = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/minimum_entry_lines");
    - min_height = (font_size + WEBVIEW_DOM_FONT_PADDING) * min_lines + WEBVIEW_DOM_TEXT_PADDING;
    -
    -
    - /* Take into account the size of the formatting toolbar */
    - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar")) {
    - toolbar_size = gtk_widget_get_allocated_height(pidgin_webview_get_toolbar(webview));
    - } else {
    - toolbar_size = 0;
    - }
    -
    - /* Calculate conv entry height */
    - height = CLAMP(height, MIN(min_height, max_height), max_height);
    - /* Add the size used by the toolbar so we always take it into consideration. */
    - height += toolbar_size;
    -
    - /* Actually set the size of the gtkconv entry widget. */
    - gtk_widget_get_size_request(gtkconv->lower_hbox, &old_w, &old_h);
    - if (old_w != -1 || old_h != height) {
    - gtk_widget_set_size_request(gtkconv->lower_hbox, -1, height);
    - purple_debug_info("pidgin", "resizing to %d, %d lines\n", height, min_lines);
    - }
    -
    - return FALSE;
    -}
    -
    -static void
    minimum_entry_lines_pref_cb(const char *name,
    PurplePrefType type,
    gconstpointer value,
    gpointer data)
    {
    +#if 0
    GList *l = purple_conversations_get_all();
    PurpleConversation *conv;
    while (l != NULL)
    @@ -4781,9 +4055,9 @@
    if (PIDGIN_IS_PIDGIN_CONVERSATION(conv))
    resize_webview_cb(PIDGIN_CONVERSATION(conv));
    -
    l = l->next;
    }
    +#endif
    }
    static void
    @@ -4806,7 +4080,7 @@
    gtkchat->topic_text = gtk_entry_new();
    gtk_widget_set_size_request(gtkchat->topic_text, -1, BUDDYICON_SIZE_MIN);
    - if(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, set_topic)) {
    + if(!PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, set_topic)) {
    gtk_editable_set_editable(GTK_EDITABLE(gtkchat->topic_text), FALSE);
    } else {
    g_signal_connect(G_OBJECT(gtkchat->topic_text), "activate",
    @@ -4958,7 +4232,7 @@
    if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
    node = (PurpleBlistNode*)(purple_blist_find_chat(purple_conversation_get_account(conv), purple_conversation_get_name(conv)));
    if (!node)
    - node = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_chat");
    + node = g_object_get_data(G_OBJECT(gtkconv->history), "transient_chat");
    } else {
    node = (PurpleBlistNode*)(purple_blist_find_buddy(purple_conversation_get_account(conv), purple_conversation_get_name(conv)));
    #if 0
    @@ -4973,326 +4247,10 @@
    return FALSE;
    }
    -/* Quick Find {{{ */
    -static gboolean
    -pidgin_conv_end_quickfind(PidginConversation *gtkconv)
    -{
    - GtkStyleContext *context = gtk_widget_get_style_context(gtkconv->quickfind_entry);
    - gtk_style_context_remove_class(context, "not-found");
    -
    - webkit_web_view_unmark_text_matches(WEBKIT_WEB_VIEW(gtkconv->webview));
    - gtk_widget_hide(gtkconv->quickfind_container);
    -
    - gtk_widget_grab_focus(gtkconv->entry);
    - return TRUE;
    -}
    -
    -static gboolean
    -quickfind_process_input(GtkWidget *entry, GdkEventKey *event, PidginConversation *gtkconv)
    -{
    - switch (event->keyval) {
    - case GDK_KEY_Return:
    - case GDK_KEY_KP_Enter:
    - if (webkit_web_view_search_text(WEBKIT_WEB_VIEW(gtkconv->webview), gtk_entry_get_text(GTK_ENTRY(entry)), FALSE, TRUE, TRUE)) {
    - GtkStyleContext *context = gtk_widget_get_style_context(gtkconv->quickfind_entry);
    - gtk_style_context_remove_class(context, "not-found");
    - } else {
    - GtkStyleContext *context = gtk_widget_get_style_context(gtkconv->quickfind_entry);
    - gtk_style_context_add_class(context, "not-found");
    - }
    - break;
    - case GDK_KEY_Escape:
    - pidgin_conv_end_quickfind(gtkconv);
    - break;
    - default:
    - return FALSE;
    - }
    - return TRUE;
    -}
    -
    -static void
    -pidgin_conv_setup_quickfind(PidginConversation *gtkconv, GtkWidget *container)
    -{
    - GtkWidget *widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    - GtkWidget *label, *entry, *close;
    - GtkStyleContext *context;
    - GtkCssProvider *filter_css;
    - const gchar filter_style[] =
    - ".not-found {"
    - "color: @error_fg_color;"
    - "text-shadow: 0 1px @error_text_shadow;"
    - "background-image: none;"
    - "background-color: @error_bg_color;"
    - "}";
    -
    - gtk_box_pack_start(GTK_BOX(container), widget, FALSE, FALSE, 0);
    -
    - close = pidgin_create_small_button(gtk_label_new("×"));
    - gtk_box_pack_start(GTK_BOX(widget), close, FALSE, FALSE, 0);
    - gtk_widget_set_tooltip_text(close, _("Close Find bar"));
    -
    - label = gtk_label_new(_("Find:"));
    - gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 10);
    -
    - entry = gtk_entry_new();
    - gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0);
    - filter_css = gtk_css_provider_new();
    - gtk_css_provider_load_from_data(filter_css, filter_style, -1, NULL);
    - context = gtk_widget_get_style_context(entry);
    - gtk_style_context_add_provider(context,
    - GTK_STYLE_PROVIDER(filter_css),
    - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    -
    - gtkconv->quickfind_entry = entry;
    - gtkconv->quickfind_container = widget;
    -
    - /* Hook to signals and stuff */
    - g_signal_connect(G_OBJECT(entry), "key-press-event",
    - G_CALLBACK(quickfind_process_input), gtkconv);
    - g_signal_connect_swapped(G_OBJECT(close), "button-press-event",
    - G_CALLBACK(pidgin_conv_end_quickfind), gtkconv);
    -}
    -
    -/* }}} */
    -
    -static char *
    -replace_header_tokens(PurpleConversation *conv, const char *text)
    -{
    - PurpleAccount *account = purple_conversation_get_account(conv);
    - GString *str;
    - const char *cur = text;
    - const char *prev = cur;
    - time_t mtime;
    - struct tm *tm = NULL;
    -
    - if (text == NULL || *text == '\0')
    - return NULL;
    -
    - str = g_string_new(NULL);
    - while ((cur = strchr(cur, '%'))) {
    - char *freeval = NULL;
    - const char *replace = NULL;
    - const char *fin = NULL;
    -
    - if (g_str_has_prefix(cur, "%chatName%")) {
    - replace = purple_conversation_get_name(conv);
    -
    - } else if (g_str_has_prefix(cur, "%sourceName%")) {
    - replace = purple_account_get_private_alias(account);
    - if (replace == NULL)
    - replace = purple_account_get_username(account);
    -
    - } else if (g_str_has_prefix(cur, "%destinationName%")) {
    - PurpleBuddy *buddy = purple_blist_find_buddy(account, purple_conversation_get_name(conv));
    - if (buddy) {
    - replace = purple_buddy_get_alias(buddy);
    - } else {
    - replace = purple_conversation_get_name(conv);
    - }
    -
    - } else if (g_str_has_prefix(cur, "%incomingIconPath%")) {
    - PurpleBuddyIcon *icon = purple_im_conversation_get_icon(PURPLE_IM_CONVERSATION(conv));
    - if (icon)
    - replace = purple_buddy_icon_get_full_path(icon);
    -
    - } else if (g_str_has_prefix(cur, "%outgoingIconPath%")) {
    - replace = purple_account_get_buddy_icon_path(account);
    -
    - } else if (g_str_has_prefix(cur, "%timeOpened")) {
    - const char *tmp = cur + strlen("%timeOpened");
    -
    - if (*tmp == '{') {
    - const char *end;
    - tmp++;
    - end = strstr(tmp, "}%");
    - if (!end) /* Invalid string */
    - continue;
    - if (!tm) {
    - mtime = time(NULL);
    - tm = localtime(&mtime);
    - }
    - replace = freeval = purple_uts35_to_str(tmp, end - tmp, tm);
    - fin = end + 1;
    - } else {
    - if (!tm) {
    - mtime = time(NULL);
    - tm = localtime(&mtime);
    - }
    -
    - replace = purple_utf8_strftime("%X", tm);
    - }
    -
    - } else if (g_str_has_prefix(cur, "%dateOpened%")) {
    - if (!tm) {
    - mtime = time(NULL);
    - tm = localtime(&mtime);
    - }
    -
    - replace = purple_date_format_short(tm);
    -
    - } else {
    - cur++;
    - continue;
    - }
    -
    - /* Here we have a replacement to make */
    - g_string_append_len(str, prev, cur - prev);
    - if (replace)
    - g_string_append(str, replace);
    -
    - /* And update the pointers */
    - if (fin) {
    - prev = cur = fin + 1;
    - } else {
    - prev = cur = strchr(cur + 1, '%') + 1;
    - }
    - g_free(freeval);
    - freeval = NULL;
    - }
    -
    - /* And wrap it up */
    - g_string_append(str, prev);
    - return g_string_free(str, FALSE);
    -}
    -
    -static char *
    -replace_template_tokens(PidginConvTheme *theme, const char *header, const char *footer)
    -{
    - GString *str;
    - const char *text;
    - char **ms;
    - char *path;
    -
    - text = pidgin_conversation_theme_get_template(theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_MAIN);
    - if (text == NULL)
    - return NULL;
    -
    - ms = g_strsplit(text, "%@", 6);
    - if (ms[0] == NULL || ms[1] == NULL || ms[2] == NULL || ms[3] == NULL || ms[4] == NULL || ms[5] == NULL) {
    - g_strfreev(ms);
    - return NULL;
    - }
    -
    - str = g_string_new(NULL);
    -
    - g_string_append(str, ms[0]);
    - g_string_append(str, "file://");
    - path = pidgin_conversation_theme_get_template_path(theme);
    - g_string_append(str, path);
    - g_free(path);
    -
    - g_string_append(str, ms[1]);
    -
    - text = pidgin_conversation_theme_get_template(theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_BASESTYLE_CSS);
    - g_string_append(str, text);
    -
    - g_string_append(str, ms[2]);
    -
    - g_string_append(str, "file://");
    - path = pidgin_conversation_theme_get_css_path(theme);
    - g_string_append(str, path);
    - g_free(path);
    -
    - g_string_append(str, ms[3]);
    - if (header)
    - g_string_append(str, header);
    - g_string_append(str, ms[4]);
    - if (footer)
    - g_string_append(str, footer);
    - g_string_append(str, ms[5]);
    -
    - g_strfreev(ms);
    -
    - return g_string_free(str, FALSE);
    -}
    -
    -static void
    -set_theme_webkit_settings(WebKitWebView *webview, PidginConvTheme *theme)
    -{
    - WebKitWebSettings *settings;
    - const GValue *val;
    -
    - g_object_get(G_OBJECT(webview), "settings", &settings, NULL);
    -
    - val = pidgin_conversation_theme_lookup(theme, "DefaultFontFamily", TRUE);
    - if (val && G_VALUE_HOLDS_STRING(val))
    - {
    - const gchar *font_family = g_value_get_string(val);
    -#ifdef _WIN32
    - /* XXX: a hack for not converting backslash to yen sign.
    - * See gtkwebview.c: pidgin_webview_new.
    - */
    - if (g_ascii_strcasecmp(font_family, "sans-serif") == 0)
    - font_family = NULL;
    -#endif
    - if (font_family)
    - g_object_set(G_OBJECT(settings), "default-font-family", font_family, NULL);
    - }
    -
    - val = pidgin_conversation_theme_lookup(theme, "DefaultFontSize", TRUE);
    - if (val && G_VALUE_HOLDS_INT(val))
    - g_object_set(G_OBJECT(settings), "default-font-size", GINT_TO_POINTER(g_value_get_int(val)), NULL);
    -
    - val = pidgin_conversation_theme_lookup(theme, "DefaultBackgroundIsTransparent", TRUE);
    - if (val && G_VALUE_HOLDS_BOOLEAN(val))
    - /* this does not work :( */
    - webkit_web_view_set_transparent(webview, g_value_get_boolean(val));
    -}
    -
    -static void
    -conv_variant_changed_cb(GObject *gobject, GParamSpec *pspec, gpointer user_data)
    -{
    - PidginConversation *gtkconv = user_data;
    - char *path, *js;
    -
    - path = pidgin_conversation_theme_get_css_path(PIDGIN_CONV_THEME(gobject));
    - js = g_strdup_printf("setStylesheet(\"mainStyle\", \"file://%s\");", path);
    - g_free(path);
    - pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(gtkconv->webview), js);
    - g_free(js);
    -}
    -
    -static void
    -load_conv_theme(PidginConversation *gtkconv)
    -{
    - char *header, *footer;
    - char *template;
    - char *basedir, *baseuri;
    -
    - header = replace_header_tokens(gtkconv->active_conv,
    - pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_HEADER));
    - footer = replace_header_tokens(gtkconv->active_conv,
    - pidgin_conversation_theme_get_template(gtkconv->theme, PIDGIN_CONVERSATION_THEME_TEMPLATE_FOOTER));
    - template = replace_template_tokens(gtkconv->theme, header, footer);
    - g_free(header);
    - g_free(footer);
    -
    - if (template == NULL)
    - return;
    -
    - set_theme_webkit_settings(WEBKIT_WEB_VIEW(gtkconv->webview), gtkconv->theme);
    -
    - basedir = pidgin_conversation_theme_get_template_path(gtkconv->theme);
    - baseuri = g_strdup_printf("file://%s", basedir);
    - webkit_web_view_load_string(WEBKIT_WEB_VIEW(gtkconv->webview), template,
    - "text/html", "UTF-8", baseuri);
    -
    - if (PURPLE_IS_CHAT_CONVERSATION(gtkconv->active_conv))
    - pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(gtkconv->webview),
    - "document.getElementById('Chat').className = 'groupchat'");
    -
    - g_signal_connect(G_OBJECT(gtkconv->theme), "notify::variant",
    - G_CALLBACK(conv_variant_changed_cb), gtkconv);
    -
    - g_free(basedir);
    - g_free(baseuri);
    - g_free(template);
    -}
    -
    static GtkWidget *
    setup_common_pane(PidginConversation *gtkconv)
    {
    - GtkWidget *vbox, *frame, *webview_sw, *event_box;
    + GtkWidget *vbox, *sw, *event_box, *view;
    GtkCellRenderer *rend;
    GtkTreePath *path;
    PurpleConversation *conv = gtkconv->active_conv;
    @@ -5388,12 +4346,20 @@
    gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(gtkconv->infopane), rend, "pixbuf", CONV_EMBLEM_COLUMN, NULL);
    g_object_set(rend, "xalign", 0.0, "xpad", 6, "ypad", 0, NULL);
    - /* Setup the webkit widget */
    - frame = pidgin_create_webview(FALSE, &gtkconv->webview, &webview_sw);
    - g_object_set(G_OBJECT(gtkconv->webview), "expand", TRUE, NULL);
    - _pidgin_widget_set_accessible_name(frame, "Conversation Pane");
    -
    - load_conv_theme(gtkconv);
    + /* Setup the history widget */
    + sw = gtk_scrolled_window_new(NULL, NULL);
    + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
    + gtk_scrolled_window_set_policy(
    + GTK_SCROLLED_WINDOW(sw),
    + GTK_POLICY_NEVER,
    + GTK_POLICY_ALWAYS
    + );
    +
    + gtkconv->history_buffer = talkatu_history_buffer_new();
    + gtkconv->history = talkatu_history_new();
    + gtk_text_view_set_buffer(GTK_TEXT_VIEW(gtkconv->history), gtkconv->history_buffer);
    + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(gtkconv->history), GTK_WRAP_WORD);
    + gtk_container_add(GTK_CONTAINER(sw), gtkconv->history);
    if (chat) {
    GtkWidget *hpaned;
    @@ -5401,52 +4367,44 @@
    /* Add the topic */
    setup_chat_topic(gtkconv, vbox);
    - /* Add the gtkwebview frame */
    + /* Add the talkatu history */
    hpaned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
    gtk_box_pack_start(GTK_BOX(vbox), hpaned, TRUE, TRUE, 0);
    gtk_widget_show(hpaned);
    - gtk_paned_pack1(GTK_PANED(hpaned), frame, TRUE, TRUE);
    + gtk_paned_pack1(GTK_PANED(hpaned), sw, TRUE, TRUE);
    /* Now add the userlist */
    setup_chat_userlist(gtkconv, hpaned);
    } else {
    - gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
    - }
    - gtk_widget_show_all(frame);
    -
    - gtk_widget_set_name(gtkconv->webview, "pidgin_conv_webview");
    - g_object_set_data(G_OBJECT(gtkconv->webview), "gtkconv", gtkconv);
    -
    - g_signal_connect_after(G_OBJECT(gtkconv->webview), "button_press_event",
    - G_CALLBACK(entry_stop_rclick_cb), NULL);
    - g_signal_connect(G_OBJECT(gtkconv->webview), "key_press_event",
    + gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
    + }
    + gtk_widget_show_all(sw);
    +
    + g_object_set_data(G_OBJECT(gtkconv->history), "gtkconv", gtkconv);
    +
    + g_signal_connect(G_OBJECT(gtkconv->history), "key_press_event",
    G_CALLBACK(refocus_entry_cb), gtkconv);
    - g_signal_connect(G_OBJECT(gtkconv->webview), "key_release_event",
    + g_signal_connect(G_OBJECT(gtkconv->history), "key_release_event",
    G_CALLBACK(refocus_entry_cb), gtkconv);
    - pidgin_conv_setup_quickfind(gtkconv, vbox);
    -
    gtkconv->lower_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
    gtk_box_pack_start(GTK_BOX(vbox), gtkconv->lower_hbox, FALSE, FALSE, 0);
    gtk_widget_show(gtkconv->lower_hbox);
    /* Setup the entry widget and all signals */
    - frame = pidgin_create_webview(TRUE, &gtkconv->entry, NULL);
    - gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), frame, TRUE, TRUE, 0);
    - gtk_widget_show(frame);
    -
    - _pidgin_widget_set_accessible_name(frame, "Message Input");
    - gtk_widget_set_name(gtkconv->entry, "pidgin_conv_entry");
    -
    - g_signal_connect(G_OBJECT(gtkconv->entry), "populate-popup",
    - G_CALLBACK(entry_popup_menu_cb), gtkconv);
    - g_signal_connect(G_OBJECT(gtkconv->entry), "key-press-event",
    - G_CALLBACK(entry_key_press_cb), gtkconv);
    -#if 0
    - /* TODO WebKit: Why? */
    - g_signal_connect_after(G_OBJECT(gtkconv->entry), "button-press-event",
    - G_CALLBACK(entry_stop_rclick_cb), NULL);
    -#endif
    + gtkconv->editor = talkatu_editor_new();
    + talkatu_editor_set_buffer(TALKATU_EDITOR(gtkconv->editor), talkatu_html_buffer_new());
    + gtk_box_pack_start(GTK_BOX(gtkconv->lower_hbox), gtkconv->editor, TRUE, TRUE, 0);
    +
    + view = talkatu_editor_get_view(TALKATU_EDITOR(gtkconv->editor));
    + gtk_widget_set_name(view, "pidgin_conv_entry");
    + talkatu_view_set_send_binding(TALKATU_VIEW(view), TALKATU_VIEW_SEND_BINDING_RETURN | TALKATU_VIEW_SEND_BINDING_KP_ENTER);
    + g_signal_connect(
    + G_OBJECT(view),
    + "send-message",
    + G_CALLBACK(send_cb),
    + gtkconv
    + );
    if (!chat) {
    /* For sending typing notifications for IMs */
    @@ -5455,149 +4413,9 @@
    gtkconv->u.im->show_icon = TRUE;
    }
    -#if 0
    - /* TODO WebKit: sizing stuff? */
    - g_signal_connect_swapped(G_OBJECT(gtkconv->entry_buffer), "changed",
    - G_CALLBACK(resize_webview_cb), gtkconv);
    - g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "size-allocate",
    - G_CALLBACK(resize_webview_cb), gtkconv);
    -#endif
    - g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "changed",
    - G_CALLBACK(resize_webview_cb), gtkconv);
    - g_signal_connect_swapped(G_OBJECT(gtkconv->entry), "size-allocate",
    - G_CALLBACK(resize_webview_cb), gtkconv);
    -
    - default_formatize(gtkconv);
    - g_signal_connect_after(G_OBJECT(gtkconv->entry), "format-cleared",
    - G_CALLBACK(clear_formatting_cb), gtkconv);
    return vbox;
    }
    -static void
    -conv_dnd_recv(GtkWidget *widget, GdkDragContext *dc, guint x, guint y,
    - GtkSelectionData *sd, guint info, guint t,
    - PidginConversation *gtkconv)
    -{
    - PurpleConversation *conv = gtkconv->active_conv;
    - PidginConvWindow *win = gtkconv->win;
    - PurpleIMConversation *im;
    - PurpleAccount *convaccount = purple_conversation_get_account(conv);
    - PurpleConnection *gc = purple_account_get_connection(convaccount);
    - PurpleProtocol *protocol = gc ? purple_connection_get_protocol(gc) : NULL;
    - const guchar *data = gtk_selection_data_get_data(sd);
    -
    - if (info == PIDGIN_DRAG_BLIST_NODE)
    - {
    - PurpleBlistNode *n = NULL;
    - PurpleBuddy *b;
    - PidginConversation *gtkconv = NULL;
    - PurpleAccount *buddyaccount;
    - const char *buddyname;
    - PurpleBlistNode **data_val = (gpointer)data;
    -
    - n = *data_val;
    -
    - if (PURPLE_IS_CONTACT(n))
    - b = purple_contact_get_priority_buddy((PurpleContact*)n);
    - else if (PURPLE_IS_BUDDY(n))
    - b = (PurpleBuddy*)n;
    - else
    - return;
    -
    - buddyaccount = purple_buddy_get_account(b);
    - buddyname = purple_buddy_get_name(b);
    - /*
    - * If a buddy is dragged to a chat window of the same protocol,
    - * invite him to the chat.
    - */
    - if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
    - protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, invite) &&
    - purple_strequal(purple_account_get_protocol_id(convaccount),
    - purple_account_get_protocol_id(buddyaccount))) {
    - purple_chat_conversation_invite_user(PURPLE_CHAT_CONVERSATION(conv), buddyname, NULL, TRUE);
    - } else {
    - /*
    - * If we already have an open conversation with this buddy, then
    - * just move the conv to this window. Otherwise, create a new
    - * conv and add it to this window.
    - */
    - im = purple_conversations_find_im_with_account(buddyname, buddyaccount);
    - if (im != NULL) {
    - PidginConvWindow *oldwin;
    - gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(im));
    - oldwin = gtkconv->win;
    - if (oldwin != win) {
    - pidgin_conv_window_remove_gtkconv(oldwin, gtkconv);
    - pidgin_conv_window_add_gtkconv(win, gtkconv);
    - }
    - } else {
    - im = purple_im_conversation_new(buddyaccount, buddyname);
    - gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(im));
    - if (gtkconv->win != win) {
    - pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv);
    - pidgin_conv_window_add_gtkconv(win, gtkconv);
    - }
    - }
    -
    - /* Make this conversation the active conversation */
    - pidgin_conv_window_switch_gtkconv(win, gtkconv);
    - }
    -
    - gtk_drag_finish(dc, TRUE,
    - gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
    - }
    - else if (info == PIDGIN_DRAG_IM_CONTACT)
    - {
    - char *protocol_id = NULL;
    - char *username = NULL;
    - PurpleAccount *account;
    - PidginConversation *gtkconv;
    -
    - if (pidgin_parse_x_im_contact((const char *) data, FALSE, &account,
    - &protocol_id, &username, NULL))
    - {
    - if (account == NULL)
    - {
    - purple_notify_error(win, NULL,
    - _("You are not currently signed on with an account that "
    - "can add that buddy."), NULL, NULL);
    - } else {
    - /*
    - * If a buddy is dragged to a chat window of the same protocol,
    - * invite him to the chat.
    - */
    - if (PURPLE_IS_CHAT_CONVERSATION(conv) &&
    - protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, invite) &&
    - purple_strequal(purple_account_get_protocol_id(convaccount), protocol_id)) {
    - purple_chat_conversation_invite_user(PURPLE_CHAT_CONVERSATION(conv), username, NULL, TRUE);
    - } else {
    - im = purple_im_conversation_new(account, username);
    - gtkconv = PIDGIN_CONVERSATION(PURPLE_CONVERSATION(im));
    - if (gtkconv->win != win) {
    - pidgin_conv_window_remove_gtkconv(gtkconv->win, gtkconv);
    - pidgin_conv_window_add_gtkconv(win, gtkconv);
    - }
    - }
    - }
    - }
    -
    - g_free(username);
    - g_free(protocol_id);
    -
    - gtk_drag_finish(dc, TRUE,
    - gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
    - }
    - else if (info == WEBKIT_WEB_VIEW_TARGET_INFO_URI_LIST) {
    - if (PURPLE_IS_IM_CONVERSATION(conv))
    - pidgin_dnd_file_manage(sd, convaccount, purple_conversation_get_name(conv));
    - gtk_drag_finish(dc, TRUE,
    - gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE, t);
    - }
    - else
    - gtk_drag_finish(dc, FALSE, FALSE, t);
    -}
    -
    -
    static PidginConversation *
    pidgin_conv_find_gtkconv(PurpleConversation * conv)
    {
    @@ -5657,43 +4475,6 @@
    return FALSE;
    }
    -static void set_typing_font(GtkWidget *widget, PidginConversation *gtkconv)
    -{
    -/* TODO WEBKIT */
    -#if 0
    - static PangoFontDescription *font_desc = NULL;
    - static GdkRGBA *color = NULL;
    - static gboolean enable = TRUE;
    -
    - if (font_desc == NULL) {
    - char *string = NULL;
    - gtk_widget_style_get(widget,
    - "typing-notification-font", &string,
    - "typing-notification-color", &color,
    - "typing-notification-enable", &enable,
    - NULL);
    - font_desc = pango_font_description_from_string(string);
    - g_free(string);
    - if (color == NULL) {
    - GdkRGBA def = {0x8888/65535.0, 0x8888/65535.0, 0x8888/65535.0, 1.0};
    - color = gdk_rgba_copy(&def);
    - }
    - }
    -
    - gtk_text_buffer_create_tag(GTK_IMHTML(widget)->text_buffer, "TYPING-NOTIFICATION",
    - "foreground-rgba", color,
    - "font-desc", font_desc,
    - NULL);
    -
    - if (!enable) {
    - g_object_set_data(G_OBJECT(widget), "disable-typing-notification", GINT_TO_POINTER(TRUE));
    - /* or may be 'gtkconv->disable_typing = TRUE;' instead? */
    - }
    -
    - g_signal_handlers_disconnect_by_func(G_OBJECT(widget), set_typing_font, gtkconv);
    -#endif /* if 0 */
    -}
    -
    /**************************************************************************
    * Conversation UI operations
    **************************************************************************/
    @@ -5701,12 +4482,9 @@
    private_gtkconv_new(PurpleConversation *conv, gboolean hidden)
    {
    PidginConversation *gtkconv;
    - const char *theme_name;
    - PurpleTheme *theme = NULL;
    GtkWidget *pane = NULL;
    GtkWidget *tab_cont;
    PurpleBlistNode *convnode;
    - GtkTargetList *targets;
    if (PURPLE_IS_IM_CONVERSATION(conv) && (gtkconv = pidgin_conv_find_gtkconv(conv))) {
    purple_conversation_set_ui_data(conv, gtkconv);
    @@ -5725,12 +4503,6 @@
    /* Setup some initial variables. */
    gtkconv->unseen_state = PIDGIN_UNSEEN_NONE;
    gtkconv->unseen_count = 0;
    - theme_name = purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/theme");
    - if (theme_name && *theme_name)
    - theme = purple_theme_manager_find_theme(theme_name, "conversation");
    - if (!theme)
    - theme = default_conv_theme;
    - gtkconv->theme = PIDGIN_CONV_THEME(g_object_ref(theme));
    gtkconv->last_flags = 0;
    if (PURPLE_IS_IM_CONVERSATION(conv)) {
    @@ -5751,54 +4523,14 @@
    return;
    }
    - /* Setup drag-and-drop */
    - gtk_drag_dest_set(pane, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
    - NULL, 0, GDK_ACTION_COPY);
    - targets = gtk_target_list_new(dnd_targets, G_N_ELEMENTS(dnd_targets));
    - gtk_target_list_add(targets, gdk_atom_intern("text/uri-list", FALSE), 0,
    - WEBKIT_WEB_VIEW_TARGET_INFO_URI_LIST);
    - gtk_drag_dest_set_target_list(pane, targets);
    -
    - if (webkit_dnd_targets) {
    - targets = webkit_dnd_targets;
    - } else {
    - GtkTargetEntry *entries;
    - gint count;
    -
    - targets = webkit_web_view_get_paste_target_list(WEBKIT_WEB_VIEW(gtkconv->webview));
    - entries = gtk_target_table_new_from_list(targets, &count);
    - targets = gtk_target_list_new(entries, count);
    - gtk_target_table_free(entries, count);
    -
    - gtk_target_list_add_table(targets, dnd_targets, G_N_ELEMENTS(dnd_targets));
    - webkit_dnd_targets = targets;
    - }
    -
    - gtk_drag_dest_set(gtkconv->webview, 0, NULL, 0, GDK_ACTION_COPY);
    - gtk_drag_dest_set_target_list(gtkconv->webview, targets);
    -
    - gtk_drag_dest_set(gtkconv->entry, 0, NULL, 0, GDK_ACTION_COPY);
    - gtk_drag_dest_set_target_list(gtkconv->entry, targets);
    -
    g_signal_connect(G_OBJECT(pane), "button_press_event",
    G_CALLBACK(ignore_middle_click), NULL);
    - g_signal_connect(G_OBJECT(pane), "drag-data-received",
    - G_CALLBACK(conv_dnd_recv), gtkconv);
    -#if 0
    - /* FIXME: WebKit confuses the dnd source when this is enabled */
    - g_signal_connect(G_OBJECT(gtkconv->webview), "drag-data-received",
    - G_CALLBACK(conv_dnd_recv), gtkconv);
    - g_signal_connect(G_OBJECT(gtkconv->entry), "drag-data-received",
    - G_CALLBACK(conv_dnd_recv), gtkconv);
    -#endif
    -
    - g_signal_connect(gtkconv->webview, "style-updated", G_CALLBACK(set_typing_font), gtkconv);
    /* Setup the container for the tab. */
    gtkconv->tab_cont = tab_cont = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
    g_object_set_data(G_OBJECT(tab_cont), "PidginConversation", gtkconv);
    gtk_container_set_border_width(GTK_CONTAINER(tab_cont), PIDGIN_HIG_BOX_SPACE);
    - gtk_container_add(GTK_CONTAINER(tab_cont), pane);
    + gtk_box_pack_start(GTK_BOX(tab_cont), pane, TRUE, TRUE, 0);
    gtk_widget_show(pane);
    convnode = get_conversation_blist_node(conv);
    @@ -5810,14 +4542,10 @@
    purple_conversation_set_logging(conv, logging);
    }
    - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar"))
    - pidgin_webview_show_toolbar(PIDGIN_WEBVIEW(gtkconv->entry));
    - else
    - pidgin_webview_hide_toolbar(PIDGIN_WEBVIEW(gtkconv->entry));
    - pidgin_webview_switch_active_conversation(
    - PIDGIN_WEBVIEW(gtkconv->entry), conv);
    - pidgin_webview_switch_active_conversation(
    - PIDGIN_WEBVIEW(gtkconv->webview), conv);
    + talkatu_editor_set_toolbar_visible(
    + TALKATU_EDITOR(gtkconv->editor),
    + purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_formatting_toolbar")
    + );
    if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/im/show_buddy_icons"))
    gtk_widget_show(gtkconv->infopane_hbox);
    @@ -5827,7 +4555,7 @@
    g_signal_connect_swapped(G_OBJECT(pane), "focus",
    G_CALLBACK(gtk_widget_grab_focus),
    - gtkconv->entry);
    + gtkconv->editor);
    if (hidden)
    pidgin_conv_window_add_gtkconv(hidden_convwin, gtkconv);
    @@ -5837,7 +4565,7 @@
    if (generated_nick_colors == NULL) {
    GdkColor color;
    GdkRGBA rgba;
    - color = gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL];
    + color = gtk_widget_get_style(gtkconv->history)->base[GTK_STATE_NORMAL];
    rgba.red = color.red / 65535.0;
    rgba.green = color.green / 65535.0;
    rgba.blue = color.blue / 65535.0;
    @@ -5845,10 +4573,7 @@
    generated_nick_colors = generate_nick_colors(NICK_COLOR_GENERATE_COUNT, rgba);
    }
    - if(NULL == (gtkconv->nick_colors = pidgin_conversation_theme_get_nick_colors(gtkconv->theme)))
    - {
    - gtkconv->nick_colors = g_array_ref(generated_nick_colors);
    - }
    + gtkconv->nick_colors = g_array_ref(generated_nick_colors);
    }
    static void
    @@ -5949,8 +4674,7 @@
    }
    gtkconv->send_history = g_list_first(gtkconv->send_history);
    - g_list_foreach(gtkconv->send_history, (GFunc)g_free, NULL);
    - g_list_free(gtkconv->send_history);
    + g_list_free_full(gtkconv->send_history, g_free);
    if (gtkconv->attach_timer) {
    g_source_remove(gtkconv->attach_timer);
    @@ -5958,10 +4682,6 @@
    g_array_unref(gtkconv->nick_colors);
    - g_object_disconnect(G_OBJECT(gtkconv->theme), "any_signal::notify",
    - conv_variant_changed_cb, gtkconv, NULL);
    - g_object_unref(gtkconv->theme);
    -
    g_free(gtkconv);
    }
    @@ -6096,226 +4816,6 @@
    return NULL;
    }
    -#if 0
    -static void pidgin_conv_calculate_newday(PidginConversation *gtkconv, time_t mtime)
    -{
    - struct tm *tm = localtime(&mtime);
    -
    - tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
    - tm->tm_mday++;
    -
    - gtkconv->newday = mktime(tm);
    -}
    -
    -/* Detect string direction and encapsulate the string in RLE/LRE/PDF unicode characters
    - str - pointer to string (string is re-allocated and the pointer updated) */
    -static void
    -str_embed_direction_chars(char **str)
    -{
    - char pre_str[4];
    - char post_str[10];
    - char *ret;
    -
    - if (PANGO_DIRECTION_RTL == pango_find_base_dir(*str, -1))
    - {
    - sprintf(pre_str, "%c%c%c",
    - 0xE2, 0x80, 0xAB); /* RLE */
    - sprintf(post_str, "%c%c%c%c%c%c%c%c%c",
    - 0xE2, 0x80, 0xAC, /* PDF */
    - 0xE2, 0x80, 0x8E, /* LRM */
    - 0xE2, 0x80, 0xAC); /* PDF */
    - }
    - else
    - {
    - sprintf(pre_str, "%c%c%c",
    - 0xE2, 0x80, 0xAA); /* LRE */
    - sprintf(post_str, "%c%c%c%c%c%c%c%c%c",
    - 0xE2, 0x80, 0xAC, /* PDF */
    - 0xE2, 0x80, 0x8F, /* RLM */
    - 0xE2, 0x80, 0xAC); /* PDF */
    - }
    -
    - ret = g_strconcat(pre_str, *str, post_str, NULL);
    -
    - g_free(*str);
    - *str = ret;
    -}
    -#endif
    -
    -static char *
    -replace_message_tokens(
    - const char *text,
    - PurpleConversation *conv,
    - const char *name, /* author */
    - const char *alias, /* author's alias */
    - const char *message,
    - PurpleMessageFlags flags,
    - time_t mtime)
    -{
    - GString *str;
    - const char *cur = text;
    - const char *prev = cur;
    - struct tm *tm = NULL;
    -
    - if (text == NULL || *text == '\0')
    - return NULL;
    -
    - str = g_string_new(NULL);
    - while ((cur = strchr(cur, '%'))) {
    - const char *replace = NULL;
    - const char *fin = NULL;
    - gpointer freeval = NULL;
    -
    - if (g_str_has_prefix(cur, "%message%")) {
    - replace = message;
    -
    - } else if (g_str_has_prefix(cur, "%messageClasses%")) {
    - char *user;
    - GString *classes = g_string_new(NULL);
    -#define ADD_CLASS(f, class) \
    - if (flags & f) \
    - g_string_append(classes, class);
    - ADD_CLASS(PURPLE_MESSAGE_SEND, "outgoing ");
    - ADD_CLASS(PURPLE_MESSAGE_RECV, "incoming ");
    - ADD_CLASS(PURPLE_MESSAGE_SYSTEM, "event ");
    - ADD_CLASS(PURPLE_MESSAGE_AUTO_RESP, "autoreply ");
    - ADD_CLASS(PURPLE_MESSAGE_DELAYED, "history ");
    - ADD_CLASS(PURPLE_MESSAGE_NICK, "mention ");
    -#undef ADD_CLASS
    - user = get_class_for_user(name);
    - g_string_append(classes, user);
    - g_free(user);
    -
    - replace = freeval = g_string_free(classes, FALSE);
    -
    - } else if (g_str_has_prefix(cur, "%time")) {
    - const char *tmp = cur + strlen("%time");
    -
    - if (*tmp == '{') {
    - char *end;
    - tmp++;
    - end = strstr(tmp, "}%");
    - if (!end) /* Invalid string */
    - continue;
    - if (!tm)
    - tm = localtime(&mtime);
    - replace = freeval = purple_uts35_to_str(tmp, end - tmp, tm);
    - fin = end + 1;
    - } else {
    - if (!tm)
    - tm = localtime(&mtime);
    -
    - replace = purple_utf8_strftime("%X", tm);
    - }
    -
    - } else if (g_str_has_prefix(cur, "%shortTime%")) {
    - if (!tm)
    - tm = localtime(&mtime);
    -
    - replace = purple_utf8_strftime("%H:%M", tm);
    -
    - } else if (g_str_has_prefix(cur, "%userIconPath%")) {
    - if (flags & PURPLE_MESSAGE_SEND) {
    - if (purple_account_get_bool(purple_conversation_get_account(conv), "use-global-buddyicon", TRUE)) {
    - replace = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/accounts/buddyicon");
    - } else {
    - PurpleImage *img = purple_buddy_icons_find_account_icon(purple_conversation_get_account(conv));
    - /* XXX: this may be NULL */
    - replace = purple_image_get_path(img);
    - }
    - if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
    - replace = freeval = g_build_filename("Outgoing", "buddy_icon.png", NULL);
    - }
    - } else if (flags & PURPLE_MESSAGE_RECV) {
    - PurpleBuddyIcon *icon = purple_im_conversation_get_icon(PURPLE_IM_CONVERSATION(conv));
    - if (icon)
    - replace = purple_buddy_icon_get_full_path(icon);
    - if (replace == NULL || !g_file_test(replace, G_FILE_TEST_EXISTS)) {
    - replace = freeval = g_build_filename("Incoming", "buddy_icon.png", NULL);
    - }
    - }
    -
    - } else if (g_str_has_prefix(cur, "%senderScreenName%")) {
    - replace = name;
    -
    - } else if (g_str_has_prefix(cur, "%sender%")) {
    - replace = alias;
    -
    - } else if (g_str_has_prefix(cur, "%senderColor%")) {
    - const GdkRGBA *color = get_nick_color(PIDGIN_CONVERSATION(conv), name);
    - replace = freeval = g_strdup_printf("#%02x%02x%02x",
    - (unsigned int)(color->red * 255),
    - (unsigned int)(color->green * 255),
    - (unsigned int)(color->blue * 255));
    -
    - } else if (g_str_has_prefix(cur, "%service%")) {
    - replace = purple_account_get_protocol_name(purple_conversation_get_account(conv));
    -
    - } else if (g_str_has_prefix(cur, "%messageDirection%")) {
    - replace = purple_markup_is_rtl(message) ? "rtl" : "ltr";
    -
    - } else if (g_str_has_prefix(cur, "%status%")) {
    - GString *classes = g_string_new(NULL);
    -
    - if (flags & PURPLE_MESSAGE_ERROR)
    - g_string_append(classes, "error ");
    -
    - replace = freeval = g_string_free(classes, FALSE);
    -
    - } else if (g_str_has_prefix(cur, "%variant%")) {
    - replace = pidgin_conversation_theme_get_variant(PIDGIN_CONVERSATION(conv)->theme);
    - replace = freeval = g_strdup(replace);
    - purple_util_chrreplace(freeval, ' ', '_');
    -
    - } else {
    - cur++;
    - continue;
    - }
    -
    - /* Here we have a replacement to make */
    - g_string_append_len(str, prev, cur - prev);
    - if (replace)
    - g_string_append(str, replace);
    - g_free(freeval);
    - replace = freeval = NULL;
    -
    - /* And update the pointers */
    - if (fin) {
    - prev = cur = fin + 1;
    - } else {
    - prev = cur = strchr(cur + 1, '%') + 1;
    - }
    -
    - }
    -
    - /* And wrap it up */
    - g_string_append(str, prev);
    -
    - return g_string_free(str, FALSE);
    -}
    -
    -static gboolean
    -pidgin_conv_write_smiley(GString *out, PurpleSmiley *smiley,
    - PurpleConversation *conv, gpointer _proto_name)
    -{
    - gchar *escaped_shortcut;
    - gchar *uri;
    -
    - escaped_shortcut = g_markup_escape_text(
    - purple_smiley_get_shortcut(smiley), -1);
    - uri = purple_image_store_get_uri(PURPLE_IMAGE(smiley));
    -
    - g_string_append_printf(out,
    - "<img class=\"emoticon\" alt=\"%s\" title=\"%s\" "
    - "src=\"%s\" />", escaped_shortcut,
    - escaped_shortcut, uri);
    -
    - g_free(uri);
    - g_free(escaped_shortcut);
    -
    - return TRUE;
    -}
    -
    static gboolean
    writing_msg(PurpleConversation *conv, PurpleMessage *msg, gpointer _unused)
    {
    @@ -6342,39 +4842,19 @@
    static void
    pidgin_conv_write_conv(PurpleConversation *conv, PurpleMessage *pmsg)
    {
    + PidginMessage *pidgin_msg = NULL;
    + PurpleMessageFlags flags;
    PidginConversation *gtkconv;
    PurpleConnection *gc;
    PurpleAccount *account;
    -#if 0
    - int gtk_font_options = 0;
    - int gtk_font_options_all = 0;
    - char buf2[BUF_LONG];
    - gboolean show_date;
    - char *mdate;
    - char *str;
    - char *with_font_tag;
    - char *sml_attrib = NULL;
    - size_t length;
    -#endif
    - char *displaying;
    gboolean plugin_return;
    -#if 0
    - gboolean is_rtl_message = FALSE;
    -#endif
    -
    - const char *message_html;
    - char *msg_tokenized;
    - char *escape;
    - char *script;
    - char *smileyed;
    - PurpleMessageFlags flags, old_flags;
    - const char *func = "appendMessage";
    g_return_if_fail(conv != NULL);
    gtkconv = PIDGIN_CONVERSATION(conv);
    g_return_if_fail(gtkconv != NULL);
    flags = purple_message_get_flags(pmsg);
    +#if 0
    if (gtkconv->attach_timer) {
    /* We are currently in the process of filling up the buffer with the message
    * history of the conversation. So we do not need to add the message here.
    @@ -6391,280 +4871,27 @@
    if (flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV))
    pidgin_conv_switch_active_conversation(conv);
    }
    +#endif
    account = purple_conversation_get_account(conv);
    g_return_if_fail(account != NULL);
    gc = purple_account_get_connection(account);
    g_return_if_fail(gc != NULL || !(flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)));
    - /* Make sure URLs are clickable */
    - if(flags & PURPLE_MESSAGE_NO_LINKIFY)
    - displaying = g_strdup(purple_message_get_contents(pmsg));
    - else
    - displaying = purple_markup_linkify(purple_message_get_contents(pmsg));
    -
    plugin_return = GPOINTER_TO_INT(purple_signal_emit_return_1(
    pidgin_conversations_get_handle(),
    (PURPLE_IS_IM_CONVERSATION(conv) ? "displaying-im-msg" : "displaying-chat-msg"),
    conv, pmsg));
    if (plugin_return)
    {
    - g_free(displaying);
    return;
    }
    -#if 0
    - length = strlen(displaying) + 1;
    -#endif
    -
    - old_flags = gtkconv->last_flags;
    - if ((flags & PURPLE_MESSAGE_SEND) && (old_flags & PURPLE_MESSAGE_SEND)) {
    - message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_NEXT_CONTENT);
    - func = "appendNextMessage";
    -
    - } else if (flags & PURPLE_MESSAGE_SEND) {
    - message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_OUTGOING_CONTENT);
    -
    - } else if ((flags & PURPLE_MESSAGE_RECV) && (old_flags & PURPLE_MESSAGE_RECV)) {
    - GList *history = purple_conversation_get_message_history(gtkconv->last_conversed);
    - PurpleMessage *last_msg = history ? history->data : NULL;
    -
    - g_assert(history != NULL);
    - g_assert(last_msg != NULL);
    -
    - /* If the senders are the same, use appendNextMessage */
    - if (purple_strequal(purple_message_get_author(last_msg), purple_message_get_author(pmsg))) {
    - message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_NEXT_CONTENT);
    - func = "appendNextMessage";
    - } else {
    - message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT);
    - }
    - } else if (flags & PURPLE_MESSAGE_RECV) {
    - message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_INCOMING_CONTENT);
    -
    - } else {
    - message_html = pidgin_conversation_theme_get_template(gtkconv->theme,
    - PIDGIN_CONVERSATION_THEME_TEMPLATE_STATUS);
    - }
    - gtkconv->last_flags = flags;
    - gtkconv->last_conversed = conv;
    -
    - if(purple_message_get_flags(pmsg) & PURPLE_MESSAGE_SYSTEM) {
    - smileyed = g_strdup(displaying);
    - } else {
    - smileyed = purple_smiley_parser_smileify(conv, displaying,
    - (flags & PURPLE_MESSAGE_RECV), pidgin_conv_write_smiley,
    - (gpointer)purple_account_get_protocol_name(account));
    - }
    - msg_tokenized = replace_message_tokens(message_html, conv,
    - purple_message_get_author(pmsg),
    - purple_message_get_author_alias(pmsg),
    - smileyed,
    - purple_message_get_flags(pmsg),
    - purple_message_get_time(pmsg));
    - escape = pidgin_webview_quote_js_string(msg_tokenized ? msg_tokenized : "");
    - script = g_strdup_printf("%s(%s)", func, escape);
    -
    - purple_debug_info("webkit", "JS: %s\n", script);
    - pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(gtkconv->webview), script);
    -
    - g_free(script);
    - g_free(smileyed);
    - g_free(msg_tokenized);
    - g_free(escape);
    -
    -#if 0
    - /* if the buffer is not empty add a <br> */
    - if (!pidgin_webview_is_empty(PIDGIN_WEBVIEW(gtkconv->webview)))
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), "<br />");
    -
    - /* First message in a conversation. */
    - if (gtkconv->newday == 0)
    - pidgin_conv_calculate_newday(gtkconv, mtime);
    -
    - /* Show the date on the first message in a new day, or if the message is
    - * older than 20 minutes. */
    - show_date = (mtime >= gtkconv->newday) || (time(NULL) > mtime + 20*60);
    -
    - mdate = purple_signal_emit_return_1(pidgin_conversations_get_handle(),
    - "conversation-timestamp",
    - conv, mtime, show_date);
    -
    - if (mdate == NULL)
    - {
    - struct tm *tm = localtime(&mtime);
    - const char *tmp;
    - if (show_date)
    - tmp = purple_date_format_long(tm);
    - else
    - tmp = purple_time_format(tm);
    - mdate = g_strdup_printf("(%s)", tmp);
    - }
    -
    - /* Bi-Directional support - set timestamp direction using unicode characters */
    - is_rtl_message = purple_markup_is_rtl(message);
    -
    - /* Handle plaintext messages with RTL text but no direction in the markup */
    - if (!is_rtl_message && pango_find_base_dir(message, -1) == PANGO_DIRECTION_RTL)
    - {
    - char *wrapped = g_strdup_printf("<SPAN style=\"direction:rtl;text-align:right;\">%s</SPAN>", displaying);
    -
    - g_free(displaying);
    - displaying = wrapped;
    -
    - length = strlen(displaying) + 1;
    - is_rtl_message = TRUE;
    - }
    -
    - /* Enforce direction only if message is RTL - doesn't effect LTR users */
    - if (is_rtl_message)
    - str_embed_direction_chars(&mdate);
    -
    - if (mtime >= gtkconv->newday)
    - pidgin_conv_calculate_newday(gtkconv, mtime);
    -
    - sml_attrib = g_strdup_printf("sml=\"%s\"", purple_account_get_protocol_name(account));
    -
    - gtk_font_options |= GTK_IMHTML_NO_COMMENTS;
    -
    - if ((flags & PURPLE_MESSAGE_RECV) &&
    - !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting"))
    - gtk_font_options |= GTK_IMHTML_NO_COLOURS | GTK_IMHTML_NO_FONTS | GTK_IMHTML_NO_SIZES | GTK_IMHTML_NO_FORMATTING;
    -
    - /* this is gonna crash one day, I can feel it. */
    - if (purple_protocols_find(purple_account_get_protocol_id(purple_conversation_get_account(conv)))->options &
    - OPT_PROTO_USE_POINTSIZE) {
    - gtk_font_options |= GTK_IMHTML_USE_POINTSIZE;
    - }
    -
    - /* TODO: These colors should not be hardcoded so log.c can use them */
    - if (flags & PURPLE_MESSAGE_RAW) {
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), message);
    - } else if (flags & PURPLE_MESSAGE_SYSTEM) {
    - g_snprintf(buf2, sizeof(buf2),
    - "<font %s><font size=\"2\"><span class='timestamp'>%s</span></font><b>%s</b></font>",
    - sml_attrib ? sml_attrib : "", mdate, displaying);
    -
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), buf2);
    -
    - } else if (flags & PURPLE_MESSAGE_ERROR) {
    - g_snprintf(buf2, sizeof(buf2),
    - "<font color=\"#ff0000\"><font %s><font size=\"2\"><span class='timestamp'>%s</span> </font><b>%s</b></font></font>",
    - sml_attrib ? sml_attrib : "", mdate, displaying);
    -
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), buf2);
    -
    - } else if (flags & PURPLE_MESSAGE_NO_LOG) {
    - g_snprintf(buf2, BUF_LONG,
    - "<b><font %s color=\"#777777\">%s</font></b>",
    - sml_attrib ? sml_attrib : "", displaying);
    -
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), buf2);
    - } else {
    - char *new_message = g_memdup(displaying, length);
    - char *alias_escaped = (alias ? g_markup_escape_text(alias, strlen(alias)) : g_strdup(""));
    - /* The initial offset is to deal with
    - * escaped entities making the string longer */
    - int tag_start_offset = 0;
    - const char *tagname = NULL;
    -
    - /* Enforce direction on alias */
    - if (is_rtl_message)
    - str_embed_direction_chars(&alias_escaped);
    -
    - str = g_malloc(1024);
    - if (TRUE) { /* XXX: reduce numer of indentations */
    - if (purple_message_meify(new_message, -1)) {
    - if (flags & PURPLE_MESSAGE_AUTO_RESP) {
    - g_snprintf(str, 1024, "%s ***%s", AUTO_RESPONSE, alias_escaped);
    - tag_start_offset += strlen(AUTO_RESPONSE) - 6 + 4;
    - } else {
    - g_snprintf(str, 1024, "***%s", alias_escaped);
    - tag_start_offset += 3;
    - }
    -
    - if (flags & PURPLE_MESSAGE_NICK)
    - tagname = "highlight-name";
    - else
    - tagname = "action-name";
    - } else {
    - if (flags & PURPLE_MESSAGE_AUTO_RESP) {
    - g_snprintf(str, 1024, "%s %s", alias_escaped, AUTO_RESPONSE);
    - tag_start_offset += strlen(AUTO_RESPONSE) - 6 + 1;
    - } else {
    - g_snprintf(str, 1024, "%s:", alias_escaped);
    -#if 0
    - tag_end_offset = 1;
    -#endif
    - }
    -
    - if (flags & PURPLE_MESSAGE_NICK) {
    - if (type == PURPLE_CONV_TYPE_IM) {
    - tagname = "highlight-name";
    - }
    - } else if (flags & PURPLE_MESSAGE_RECV) {
    - /* The tagname for chats is handled by get_buddy_tag */
    - if (type == PURPLE_CONV_TYPE_IM) {
    - tagname = "receive-name";
    - }
    - } else if (flags & PURPLE_MESSAGE_SEND) {
    - tagname = "send-name";
    - } else {
    - purple_debug_error("gtkconv", "message missing flags\n");
    - }
    - }
    - }
    -
    - g_free(alias_escaped);
    -
    - /* TODO WEBKIT */
    -#if 0
    - if (tagname)
    - tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tagname);
    - else
    - tag = get_buddy_tag(conv, name, flags, TRUE);
    -
    - if (GTK_IMHTML(gtkconv->imhtml)->show_comments) {
    - {
    - /* The color for the timestamp has to be set in the font-tags, unfortunately.
    - * Applying the nick-tag to timestamps would work, but that can make it
    - * bold. I thought applying the "comment" tag again, which has "weight" set
    - * to PANGO_WEIGHT_NORMAL, would remove the boldness. But it doesn't. So
    - * this will have to do. I don't terribly like it. -- sadrul */
    - /* const char *color = get_text_tag_color(tag); */
    - g_snprintf(buf2, BUF_LONG, "<FONT %s%s%s SIZE=\"2\"><!--%s --></FONT>",
    - color ? "COLOR=\"" : "", color ? color : "", color ? "\"" : "", mdate);
    - pidgin_webview_append_html (PIDGIN_WEBVIEW(gtkconv->webview), buf2);
    - }
    -#endif /* if 0 */
    - g_snprintf(buf2, BUF_LONG, "<font %s>%s</font> ", sml_attrib ? sml_attrib : "", str);
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), buf2);
    -
    - g_free(str);
    -
    - if (gc) {
    - char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : "");
    - char *post = "</font>";
    - with_font_tag = g_strdup_printf("%s%s%s", pre, new_message, post);
    - g_free(pre);
    - } else
    - with_font_tag = g_memdup(new_message, length);
    -
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview),
    - with_font_tag);
    -
    - g_free(with_font_tag);
    - g_free(new_message);
    - }
    -
    - g_free(mdate);
    - g_free(sml_attrib);
    -
    -#endif
    +
    + pidgin_msg = pidgin_message_new(pmsg);
    + talkatu_history_buffer_write_message(
    + TALKATU_HISTORY_BUFFER(gtkconv->history_buffer),
    + TALKATU_MESSAGE(pidgin_msg)
    + );
    /* Tab highlighting stuff */
    if (!(flags & PURPLE_MESSAGE_SEND) && !pidgin_conv_has_focus(conv))
    @@ -6727,7 +4954,6 @@
    purple_signal_emit(pidgin_conversations_get_handle(),
    (PURPLE_IS_IM_CONVERSATION(conv) ? "displayed-im-msg" : "displayed-chat-msg"),
    conv, pmsg);
    - g_free(displaying);
    update_typing_message(gtkconv, NULL);
    }
    @@ -6921,8 +5147,7 @@
    purple_chat_user_set_ui_data(chatuser, NULL);
    }
    - if (chatuser)
    - add_chat_user_common(chat, chatuser, NULL);
    + add_chat_user_common(chat, chatuser, NULL);
    }
    gboolean
    @@ -6942,14 +5167,6 @@
    return FALSE;
    }
    -static void
    -pidgin_conv_send_confirm(PurpleConversation *conv, const char *message)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    -
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(gtkconv->webview), message);
    -}
    -
    /*
    * Makes sure all the menu items and all the buttons are hidden/shown and
    * sensitive/insensitive. This is called after changing tabs and when an
    @@ -6963,7 +5180,7 @@
    PurpleConnection *gc;
    PurpleProtocol *protocol = NULL;
    GdkPixbuf *window_icon = NULL;
    - PidginWebViewButtons buttons;
    +// PidginWebViewButtons buttons;
    PurpleAccount *account;
    win = pidgin_conv_get_window(gtkconv);
    @@ -7052,6 +5269,7 @@
    PurpleConnectionFlags features = purple_conversation_get_features(conv);
    /* Account is online */
    /* Deal with the toolbar */
    +#if 0
    if (features & PURPLE_CONNECTION_FLAG_HTML)
    {
    buttons = PIDGIN_WEBVIEW_ALL; /* Everything on */
    @@ -7063,7 +5281,7 @@
    buttons &= ~PIDGIN_WEBVIEW_SHRINK;
    }
    if (features & PURPLE_CONNECTION_FLAG_NO_URLDESC)
    - buttons &= ~PIDGIN_WEBVIEW_LINKDESC;
    + buttons &= ~PIDGIN_WEBVIEW_LINKDESC
    } else {
    buttons = PIDGIN_WEBVIEW_SMILEY | PIDGIN_WEBVIEW_IMAGE;
    }
    @@ -7077,12 +5295,13 @@
    buttons &= ~PIDGIN_WEBVIEW_CUSTOM_SMILEY;
    pidgin_webview_set_format_functions(PIDGIN_WEBVIEW(gtkconv->entry), buttons);
    +#endif
    /* Deal with menu items */
    gtk_action_set_sensitive(win->menu->view_log, TRUE);
    gtk_action_set_sensitive(win->menu->add_pounce, TRUE);
    - gtk_action_set_sensitive(win->menu->get_info, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, get_info)));
    - gtk_action_set_sensitive(win->menu->invite, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, invite)));
    + gtk_action_set_sensitive(win->menu->get_info, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, get_info)));
    + gtk_action_set_sensitive(win->menu->invite, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, invite)));
    gtk_action_set_sensitive(win->menu->insert_link, (features & PURPLE_CONNECTION_FLAG_HTML));
    gtk_action_set_sensitive(win->menu->insert_image, !(features & PURPLE_CONNECTION_FLAG_NO_IMAGES));
    @@ -7097,8 +5316,8 @@
    can_send_file = TRUE;
    }
    - gtk_action_set_sensitive(win->menu->add, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, add_buddy)));
    - gtk_action_set_sensitive(win->menu->remove, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER_IFACE, remove_buddy)));
    + gtk_action_set_sensitive(win->menu->add, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, add_buddy)));
    + gtk_action_set_sensitive(win->menu->remove, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, SERVER, remove_buddy)));
    gtk_action_set_sensitive(win->menu->send_file, can_send_file);
    gtk_action_set_sensitive(win->menu->get_attention, (PURPLE_IS_PROTOCOL_ATTENTION(protocol)));
    gtk_action_set_sensitive(win->menu->alias,
    @@ -7107,8 +5326,8 @@
    }
    else if (PURPLE_IS_CHAT_CONVERSATION(conv))
    {
    - gtk_action_set_sensitive(win->menu->add, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join)));
    - gtk_action_set_sensitive(win->menu->remove, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, join)));
    + gtk_action_set_sensitive(win->menu->add, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, join)));
    + gtk_action_set_sensitive(win->menu->remove, (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, join)));
    gtk_action_set_sensitive(win->menu->alias,
    (account != NULL) &&
    (purple_blist_find_chat(account, purple_conversation_get_name(conv)) != NULL));
    @@ -7433,7 +5652,7 @@
    pidgin_conv_chat_update_user, /* chat_update_user */
    pidgin_conv_present_conversation, /* present */
    pidgin_conv_has_focus, /* has_focus */
    - pidgin_conv_send_confirm, /* send_confirm */
    + NULL, /* send_confirm */
    NULL,
    NULL,
    NULL,
    @@ -7762,8 +5981,7 @@
    gtkconv = PIDGIN_CONVERSATION(conv);
    - pidgin_webview_set_spellcheck(PIDGIN_WEBVIEW(gtkconv->entry),
    - (gboolean)GPOINTER_TO_INT(value));
    +# warning toggle spell checking when talkatu #60 is done.
    }
    }
    @@ -7794,6 +6012,7 @@
    PurpleConversation *conv;
    PidginConversation *gtkconv;
    PidginConvWindow *win;
    + gboolean visible = (gboolean)GPOINTER_TO_INT(value);
    for (l = purple_conversations_get_all(); l != NULL; l = l->next)
    {
    @@ -7807,14 +6026,10 @@
    gtk_toggle_action_set_active(
    GTK_TOGGLE_ACTION(win->menu->show_formatting_toolbar),
    - (gboolean)GPOINTER_TO_INT(value));
    -
    - if ((gboolean)GPOINTER_TO_INT(value))
    - pidgin_webview_show_toolbar(PIDGIN_WEBVIEW(gtkconv->entry));
    - else
    - pidgin_webview_hide_toolbar(PIDGIN_WEBVIEW(gtkconv->entry));
    -
    - resize_webview_cb(gtkconv);
    + visible
    + );
    +
    + talkatu_editor_set_toolbar_visible(TALKATU_EDITOR(gtkconv->editor), visible);
    }
    }
    @@ -8165,10 +6380,10 @@
    /* Compare two PurpleMessages, according to time in ascending order. */
    static int
    -message_compare(gconstpointer p1, gconstpointer p2)
    -{
    - const PurpleMessage *m1 = p1, *m2 = p2;
    - return (purple_message_get_time(m1) > purple_message_get_time(m2));
    +message_compare(PurpleMessage *m1, PurpleMessage *m2)
    +{
    + guint64 t1 = purple_message_get_time(m1), t2 = purple_message_get_time(m2);
    + return (t1 > t2) - (t1 < t2);
    }
    /* Adds some message history to the gtkconv. This happens in a idle-callback. */
    @@ -8176,19 +6391,16 @@
    add_message_history_to_gtkconv(gpointer data)
    {
    PidginConversation *gtkconv = data;
    - PidginWebView *webview = PIDGIN_WEBVIEW(gtkconv->webview);
    int count = 0;
    int timer = gtkconv->attach_timer;
    - time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->entry), "attach-start-time"));
    + time_t when = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtkconv->editor), "attach-start-time"));
    gboolean im = (PURPLE_IS_IM_CONVERSATION(gtkconv->active_conv));
    gtkconv->attach_timer = 0;
    while (gtkconv->attach_current && count < ADD_MESSAGE_HISTORY_AT_ONCE) {
    PurpleMessage *msg = gtkconv->attach_current->data;
    if (!im && when && (guint64)when < purple_message_get_time(msg)) {
    - pidgin_webview_append_html(webview, "<BR><HR>");
    - pidgin_webview_scroll_to_end(webview, TRUE);
    - g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
    + g_object_set_data(G_OBJECT(gtkconv->editor), "attach-start-time", NULL);
    }
    /* XXX: should it be gtkconv->active_conv? */
    pidgin_conv_write_conv(gtkconv->active_conv, msg);
    @@ -8218,18 +6430,16 @@
    msgs = g_list_prepend(msgs, msg);
    }
    }
    - msgs = g_list_sort(msgs, message_compare);
    + msgs = g_list_sort(msgs, (GCompareFunc)message_compare);
    for (; msgs; msgs = g_list_delete_link(msgs, msgs)) {
    PurpleMessage *msg = msgs->data;
    /* XXX: see above - should it be active_conv? */
    pidgin_conv_write_conv(gtkconv->active_conv, msg);
    }
    - pidgin_webview_append_html(webview, "<BR><HR>");
    - pidgin_webview_scroll_to_end(webview, TRUE);
    - g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
    - }
    -
    - g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time", NULL);
    + g_object_set_data(G_OBJECT(gtkconv->editor), "attach-start-time", NULL);
    + }
    +
    + g_object_set_data(G_OBJECT(gtkconv->editor), "attach-start-time", NULL);
    purple_signal_emit(pidgin_conversations_get_handle(),
    "conversation-displayed", gtkconv);
    return FALSE;
    @@ -8287,14 +6497,14 @@
    pidgin_conv_attach(convs->data);
    list = g_list_concat(list, g_list_copy(purple_conversation_get_message_history(convs->data)));
    }
    - list = g_list_sort(list, message_compare);
    + list = g_list_sort(list, (GCompareFunc)message_compare);
    gtkconv->attach_current = list;
    list = g_list_last(list);
    } else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
    gtkconv->attach_current = g_list_last(list);
    }
    - g_object_set_data(G_OBJECT(gtkconv->entry), "attach-start-time",
    + g_object_set_data(G_OBJECT(gtkconv->editor), "attach-start-time",
    GINT_TO_POINTER(purple_message_get_time(list->data)));
    gtkconv->attach_timer = g_idle_add(add_message_history_to_gtkconv, gtkconv);
    } else {
    @@ -8314,12 +6524,6 @@
    return TRUE;
    }
    -PurpleTheme *
    -pidgin_conversations_get_default_theme(void)
    -{
    - return default_conv_theme;
    -}
    -
    void *
    pidgin_conversations_get_handle(void)
    {
    @@ -8336,14 +6540,12 @@
    {
    void *handle = pidgin_conversations_get_handle();
    void *blist_handle = purple_blist_get_handle();
    - char *theme_dir;
    e2ee_stock = g_hash_table_new_full(g_str_hash, g_str_equal,
    g_free, g_object_unref);
    /* Conversations */
    purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations");
    - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/themes");
    purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", TRUE);
    purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/close_on_tabs", TRUE);
    purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold", FALSE);
    @@ -8594,12 +6796,6 @@
    PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
    purple_signal_connect(purple_conversations_get_handle(), "wrote-chat-msg", handle,
    PURPLE_CALLBACK(wrote_msg_update_unseen_cb), NULL);
    -
    - purple_theme_manager_register_type(g_object_new(PIDGIN_TYPE_CONV_THEME_LOADER, "type", "conversation", NULL));
    - theme_dir = g_build_filename(PURPLE_DATADIR, "pidgin", "theme", NULL);
    - default_conv_theme = purple_theme_manager_load_theme(theme_dir, "conversation");
    - g_free(theme_dir);
    -
    }
    static void
    @@ -8616,9 +6812,9 @@
    pidgin_conversations_set_tab_colors(void)
    {
    /* Set default tab colors */
    - GString *str = g_string_new(NULL);
    - GtkSettings *settings = gtk_settings_get_default();
    - GtkStyle *parent = gtk_rc_get_style_by_paths(settings, "tab-container.tab-label*", NULL, G_TYPE_NONE), *now;
    + GString *str;
    + GtkSettings *settings;
    + GtkStyle *parent, *now;
    struct {
    const char *stylename;
    const char *labelname;
    @@ -8638,6 +6834,11 @@
    return;
    }
    + str = g_string_new(NULL);
    + settings = gtk_settings_get_default();
    + parent = gtk_rc_get_style_by_paths(settings, "tab-container.tab-label*",
    + NULL, G_TYPE_NONE);
    +
    for (iter = 0; styles[iter].stylename; iter++) {
    now = gtk_rc_get_style_by_paths(settings, styles[iter].labelname, NULL, G_TYPE_NONE);
    if (parent == now ||
    @@ -8960,11 +7161,13 @@
    /* Grab the pointer */
    gtk_grab_add(gtkwin->notebook);
    device = gdk_event_get_device(event);
    - if (!gdk_display_device_is_grabbed(gdk_device_get_display(device), device))
    - gdk_device_grab(device, gtk_widget_get_window(gtkwin->notebook),
    - GDK_OWNERSHIP_WINDOW, FALSE,
    - GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
    - cursor, gdk_event_get_time(event));
    + if (!gdk_display_device_is_grabbed(gdk_device_get_display(device),
    + device)) {
    + gdk_seat_grab(gdk_event_get_seat(event),
    + gtk_widget_get_window(gtkwin->notebook),
    + GDK_SEAT_CAPABILITY_ALL_POINTING, FALSE, cursor, event,
    + NULL, NULL);
    + }
    }
    static gboolean
    @@ -9242,7 +7445,7 @@
    device = gdk_event_get_device((GdkEvent *)e);
    if (gdk_display_device_is_grabbed(gdk_device_get_display(device), device)) {
    - gdk_device_ungrab(device, gdk_event_get_time((GdkEvent *)e));
    + gdk_seat_ungrab(gdk_event_get_seat((GdkEvent *)e));
    gtk_grab_remove(widget);
    }
    @@ -9337,7 +7540,7 @@
    }
    }
    - gtk_widget_grab_focus(active_gtkconv->entry);
    + gtk_widget_grab_focus(active_gtkconv->editor);
    return TRUE;
    }
    @@ -9540,7 +7743,7 @@
    g_signal_handlers_disconnect_matched(G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
    0, 0, NULL, NULL, gtkconv);
    gtk_widget_show(gtkconv->infopane);
    - gtk_widget_grab_focus(gtkconv->entry);
    + gtk_widget_grab_focus(gtkconv->editor);
    gtk_widget_destroy(entry);
    }
    @@ -9622,7 +7825,7 @@
    gc = purple_conversation_get_connection(conv);
    if (gc != NULL)
    protocol = purple_connection_get_protocol(gc);
    - if (protocol && !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, set_topic))
    + if (protocol && !PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, set_topic))
    /* This protocol doesn't support setting the chat room topic */
    return FALSE;
    @@ -9874,10 +8077,6 @@
    pos = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/tab_side");
    -#if 0
    - gtk_notebook_set_tab_hborder(GTK_NOTEBOOK(win->notebook), 0);
    - gtk_notebook_set_tab_vborder(GTK_NOTEBOOK(win->notebook), 0);
    -#endif
    gtk_notebook_set_tab_pos(GTK_NOTEBOOK(win->notebook), pos);
    gtk_notebook_set_scrollable(GTK_NOTEBOOK(win->notebook), TRUE);
    gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
    @@ -10164,7 +8363,7 @@
    focus_gtkconv = g_list_nth_data(pidgin_conv_window_get_gtkconvs(win),
    gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook)));
    - gtk_widget_grab_focus(focus_gtkconv->entry);
    + gtk_widget_grab_focus(focus_gtkconv->editor);
    if (pidgin_conv_window_get_gtkconv_count(win) == 1)
    update_send_to_selection(win);
    --- a/pidgin/gtkconv.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkconv.h Tue Oct 08 21:48:28 2019 -0500
    @@ -76,7 +76,6 @@
    #include "pidgin.h"
    #include "conversation.h"
    #include "gtkconvwin.h"
    -#include "gtkconv-theme.h"
    /**************************************************************************
    * Structures
    @@ -104,18 +103,13 @@
    GtkWidget *tabby;
    GtkWidget *menu_tabby;
    - PidginConvTheme *theme;
    GArray *nick_colors;
    PurpleMessageFlags last_flags;
    - GtkWidget *webview;
    + GtkTextBuffer *history_buffer;
    + GtkWidget *history;
    +
    + GtkWidget *editor;
    GtkWidget *entry;
    - gboolean auto_resize; /* this is set to TRUE if the conversation
    - * is being resized by a non-user-initiated
    - * event, such as the buddy icon appearing
    - */
    - gboolean entry_growing; /* True if the size of the entry was set
    - * automatically by typing too much to fit
    - * in one line */
    GtkWidget *close; /* "x" on the tab */
    GtkWidget *icon;
    @@ -176,15 +170,6 @@
    PurpleConversationUiOps *pidgin_conversations_get_conv_ui_ops(void);
    /**
    - * pidgin_conversations_get_default_theme:
    - *
    - * Returns the default theme for GTK+ conversations.
    - *
    - * Returns: (transfer none): The default GTK+ conversation theme.
    - */
    -PurpleTheme *pidgin_conversations_get_default_theme(void);
    -
    -/**
    * pidgin_conv_update_buddy_icon:
    * @im: The IM conversation.
    *
    --- a/pidgin/gtkdialogs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkdialogs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#define _PIDGIN_GTKDIALOGS_C_
    #include <talkatu.h>
    @@ -166,6 +165,7 @@
    GList *plugins, *l = NULL;
    PurplePlugin *plugin = NULL;
    PurplePluginInfo *info;
    + GPluginPluginInfo *ginfo;
    PurplePluginExtraCb extra_cb;
    char *title = g_strdup_printf(_("%s Plugin Information"), PIDGIN_NAME);
    char *pname = NULL, *authors, *pauthors, *pextra;
    @@ -184,10 +184,12 @@
    for(l = plugins; l; l = l->next) {
    plugin = PURPLE_PLUGIN(l->data);
    info = purple_plugin_get_info(plugin);
    + ginfo = GPLUGIN_PLUGIN_INFO(info);
    extra_cb = purple_plugin_info_get_extra_cb(info);
    - pname = g_markup_escape_text(purple_plugin_info_get_name(info), -1);
    - authorlist = purple_plugin_info_get_authors(info);
    + pname = g_markup_escape_text(
    + gplugin_plugin_info_get_name(ginfo), -1);
    + authorlist = gplugin_plugin_info_get_authors(ginfo);
    if (authorlist) {
    authors = g_strjoinv(", ", (gchar **)authorlist);
    @@ -202,10 +204,10 @@
    else
    pauthors = NULL;
    - pver = purple_plugin_info_get_version(info);
    - plicense = purple_plugin_info_get_license_id(info);
    - pwebsite = purple_plugin_info_get_website(info);
    - pid = purple_plugin_info_get_id(info);
    + pver = gplugin_plugin_info_get_version(ginfo);
    + plicense = gplugin_plugin_info_get_license_id(ginfo);
    + pwebsite = gplugin_plugin_info_get_website(ginfo);
    + pid = gplugin_plugin_info_get_id(ginfo);
    ploadable = !purple_plugin_info_get_error(info);
    ploaded = purple_plugin_is_loaded(plugin);
    @@ -215,9 +217,11 @@
    pextra = NULL;
    g_string_append_printf(str, "<dt>%s</dt><dd>", pname);
    - if (pauthors)
    + if (pauthors) {
    g_string_append_printf(str, "<b>%s:</b> %s<br/>",
    - (n_authors > 1 ? "Authors" : "Author"), pauthors ? pauthors : "");
    + (n_authors > 1 ? "Authors" : "Author"),
    + pauthors);
    + }
    g_string_append_printf(str,
    "<b>Version:</b> %s<br/>"
    "<b>License:</b> %s<br/>"
    @@ -310,14 +314,12 @@
    purple_request_field_set_required(field, TRUE);
    purple_request_field_group_add_field(group, field);
    - purple_request_fields(purple_blist_get_buddy_list(), _("New Instant Message"),
    - NULL,
    - _("Please enter the username or alias of the person "
    - "you would like to IM."),
    - fields,
    - _("OK"), G_CALLBACK(pidgin_dialogs_im_cb),
    - _("Cancel"), NULL,
    - NULL, NULL);
    + purple_request_fields(
    + purple_blist_get_default(), _("New Instant Message"), NULL,
    + _("Please enter the username or alias of the person "
    + "you would like to IM."),
    + fields, _("OK"), G_CALLBACK(pidgin_dialogs_im_cb), _("Cancel"),
    + NULL, NULL, NULL);
    }
    void
    @@ -451,14 +453,12 @@
    purple_request_field_set_required(field, TRUE);
    purple_request_field_group_add_field(group, field);
    - purple_request_fields(purple_blist_get_buddy_list(), _("Get User Info"),
    - NULL,
    - _("Please enter the username or alias of the person "
    - "whose info you would like to view."),
    - fields,
    - _("OK"), G_CALLBACK(pidgin_dialogs_info_cb),
    - _("Cancel"), NULL,
    - NULL, NULL);
    + purple_request_fields(
    + purple_blist_get_default(), _("Get User Info"), NULL,
    + _("Please enter the username or alias of the person "
    + "whose info you would like to view."),
    + fields, _("OK"), G_CALLBACK(pidgin_dialogs_info_cb),
    + _("Cancel"), NULL, NULL, NULL);
    }
    static void
    @@ -542,14 +542,12 @@
    purple_request_field_set_required(field, TRUE);
    purple_request_field_group_add_field(group, field);
    - purple_request_fields(purple_blist_get_buddy_list(), _("View User Log"),
    - NULL,
    - _("Please enter the username or alias of the person "
    - "whose log you would like to view."),
    - fields,
    - _("OK"), G_CALLBACK(pidgin_dialogs_log_cb),
    - _("Cancel"), NULL,
    - NULL, NULL);
    + purple_request_fields(
    + purple_blist_get_default(), _("View User Log"), NULL,
    + _("Please enter the username or alias of the person "
    + "whose log you would like to view."),
    + fields, _("OK"), G_CALLBACK(pidgin_dialogs_log_cb), _("Cancel"),
    + NULL, NULL, NULL);
    }
    static void
    --- a/pidgin/gtkdocklet.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkdocklet.c Tue Oct 08 21:48:28 2019 -0500
    @@ -104,9 +104,7 @@
    if (newflag & PIDGIN_DOCKLET_CONNECTING)
    icon_name = PIDGIN_STOCK_TRAY_CONNECT;
    - if (icon_name) {
    - gtk_status_icon_set_from_icon_name(docklet, icon_name);
    - }
    + gtk_status_icon_set_from_icon_name(docklet, icon_name);
    }
    static GList *
    @@ -243,7 +241,7 @@
    while(c != NULL) {
    PurpleConnection *gc = c->data;
    PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    - if (protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT_IFACE, info))
    + if (protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info))
    return TRUE;
    c = c->next;
    }
    @@ -279,7 +277,7 @@
    docklet_signed_on_cb(PurpleConnection *gc)
    {
    if (!enable_join_chat) {
    - if (PURPLE_PROTOCOL_IMPLEMENTS(purple_connection_get_protocol(gc), CHAT_IFACE, info))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(purple_connection_get_protocol(gc), CHAT, info))
    enable_join_chat = TRUE;
    }
    docklet_update_status();
    @@ -289,7 +287,7 @@
    docklet_signed_off_cb(PurpleConnection *gc)
    {
    if (enable_join_chat) {
    - if (PURPLE_PROTOCOL_IMPLEMENTS(purple_connection_get_protocol(gc), CHAT_IFACE, info))
    + if (PURPLE_PROTOCOL_IMPLEMENTS(purple_connection_get_protocol(gc), CHAT, info))
    enable_join_chat = online_account_supports_chat();
    }
    docklet_update_status();
    @@ -661,8 +659,9 @@
    if (!purple_plugin_info_get_actions_cb(info))
    continue;
    - menuitem =
    - gtk_image_menu_item_new_with_label(_(purple_plugin_info_get_name(info)));
    + menuitem = gtk_image_menu_item_new_with_label(
    + _(gplugin_plugin_info_get_name(
    + GPLUGIN_PLUGIN_INFO(info))));
    gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
    submenu = gtk_menu_new();
    --- a/pidgin/gtknotify.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtknotify.c Tue Oct 08 21:48:28 2019 -0500
    @@ -345,8 +345,7 @@
    gtk_tree_model_get(GTK_TREE_MODEL(pounce_dialog->treemodel), &iter,
    PIDGIN_POUNCE_DATA, &pounce_data,
    -1);
    - g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, (GDestroyNotify)gtk_tree_path_free);
    pounces = purple_pounces_get_all();
    for (; pounces != NULL; pounces = pounces->next) {
    @@ -499,8 +498,7 @@
    button = bd->button;
    button->callback(purple_account_get_connection(data->account), row, data->user_data);
    - g_list_foreach(row, (GFunc)g_free, NULL);
    - g_list_free(row);
    + g_list_free_full(row, g_free);
    }
    /* copy-paste from gtkrequest.c */
    @@ -816,8 +814,6 @@
    *tos, (int)count);
    data2 = pidgin_notify_add_mail(mail_dialog->treemodel, account, notification, urls ? *urls : NULL, count, FALSE, &new_data);
    if (data2 && new_data) {
    - if (data)
    - data->purple_has_handle = FALSE;
    data = data2;
    }
    g_free(notification);
    @@ -1016,7 +1012,7 @@
    g_return_val_if_fail(gc != NULL, NULL);
    g_return_val_if_fail(results != NULL, NULL);
    - data = g_malloc(sizeof(PidginNotifySearchResultsData));
    + data = g_new0(PidginNotifySearchResultsData, 1);
    data->user_data = user_data;
    data->results = results;
    --- a/pidgin/gtkplugin.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkplugin.c Tue Oct 08 21:48:28 2019 -0500
    @@ -300,9 +300,11 @@
    prefs_count++;
    if (prefs_count > 1) {
    - purple_debug_warning("gtkplugin", "Plugin %s contains more than"
    - " one prefs callback, some will be ignored.",
    - purple_plugin_info_get_name(info));
    + purple_debug_warning("gtkplugin",
    + "Plugin %s contains more than one prefs "
    + "callback, some will be ignored.",
    + gplugin_plugin_info_get_name(
    + GPLUGIN_PLUGIN_INFO(info)));
    }
    g_return_if_fail(prefs_count > 0);
    @@ -350,7 +352,8 @@
    gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config");
    gtk_window_set_title(GTK_WINDOW(dialog),
    - _(purple_plugin_info_get_name(info)));
    + _(gplugin_plugin_info_get_name(
    + GPLUGIN_PLUGIN_INFO(info))));
    gtk_widget_show_all(dialog);
    }
    }
    @@ -369,6 +372,7 @@
    GList *plugins, *l;
    PurplePlugin *plug;
    PurplePluginInfo *info;
    + GPluginPluginInfo *ginfo;
    gtk_list_store_clear(ls);
    purple_plugins_refresh();
    @@ -383,21 +387,26 @@
    char *desc;
    plug = PURPLE_PLUGIN(l->data);
    info = purple_plugin_get_info(plug);
    + ginfo = GPLUGIN_PLUGIN_INFO(info);
    if (purple_plugin_is_internal(plug))
    continue;
    gtk_list_store_append (ls, &iter);
    - if (purple_plugin_info_get_name(info)) {
    - name = g_markup_escape_text(_(purple_plugin_info_get_name(info)), -1);
    + if (gplugin_plugin_info_get_name(ginfo)) {
    + name = g_markup_escape_text(
    + _(gplugin_plugin_info_get_name(ginfo)), -1);
    } else {
    - char *tmp = g_path_get_basename(purple_plugin_get_filename(plug));
    + char *tmp = g_path_get_basename(
    + gplugin_plugin_get_filename(plug));
    name = g_markup_escape_text(tmp, -1);
    g_free(tmp);
    }
    - version = g_markup_escape_text(purple_plugin_info_get_version(info), -1);
    - summary = g_markup_escape_text(purple_plugin_info_get_summary(info), -1);
    + version = g_markup_escape_text(
    + gplugin_plugin_info_get_version(ginfo), -1);
    + summary = g_markup_escape_text(
    + gplugin_plugin_info_get_summary(ginfo), -1);
    desc = g_strdup_printf("<b>%s</b> %s\n%s", name,
    version,
    @@ -528,12 +537,20 @@
    {
    const char *dep_name = (const char *)l->data;
    PurplePlugin *dep_plugin = purple_plugins_find_plugin(dep_name);
    - PurplePluginInfo *dep_info;
    + GPluginPluginInfo *dep_info;
    - g_return_if_fail(dep_plugin != NULL);
    + if (dep_plugin == NULL) {
    + purple_debug_error("gtkplugin",
    + "The %s plugin could not be found.",
    + dep_name);
    + continue;
    + }
    - dep_info = purple_plugin_get_info(dep_plugin);
    - g_string_append_printf(tmp, "\n\t%s\n", purple_plugin_info_get_name(dep_info));
    + dep_info = GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(dep_plugin));
    + g_string_append_printf(
    + tmp, "\n\t%s\n",
    + gplugin_plugin_info_get_name(dep_info));
    }
    cb_data = g_new(gpointer, 3);
    @@ -556,7 +573,8 @@
    static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, GtkTreeIter *iter, GError *error, gboolean unload)
    {
    - PurplePluginInfo *info = purple_plugin_get_info(plug);
    + GPluginPluginInfo *info =
    + GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plug));
    if (unload)
    {
    @@ -575,9 +593,7 @@
    }
    pidgin_clear_cursor(plugin_dialog);
    - }
    - else if (!unload && error)
    - {
    + } else if (error) {
    purple_notify_warning(NULL, NULL, _("Could not load plugin"), error->message, NULL);
    }
    @@ -585,15 +601,17 @@
    if (error != NULL)
    {
    - gchar *name = g_markup_escape_text(purple_plugin_info_get_name(info), -1);
    + gchar *name = g_markup_escape_text(
    + gplugin_plugin_info_get_name(info), -1);
    gchar *disp_error = g_markup_escape_text(error->message, -1);
    gchar *text;
    - text = g_strdup_printf(
    - "<b>%s</b> %s\n<span weight=\"bold\" color=\"red\">%s</span>",
    - purple_plugin_info_get_name(info),
    - purple_plugin_info_get_version(info), disp_error);
    + text = g_strdup_printf("<b>%s</b> %s\n<span weight=\"bold\" "
    + "color=\"red\">%s</span>",
    + gplugin_plugin_info_get_name(info),
    + gplugin_plugin_info_get_version(info),
    + disp_error);
    gtk_list_store_set(GTK_LIST_STORE (model), iter,
    1, text,
    -1);
    @@ -612,12 +630,13 @@
    }
    if ((unload && purple_plugin_get_dependent_plugins(plug)) ||
    - (!unload && purple_plugin_info_get_dependencies(info)))
    + (!unload && gplugin_plugin_info_get_dependencies(info))) {
    update_loaded_plugins(model);
    - else
    + } else {
    gtk_list_store_set(GTK_LIST_STORE (model), iter,
    0, purple_plugin_is_loaded(plug),
    -1);
    + }
    g_free(iter);
    pidgin_plugins_save();
    @@ -647,6 +666,7 @@
    GValue val;
    PurplePlugin *plug;
    PurplePluginInfo *info;
    + GPluginPluginInfo *ginfo;
    if (!gtk_tree_selection_get_selected (sel, &model, &iter))
    {
    @@ -665,9 +685,11 @@
    gtk_tree_model_get_value (model, &iter, 2, &val);
    plug = g_value_get_pointer(&val);
    info = purple_plugin_get_info(plug);
    + ginfo = GPLUGIN_PLUGIN_INFO(info);
    - name = g_markup_escape_text(purple_plugin_info_get_name(info), -1);
    - version = g_markup_escape_text(purple_plugin_info_get_version(info), -1);
    + name = g_markup_escape_text(gplugin_plugin_info_get_name(ginfo), -1);
    + version = g_markup_escape_text(gplugin_plugin_info_get_version(ginfo),
    + -1);
    buf = g_strdup_printf(
    "<span size=\"larger\" weight=\"bold\">%s</span> "
    "<span size=\"smaller\">%s</span>",
    @@ -677,18 +699,19 @@
    g_free(version);
    g_free(buf);
    - gtk_text_buffer_set_text(plugin_desc, purple_plugin_info_get_description(info), -1);
    + gtk_text_buffer_set_text(
    + plugin_desc, gplugin_plugin_info_get_description(ginfo), -1);
    - authorlist = purple_plugin_info_get_authors(info);
    + authorlist = gplugin_plugin_info_get_authors(ginfo);
    if (authorlist)
    authors = g_strjoinv(",\n", (gchar **)authorlist);
    gtk_label_set_text(plugin_authors, authors);
    g_free(authors);
    - gtk_label_set_text(plugin_filename, purple_plugin_get_filename(plug));
    + gtk_label_set_text(plugin_filename, gplugin_plugin_get_filename(plug));
    g_free(plugin_website_uri);
    - plugin_website_uri = g_strdup(purple_plugin_info_get_website(info));
    + plugin_website_uri = g_strdup(gplugin_plugin_info_get_website(ginfo));
    if (plugin_website_uri)
    {
    @@ -807,7 +830,7 @@
    GtkTreeIter iter;
    GtkTreeView *treeview = GTK_TREE_VIEW(data);
    PurplePlugin *plugin = NULL;
    - PurplePluginInfo *info;
    + GPluginPluginInfo *info;
    GtkTreeModel *model = gtk_tree_view_get_model(treeview);
    PangoLayout *layout;
    int width, height;
    @@ -819,18 +842,25 @@
    return FALSE;
    gtk_tree_model_get(model, &iter, 2, &plugin, -1);
    - info = purple_plugin_get_info(plugin);
    - authorlist = purple_plugin_info_get_authors(info);
    + info = GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin));
    + authorlist = gplugin_plugin_info_get_authors(info);
    if (authorlist)
    authors = g_strjoinv(", ", (gchar **)authorlist);
    if (authors)
    pauthors = g_markup_escape_text(authors, -1);
    - markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s\n<b>%s:</b> %s",
    - name = g_markup_escape_text(purple_plugin_info_get_name(info), -1),
    - _("Description"), desc = g_markup_escape_text(purple_plugin_info_get_description(info), -1),
    - (g_strv_length((gchar **)authorlist) > 1 ? _("Authors") : _("Author")), pauthors);
    + markup = g_strdup_printf(
    + "<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> "
    + "%s\n<b>%s:</b> %s",
    + name = g_markup_escape_text(gplugin_plugin_info_get_name(info), -1),
    + _("Description"),
    + desc = g_markup_escape_text(
    + gplugin_plugin_info_get_description(info), -1),
    + (authorlist && g_strv_length((gchar **)authorlist) > 1
    + ? _("Authors")
    + : _("Author")),
    + pauthors);
    layout = gtk_widget_create_pango_layout(tipwindow, NULL);
    pango_layout_set_markup(layout, markup, -1);
    --- a/pidgin/gtkpounce.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkpounce.c Tue Oct 08 21:48:28 2019 -0500
    @@ -40,6 +40,7 @@
    #include "gtknotify.h"
    #include "pidginstock.h"
    #include "gtkutils.h"
    +#include "pidginaccountchooser.h"
    #include "pidgintalkatu.h"
    #include <gdk/gdkkeysyms.h>
    @@ -391,10 +392,9 @@
    }
    static void
    -pounce_choose_cb(GtkWidget *item, PurpleAccount *account,
    - PidginPounceDialog *dialog)
    +pounce_choose_cb(GtkWidget *chooser, PidginPounceDialog *dialog)
    {
    - dialog->account = account;
    + dialog->account = pidgin_account_chooser_get_selected(chooser);
    }
    static void
    @@ -443,7 +443,8 @@
    gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry), purple_buddy_get_name(buddy));
    dialog->account = purple_buddy_get_account(buddy);
    - pidgin_account_option_menu_set_selected(dialog->account_menu, purple_buddy_get_account(buddy));
    + pidgin_account_chooser_set_selected(
    + dialog->account_menu, purple_buddy_get_account(buddy));
    gtk_drag_finish(dc, TRUE, (gdk_drag_context_get_actions(dc) == GDK_ACTION_MOVE), t);
    }
    @@ -468,7 +469,8 @@
    gtk_entry_set_text(GTK_ENTRY(dialog->buddy_entry), username);
    dialog->account = account;
    - pidgin_account_option_menu_set_selected(dialog->account_menu, account);
    + pidgin_account_chooser_set_selected(
    + dialog->account_menu, account);
    }
    }
    @@ -488,7 +490,8 @@
    static void
    reset_send_msg_entry(PidginPounceDialog *dialog, GtkWidget *dontcare)
    {
    - PurpleAccount *account = pidgin_account_option_menu_get_selected(dialog->account_menu);
    + PurpleAccount *account =
    + pidgin_account_chooser_get_selected(dialog->account_menu);
    if(GTK_IS_TEXT_BUFFER(dialog->send_msg_buffer)) {
    g_object_unref(dialog->send_msg_buffer);
    @@ -590,9 +593,9 @@
    gtk_size_group_add_widget(sg, label);
    dialog->account_menu =
    - pidgin_account_option_menu_new(dialog->account, TRUE,
    - G_CALLBACK(pounce_choose_cb),
    - NULL, dialog);
    + pidgin_account_chooser_new(dialog->account, TRUE);
    + g_signal_connect(dialog->account_menu, "changed",
    + G_CALLBACK(pounce_choose_cb), dialog);
    gtk_box_pack_start(GTK_BOX(hbox), dialog->account_menu, FALSE, FALSE, 0);
    gtk_widget_show(dialog->account_menu);
    @@ -983,7 +986,8 @@
    "play-sound",
    "filename")) != NULL)
    {
    - gtk_entry_set_text(GTK_ENTRY(dialog->play_sound_entry), (value && *value != '\0') ? value : _("(default)"));
    + gtk_entry_set_text(GTK_ENTRY(dialog->play_sound_entry),
    + (*value != '\0') ? value : _("(default)"));
    }
    }
    else
    --- a/pidgin/gtkprefs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkprefs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -45,7 +45,6 @@
    #include "gtkblist.h"
    #include "gtkconv.h"
    -#include "gtkconv-theme.h"
    #include "gtkdialogs.h"
    #include "gtkprefs.h"
    #include "gtksavedstatuses.h"
    @@ -104,8 +103,8 @@
    struct _PidginPrefsWindow {
    GtkDialog parent;
    - /* Notebook */
    - GtkWidget *notebook;
    + /* Stack */
    + GtkWidget *stack;
    /* Interface page */
    struct {
    @@ -211,6 +210,31 @@
    GtkWidget *password;
    } proxy;
    + /* Keyrings page */
    + struct {
    + PidginPrefCombo active;
    + GtkWidget *vbox;
    + PurpleRequestFields *settings;
    + GtkWidget *settings_box;
    + GtkWidget *apply;
    + } keyring;
    +
    + /* Sounds page */
    + struct {
    + PidginPrefCombo method;
    + GtkWidget *method_vbox;
    + GtkWidget *command;
    + GtkWidget *command_hbox;
    + GtkWidget *mute;
    + GtkWidget *conv_focus;
    + PidginPrefCombo while_status;
    + struct {
    + GtkWidget *view;
    + GtkListStore *store;
    + } event;
    + GtkWidget *entry;
    + } sound;
    +
    /* Away page */
    struct {
    PidginPrefCombo idle_reporting;
    @@ -222,6 +246,37 @@
    GtkWidget *startup_hbox;
    GtkWidget *startup_label;
    } away;
    +
    + /* Themes page */
    + struct {
    + GtkWidget *blist;
    + GtkWidget *status;
    + GtkWidget *sound;
    + GtkWidget *smiley;
    + } theme;
    +
    +#ifdef USE_VV
    + /* Voice/Video page */
    + struct {
    + struct {
    + PidginPrefCombo input;
    + PidginPrefCombo output;
    + GtkWidget *level;
    + GtkWidget *threshold;
    + GtkWidget *volume;
    + GtkWidget *test;
    + GstElement *pipeline;
    + } voice;
    +
    + struct {
    + PidginPrefCombo input;
    + PidginPrefCombo output;
    + GtkWidget *drawing_area;
    + GtkWidget *test;
    + GstElement *pipeline;
    + } video;
    + } vv;
    +#endif
    };
    /* Main dialog */
    @@ -230,45 +285,20 @@
    /* Themes page */
    static GtkWidget *prefs_sound_themes_combo_box;
    static GtkWidget *prefs_blist_themes_combo_box;
    -static GtkWidget *prefs_conv_themes_combo_box;
    -static GtkWidget *prefs_conv_variants_combo_box;
    static GtkWidget *prefs_status_themes_combo_box;
    static GtkWidget *prefs_smiley_themes_combo_box;
    -static PurpleHttpConnection *prefs_conv_themes_running_request = NULL;
    -
    -/* Keyrings page */
    -static GtkWidget *keyring_page_instance = NULL;
    -static GtkComboBox *keyring_combo = NULL;
    -static GtkBox *keyring_vbox = NULL;
    -static PurpleRequestFields *keyring_settings = NULL;
    -static GList *keyring_settings_fields = NULL;
    -static GtkWidget *keyring_apply = NULL;
    +static PurpleHttpConnection *prefs_themes_running_request = NULL;
    /* Sound theme specific */
    -static GtkWidget *sound_entry = NULL;
    static int sound_row_sel = 0;
    static gboolean prefs_sound_themes_loading;
    /* These exist outside the lifetime of the prefs dialog */
    static GtkListStore *prefs_sound_themes;
    static GtkListStore *prefs_blist_themes;
    -static GtkListStore *prefs_conv_themes;
    -static GtkListStore *prefs_conv_variants;
    static GtkListStore *prefs_status_icon_themes;
    static GtkListStore *prefs_smiley_themes;
    -#ifdef USE_VV
    -
    -static GtkWidget *voice_level;
    -static GtkWidget *voice_threshold;
    -static GtkWidget *voice_volume;
    -static GstElement *voice_pipeline;
    -
    -static GtkWidget *video_drawing_area;
    -static GstElement *video_pipeline;
    -
    -#endif
    -
    /*
    * PROTOTYPES
    */
    @@ -437,18 +467,15 @@
    cb(combo_box, active);
    }
    -static void pidgin_prefs_dropdown_revert_active(GtkComboBox *combo_box)
    +static void
    +pidgin_prefs_bind_dropdown_revert_active(PidginPrefCombo *combo)
    {
    - gint previously_active;
    -
    - g_return_if_fail(combo_box != NULL);
    -
    - previously_active = GPOINTER_TO_INT(g_object_get_data(
    - G_OBJECT(combo_box), "previously_active"));
    - g_object_set_data(G_OBJECT(combo_box), "current_active",
    - GINT_TO_POINTER(previously_active));
    -
    - gtk_combo_box_set_active(combo_box, previously_active);
    + g_return_if_fail(combo != NULL);
    +
    + combo->current_active = combo->previously_active;
    +
    + gtk_combo_box_set_active(GTK_COMBO_BOX(combo->combo),
    + combo->previously_active);
    }
    static GtkWidget *
    @@ -762,7 +789,7 @@
    static void
    pidgin_prefs_bind_dropdown(PidginPrefCombo *combo)
    {
    - GtkListStore *store = NULL;
    + GtkTreeModel *store = NULL;
    GtkTreeIter iter;
    GtkTreeIter active;
    @@ -776,10 +803,9 @@
    g_return_if_reached();
    }
    - store = GTK_LIST_STORE(
    - gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo)));
    -
    - if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
    + store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo));
    +
    + if (!gtk_tree_model_get_iter_first(store, &iter)) {
    g_return_if_reached();
    }
    @@ -789,7 +815,7 @@
    gboolean bool_value = FALSE;
    if (combo->type == PURPLE_PREF_INT) {
    - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
    + gtk_tree_model_get(store, &iter,
    PREF_DROPDOWN_VALUE, &int_value,
    -1);
    if (combo->value.integer == int_value) {
    @@ -798,7 +824,7 @@
    }
    }
    else if (combo->type == PURPLE_PREF_STRING) {
    - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
    + gtk_tree_model_get(store, &iter,
    PREF_DROPDOWN_VALUE, &str_value,
    -1);
    if (purple_strequal(combo->value.string, str_value)) {
    @@ -807,7 +833,7 @@
    }
    }
    else if (combo->type == PURPLE_PREF_BOOLEAN) {
    - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
    + gtk_tree_model_get(store, &iter,
    PREF_DROPDOWN_VALUE, &bool_value,
    -1);
    if (combo->value.boolean == bool_value) {
    @@ -815,7 +841,7 @@
    break;
    }
    }
    - } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
    + } while (gtk_tree_model_iter_next(store, &iter));
    gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo->combo), &active);
    @@ -863,15 +889,11 @@
    G_CALLBACK(set_bool_pref), (char *)key);
    }
    -static void keyring_page_cleanup(void);
    +static void keyring_page_cleanup(PidginPrefsWindow *win);
    static void
    delete_prefs(GtkWidget *asdf, void *gdsa)
    {
    - /* Cancel HTTP requests */
    - purple_http_conn_cancel(prefs_conv_themes_running_request);
    - prefs_conv_themes_running_request = NULL;
    -
    /* Close any "select sound" request dialogs */
    purple_request_close_with_handle(prefs);
    @@ -881,25 +903,16 @@
    purple_prefs_disconnect_by_handle(prefs);
    /* NULL-ify globals */
    - sound_entry = NULL;
    sound_row_sel = 0;
    prefs_sound_themes_loading = FALSE;
    prefs_sound_themes_combo_box = NULL;
    prefs_blist_themes_combo_box = NULL;
    - prefs_conv_themes_combo_box = NULL;
    - prefs_conv_variants_combo_box = NULL;
    prefs_status_themes_combo_box = NULL;
    prefs_smiley_themes_combo_box = NULL;
    - keyring_page_cleanup();
    -
    -#ifdef USE_VV
    - voice_level = NULL;
    - voice_threshold = NULL;
    - voice_volume = NULL;
    - video_drawing_area = NULL;
    -#endif
    + keyring_page_cleanup(prefs);
    +
    g_free(prefs->proxy.gnome_program_path);
    g_free(prefs->browser.gnome_program_path);
    prefs = NULL;
    @@ -1042,17 +1055,6 @@
    if (pixbuf != NULL)
    g_object_unref(G_OBJECT(pixbuf));
    - } else if (PIDGIN_IS_CONV_THEME(theme)) {
    - /* No image available? */
    -
    - name = purple_theme_get_name(theme);
    - /* No author available */
    - /* No description available */
    -
    - markup = get_theme_markup(name, FALSE, NULL, NULL);
    -
    - gtk_list_store_append(prefs_conv_themes, &iter);
    - gtk_list_store_set(prefs_conv_themes, &iter, 1, markup, 2, name, -1);
    }
    }
    @@ -1092,7 +1094,7 @@
    purple_theme_manager_refresh();
    tmp = g_build_filename(PURPLE_DATADIR, "icons", "hicolor", "32x32",
    - "apps", "pidgin.png", NULL);
    + "apps", "im.pidgin.Pidgin3.png", NULL);
    pixbuf = pidgin_pixbuf_new_from_file_at_scale(tmp, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE, TRUE);
    g_free(tmp);
    @@ -1109,17 +1111,6 @@
    gtk_list_store_set(prefs_blist_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
    g_free(tmp);
    - /* conversation themes */
    - gtk_list_store_clear(prefs_conv_themes);
    - gtk_list_store_append(prefs_conv_themes, &iter);
    - tmp = get_theme_markup(_("Default"), FALSE, _("Penguin Pimps"),
    - _("The default Pidgin conversation theme"));
    - gtk_list_store_set(prefs_conv_themes, &iter, 0, pixbuf, 1, tmp, 2, "", -1);
    - g_free(tmp);
    -
    - /* conversation theme variants */
    - gtk_list_store_clear(prefs_conv_variants);
    -
    /* status icon themes */
    gtk_list_store_clear(prefs_status_icon_themes);
    gtk_list_store_append(prefs_status_icon_themes, &iter);
    @@ -1140,7 +1131,6 @@
    /* set active */
    prefs_set_active_theme_combo(prefs_sound_themes_combo_box, prefs_sound_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/theme"));
    prefs_set_active_theme_combo(prefs_blist_themes_combo_box, prefs_blist_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/blist/theme"));
    - prefs_set_active_theme_combo(prefs_conv_themes_combo_box, prefs_conv_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/theme"));
    prefs_set_active_theme_combo(prefs_status_themes_combo_box, prefs_status_icon_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/status/icon-theme"));
    prefs_set_active_theme_combo(prefs_smiley_themes_combo_box, prefs_smiley_themes, purple_prefs_get_string(PIDGIN_PREFS_ROOT "/smileys/theme"));
    prefs_sound_themes_loading = FALSE;
    @@ -1154,10 +1144,6 @@
    prefs_blist_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
    - prefs_conv_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
    -
    - prefs_conv_variants = gtk_list_store_new(1, G_TYPE_STRING);
    -
    prefs_status_icon_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
    prefs_smiley_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
    @@ -1237,9 +1223,6 @@
    static void
    theme_install_theme(char *path, struct theme_info *info)
    {
    -#ifndef _WIN32
    - gchar *command;
    -#endif
    gchar *destdir;
    const char *tail;
    gboolean is_smiley_theme, is_archive;
    @@ -1262,9 +1245,9 @@
    g_strchomp(path);
    if ((is_smiley_theme = purple_strequal(info->type, "smiley")))
    - destdir = g_build_filename(purple_user_dir(), "smileys", NULL);
    + destdir = g_build_filename(purple_data_dir(), "smileys", NULL);
    else
    - destdir = g_build_filename(purple_user_dir(), "themes", "temp", NULL);
    + destdir = g_build_filename(purple_data_dir(), "themes", "temp", NULL);
    /* We'll check this just to make sure. This also lets us do something different on
    * other platforms, if need be */
    @@ -1272,6 +1255,7 @@
    #ifndef _WIN32
    gchar *path_escaped = g_shell_quote(path);
    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);
    @@ -1288,6 +1272,7 @@
    free_theme_info(info);
    return;
    }
    + g_free(command);
    #else
    if (!winpidgin_gz_untar(path, destdir)) {
    purple_notify_error(NULL, NULL, _("Theme failed to unpack."), NULL, NULL);
    @@ -1307,17 +1292,17 @@
    if (PURPLE_IS_THEME(theme)) {
    /* create the location for the theme */
    - gchar *theme_dest = g_build_filename(purple_user_dir(), "themes",
    - purple_theme_get_name(theme),
    - "purple", info->type, NULL);
    + gchar *theme_dest = g_build_filename(purple_data_dir(), "themes",
    + 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);
    g_free(theme_dest);
    - theme_dest = g_build_filename(purple_user_dir(), "themes",
    - purple_theme_get_name(theme),
    - "purple", info->type, NULL);
    + theme_dest = g_build_filename(purple_data_dir(), "themes",
    + purple_theme_get_name(theme),
    + "purple", info->type, NULL);
    /* move the entire directory to new location */
    if (g_rename(purple_theme_get_dir(theme), theme_dest)) {
    @@ -1344,7 +1329,8 @@
    } else { /* just a single file so copy it to a new temp directory and attempt to load it*/
    gchar *temp_path, *temp_file;
    - temp_path = g_build_filename(purple_user_dir(), "themes", "temp", "sub_folder", NULL);
    + temp_path = g_build_filename(purple_data_dir(), "themes", "temp",
    + "sub_folder", NULL);
    if (info->original_name != NULL) {
    /* name was changed from the original (probably a dnd) change it back before loading */
    @@ -1364,9 +1350,10 @@
    theme = prefs_theme_find_theme(temp_path, info->type);
    if (PURPLE_IS_THEME(theme)) {
    - gchar *theme_dest = g_build_filename(purple_user_dir(), "themes",
    - purple_theme_get_name(theme),
    - "purple", info->type, NULL);
    + gchar *theme_dest =
    + g_build_filename(purple_data_dir(), "themes",
    + 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);
    @@ -1411,8 +1398,8 @@
    gchar *path;
    size_t wc;
    - g_assert(http_conn == prefs_conv_themes_running_request);
    - prefs_conv_themes_running_request = NULL;
    + g_assert(http_conn == prefs_themes_running_request);
    + prefs_themes_running_request = NULL;
    if (!purple_http_response_is_successful(response)) {
    free_theme_info(info);
    @@ -1475,12 +1462,12 @@
    /* Oo, a web drag and drop. This is where things
    * will start to get interesting */
    PurpleHttpRequest *hr;
    - purple_http_conn_cancel(prefs_conv_themes_running_request);
    + purple_http_conn_cancel(prefs_themes_running_request);
    hr = purple_http_request_new(name);
    purple_http_request_set_max_len(hr,
    PREFS_MAX_DOWNLOADED_THEME_SIZE);
    - prefs_conv_themes_running_request = purple_http_request(
    + prefs_themes_running_request = purple_http_request(
    NULL, hr, theme_got_url, info);
    purple_http_request_unref(hr);
    } else
    @@ -1493,43 +1480,32 @@
    }
    /* builds a theme combo box from a list store with colums: icon preview, markup, theme name */
    -static GtkWidget *
    -prefs_build_theme_combo_box(GtkListStore *store, const char *current_theme, const char *type)
    +static void
    +prefs_build_theme_combo_box(GtkWidget *combo_box, GtkListStore *store,
    + const char *current_theme, const char *type)
    {
    - GtkCellRenderer *cell_rend;
    - GtkWidget *combo_box;
    GtkTargetEntry te[3] = {
    {"text/plain", 0, 0},
    {"text/uri-list", 0, 1},
    {"STRING", 0, 2}
    };
    - g_return_val_if_fail(store != NULL && current_theme != NULL, NULL);
    -
    - combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
    -
    - cell_rend = gtk_cell_renderer_pixbuf_new();
    - gtk_cell_renderer_set_fixed_size(cell_rend, PREFS_OPTIMAL_ICON_SIZE, PREFS_OPTIMAL_ICON_SIZE);
    - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, FALSE);
    - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "pixbuf", 0, NULL);
    -
    - cell_rend = gtk_cell_renderer_text_new();
    - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, TRUE);
    - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL);
    - g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    + g_return_if_fail(store != NULL && current_theme != NULL);
    +
    + gtk_combo_box_set_model(GTK_COMBO_BOX(combo_box),
    + GTK_TREE_MODEL(store));
    gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te,
    sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
    g_signal_connect(G_OBJECT(combo_box), "drag_data_received", G_CALLBACK(theme_dnd_recv), (gpointer) type);
    -
    - return combo_box;
    }
    /* sets the current sound theme */
    static void
    prefs_set_sound_theme_cb(GtkComboBox *combo_box, gpointer user_data)
    {
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(user_data);
    gint i;
    gchar *pref;
    gchar *new_theme;
    @@ -1552,7 +1528,7 @@
    /* gets rid of the "(Custom)" from the last selection */
    pref_sound_generate_markup();
    - gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
    + gtk_entry_set_text(GTK_ENTRY(win->sound.entry), _("(default)"));
    g_free(new_theme);
    }
    @@ -1571,11 +1547,6 @@
    purple_prefs_set_string(PIDGIN_PREFS_ROOT "/smileys/theme", new_theme);
    -#if 0
    - /* TODO: update smileys in sample_webview input box. */
    - update_smileys_in_webview_input_box(win->conversation.sample_webview);
    -#endif
    -
    g_free(new_theme);
    }
    }
    @@ -1637,80 +1608,6 @@
    }
    }
    -/* sets the current conversation theme variant */
    -static void
    -prefs_set_conv_variant_cb(GtkComboBox *combo_box, gpointer user_data)
    -{
    - PidginConvTheme *theme = NULL;
    - GtkTreeIter iter;
    - gchar *name = NULL;
    -
    - if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(prefs_conv_themes_combo_box), &iter)) {
    - gtk_tree_model_get(GTK_TREE_MODEL(prefs_conv_themes), &iter, 2, &name, -1);
    - if (name && *name)
    - theme = PIDGIN_CONV_THEME(purple_theme_manager_find_theme(name, "conversation"));
    - else
    - theme = PIDGIN_CONV_THEME(pidgin_conversations_get_default_theme());
    - g_free(name);
    -
    - if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
    - gtk_tree_model_get(GTK_TREE_MODEL(prefs_conv_variants), &iter, 0, &name, -1);
    - pidgin_conversation_theme_set_variant(theme, name);
    - g_free(name);
    - }
    - }
    -}
    -
    -/* sets the current conversation theme */
    -static void
    -prefs_set_conv_theme_cb(GtkComboBox *combo_box, gpointer user_data)
    -{
    - GtkTreeIter iter;
    -
    - if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
    - gchar *name = NULL;
    - PidginConvTheme *theme;
    - const char *current_variant;
    - const GList *variants;
    - gboolean unset = TRUE;
    -
    - gtk_tree_model_get(GTK_TREE_MODEL(prefs_conv_themes), &iter, 2, &name, -1);
    -
    - purple_prefs_set_string(PIDGIN_PREFS_ROOT "/conversations/theme", name);
    -
    - g_signal_handlers_block_by_func(prefs_conv_variants_combo_box,
    - prefs_set_conv_variant_cb, NULL);
    -
    - /* Update list of variants */
    - gtk_list_store_clear(prefs_conv_variants);
    -
    - if (name && *name)
    - theme = PIDGIN_CONV_THEME(purple_theme_manager_find_theme(name, "conversation"));
    - else
    - theme = PIDGIN_CONV_THEME(pidgin_conversations_get_default_theme());
    -
    - current_variant = pidgin_conversation_theme_get_variant(theme);
    -
    - variants = pidgin_conversation_theme_get_variants(theme);
    - for (; variants && current_variant; variants = g_list_next(variants)) {
    - gtk_list_store_append(prefs_conv_variants, &iter);
    - gtk_list_store_set(prefs_conv_variants, &iter, 0, variants->data, -1);
    -
    - if (g_str_equal(variants->data, current_variant)) {
    - gtk_combo_box_set_active_iter(GTK_COMBO_BOX(prefs_conv_variants_combo_box), &iter);
    - unset = FALSE;
    - }
    - }
    -
    - if (unset)
    - gtk_combo_box_set_active(GTK_COMBO_BOX(prefs_conv_variants_combo_box), 0);
    -
    - g_signal_handlers_unblock_by_func(prefs_conv_variants_combo_box,
    - prefs_set_conv_variant_cb, NULL);
    - g_free(name);
    - }
    -}
    -
    /* sets the current icon theme */
    static void
    prefs_set_status_icon_theme_cb(GtkComboBox *combo_box, gpointer user_data)
    @@ -1729,148 +1626,40 @@
    g_free(name);
    pidgin_stock_load_status_icon_theme(theme);
    - pidgin_blist_refresh(purple_blist_get_buddy_list());
    + pidgin_blist_refresh(purple_blist_get_default());
    }
    }
    -static GtkWidget *
    -add_theme_prefs_combo(GtkWidget *vbox,
    - GtkSizeGroup *combo_sg, GtkSizeGroup *label_sg,
    - GtkListStore *theme_store,
    - GCallback combo_box_cb, gpointer combo_box_cb_user_data,
    - const char *label_str, const char *prefs_path,
    - const char *theme_type)
    -{
    - GtkWidget *label;
    - GtkWidget *combo_box = NULL;
    - GtkWidget *themesel_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
    -
    - label = gtk_label_new(label_str);
    - gtk_label_set_xalign(GTK_LABEL(label), 0.0);
    - gtk_label_set_yalign(GTK_LABEL(label), 0.5);
    - gtk_size_group_add_widget(label_sg, label);
    - gtk_box_pack_start(GTK_BOX(themesel_hbox), label, FALSE, FALSE, 0);
    -
    - combo_box = prefs_build_theme_combo_box(theme_store,
    - purple_prefs_get_string(prefs_path),
    - theme_type);
    - g_signal_connect(G_OBJECT(combo_box), "changed",
    - (GCallback)combo_box_cb, combo_box_cb_user_data);
    - gtk_size_group_add_widget(combo_sg, combo_box);
    - gtk_box_pack_start(GTK_BOX(themesel_hbox), combo_box, TRUE, TRUE, 0);
    -
    - gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
    -
    - return combo_box;
    -}
    -
    -static GtkWidget *
    -add_child_theme_prefs_combo(GtkWidget *vbox, GtkSizeGroup *combo_sg,
    - GtkSizeGroup *label_sg, GtkListStore *theme_store,
    - GCallback combo_box_cb, gpointer combo_box_cb_user_data,
    - const char *label_str)
    +static void
    +bind_theme_page(PidginPrefsWindow *win)
    {
    - GtkWidget *label;
    - GtkWidget *combo_box;
    - GtkWidget *themesel_hbox;
    - GtkCellRenderer *cell_rend;
    -
    - themesel_hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
    - gtk_box_pack_start(GTK_BOX(vbox), themesel_hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new(label_str);
    - gtk_label_set_xalign(GTK_LABEL(label), 1.0);
    - gtk_label_set_yalign(GTK_LABEL(label), 0.5);
    - gtk_size_group_add_widget(label_sg, label);
    - gtk_box_pack_start(GTK_BOX(themesel_hbox), label, FALSE, FALSE, 0);
    -
    - combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(theme_store));
    -
    - cell_rend = gtk_cell_renderer_text_new();
    - gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), cell_rend, TRUE);
    - gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "text", 0, NULL);
    - g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
    -
    - g_signal_connect(G_OBJECT(combo_box), "changed",
    - (GCallback)combo_box_cb, combo_box_cb_user_data);
    - gtk_size_group_add_widget(combo_sg, combo_box);
    - gtk_box_pack_start(GTK_BOX(themesel_hbox), combo_box, TRUE, TRUE, 0);
    -
    - return combo_box;
    -}
    -
    -static GtkWidget *
    -theme_page(void)
    -{
    - GtkWidget *label;
    - GtkWidget *ret, *vbox;
    - GtkSizeGroup *label_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    - GtkSizeGroup *combo_sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    -
    - ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_CAT_SPACE);
    - gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
    -
    - vbox = pidgin_make_frame(ret, _("Theme Selections"));
    -
    - /* Instructions */
    - label = gtk_label_new(_("Select a theme that you would like to use from "
    - "the lists below.\nNew themes can be installed by "
    - "dragging and dropping them onto the theme list."));
    -
    - gtk_label_set_xalign(GTK_LABEL(label), 0.0);
    - gtk_label_set_yalign(GTK_LABEL(label), 0.5);
    - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
    -
    - gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, FALSE, 0);
    - gtk_widget_show(label);
    -
    /* Buddy List Themes */
    - prefs_blist_themes_combo_box = add_theme_prefs_combo(
    - vbox, combo_sg, label_sg, prefs_blist_themes,
    - (GCallback)prefs_set_blist_theme_cb, NULL,
    - _("Buddy List Theme:"), PIDGIN_PREFS_ROOT "/blist/theme", "blist");
    -
    - /* Conversation Themes */
    - prefs_conv_themes_combo_box = add_theme_prefs_combo(
    - vbox, combo_sg, label_sg, prefs_conv_themes,
    - (GCallback)prefs_set_conv_theme_cb, NULL,
    - _("Conversation Theme:"), PIDGIN_PREFS_ROOT "/conversations/theme", "conversation");
    -
    - /* Conversation Theme Variants */
    - prefs_conv_variants_combo_box = add_child_theme_prefs_combo(
    - vbox, combo_sg, label_sg, prefs_conv_variants,
    - (GCallback)prefs_set_conv_variant_cb, NULL, _("\tVariant:"));
    -
    - gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(prefs_conv_variants),
    - 0, GTK_SORT_ASCENDING);
    + prefs_build_theme_combo_box(win->theme.blist, prefs_blist_themes,
    + PIDGIN_PREFS_ROOT "/blist/theme", "blist");
    + prefs_blist_themes_combo_box = win->theme.blist;
    /* Status Icon Themes */
    - prefs_status_themes_combo_box = add_theme_prefs_combo(
    - vbox, combo_sg, label_sg, prefs_status_icon_themes,
    - (GCallback)prefs_set_status_icon_theme_cb, NULL,
    - _("Status Icon Theme:"), PIDGIN_PREFS_ROOT "/status/icon-theme", "icon");
    + prefs_build_theme_combo_box(win->theme.status, prefs_status_icon_themes,
    + PIDGIN_PREFS_ROOT "/status/icon-theme",
    + "icon");
    + prefs_status_themes_combo_box = win->theme.status;
    /* Sound Themes */
    - prefs_sound_themes_combo_box = add_theme_prefs_combo(
    - vbox, combo_sg, label_sg, prefs_sound_themes,
    - (GCallback)prefs_set_sound_theme_cb, NULL,
    - _("Sound Theme:"), PIDGIN_PREFS_ROOT "/sound/theme", "sound");
    + prefs_build_theme_combo_box(win->theme.sound, prefs_sound_themes,
    + PIDGIN_PREFS_ROOT "/sound/theme", "sound");
    + prefs_sound_themes_combo_box = win->theme.sound;
    /* Smiley Themes */
    - prefs_smiley_themes_combo_box = add_theme_prefs_combo(
    - vbox, combo_sg, label_sg, prefs_smiley_themes,
    - (GCallback)prefs_set_smiley_theme_cb, NULL,
    - _("Smiley Theme:"), PIDGIN_PREFS_ROOT "/smileys/theme", "smiley");
    + prefs_build_theme_combo_box(win->theme.smiley, prefs_smiley_themes,
    + PIDGIN_PREFS_ROOT "/smileys/theme",
    + "smiley");
    + prefs_smiley_themes_combo_box = win->theme.smiley;
    /* Custom sort so "none" theme is at top of list */
    gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(prefs_smiley_themes),
    2, pidgin_sort_smileys, NULL, NULL);
    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(prefs_smiley_themes),
    2, GTK_SORT_ASCENDING);
    -
    - gtk_widget_show_all(ret);
    -
    - return ret;
    }
    static void
    @@ -2353,11 +2142,20 @@
    gtk_widget_set_sensitive(hbox, purple_strequal(browser, "custom"));
    }
    +#endif /* _WIN32 */
    static void
    bind_browser_page(PidginPrefsWindow *win)
    {
    - if (purple_running_gnome()) {
    +#ifdef _WIN32
    + /* We use the registered default browser in windows */
    + gtk_widget_hide(win->browser.page);
    + return;
    +#else
    + /* if the user is running Mac OS X, hide the browsers tab */
    + if (purple_running_osx()) {
    + gtk_widget_hide(win->browser.page);
    + } else if (purple_running_gnome()) {
    gchar *path;
    gtk_stack_set_visible_child_name(GTK_STACK(win->browser.stack),
    @@ -2418,8 +2216,8 @@
    FALSE);
    }
    }
    +#endif /* _WIN32 */
    }
    -#endif /*_WIN32*/
    static void
    bind_proxy_page(PidginPrefsWindow *win)
    @@ -2522,7 +2320,7 @@
    PurpleRequestField *setting = _setting;
    PurpleRequestFieldType field_type;
    - gtk_widget_set_sensitive(keyring_apply, TRUE);
    + gtk_widget_set_sensitive(prefs->keyring.apply, TRUE);
    field_type = purple_request_field_get_field_type(setting);
    @@ -2541,11 +2339,11 @@
    g_return_if_reached();
    }
    -static GtkWidget *
    +static void
    keyring_page_add_settings_field(GtkBox *vbox, PurpleRequestField *setting,
    GtkSizeGroup *sg)
    {
    - GtkWidget *widget, *hbox;
    + GtkWidget *widget;
    PurpleRequestFieldType field_type;
    const gchar *label;
    @@ -2577,25 +2375,26 @@
    G_CALLBACK(keyring_page_settings_changed), setting);
    } else {
    purple_debug_error("gtkprefs", "Unsupported field type\n");
    - return NULL;
    + return;
    }
    - hbox = pidgin_add_widget_to_vbox(vbox, label, sg, widget,
    - FALSE, NULL);
    - return ((void*)hbox == (void*)vbox) ? widget : hbox;
    + pidgin_add_widget_to_vbox(vbox, label, sg, widget, FALSE, NULL);
    }
    /* XXX: it could be available for all plugins, not keyrings only */
    -static GList *
    -keyring_page_add_settings(PurpleRequestFields *settings)
    +static void
    +keyring_page_add_settings(PidginPrefsWindow *win)
    {
    - GList *it, *groups, *added_fields;
    + GtkWidget *box;
    + GList *it, *groups;
    GtkSizeGroup *sg;
    + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
    + gtk_box_pack_start(GTK_BOX(win->keyring.vbox), box, FALSE, FALSE, 0);
    +
    sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    - added_fields = NULL;
    - groups = purple_request_fields_get_groups(settings);
    + groups = purple_request_fields_get_groups(win->keyring.settings);
    for (it = g_list_first(groups); it != NULL; it = g_list_next(it)) {
    GList *it2, *fields;
    GtkBox *vbox;
    @@ -2605,74 +2404,71 @@
    group = it->data;
    group_title = purple_request_field_group_get_title(group);
    if (group_title) {
    - vbox = GTK_BOX(pidgin_make_frame(
    - GTK_WIDGET(keyring_vbox), group_title));
    - added_fields = g_list_prepend(added_fields,
    - g_object_get_data(G_OBJECT(vbox), "main-vbox"));
    - } else
    - vbox = keyring_vbox;
    + vbox = GTK_BOX(pidgin_make_frame(box, group_title));
    + } else {
    + vbox = GTK_BOX(box);
    + }
    fields = purple_request_field_group_get_fields(group);
    for (it2 = g_list_first(fields); it2 != NULL;
    it2 = g_list_next(it2)) {
    - GtkWidget *added = keyring_page_add_settings_field(vbox,
    - it2->data, sg);
    - if (added == NULL || vbox != keyring_vbox)
    - continue;
    - added_fields = g_list_prepend(added_fields, added);
    + keyring_page_add_settings_field(vbox, it2->data, sg);
    }
    }
    g_object_unref(sg);
    - return added_fields;
    + win->keyring.settings_box = box;
    }
    static void
    -keyring_page_settings_apply(GtkButton *button, gpointer _unused)
    +keyring_page_settings_apply(GtkButton *button, gpointer data)
    {
    - if (!purple_keyring_apply_settings(prefs, keyring_settings))
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    +
    + if (!purple_keyring_apply_settings(win, win->keyring.settings)) {
    return;
    -
    - gtk_widget_set_sensitive(keyring_apply, FALSE);
    + }
    +
    + gtk_widget_set_sensitive(win->keyring.apply, FALSE);
    }
    static void
    -keyring_page_update_settings()
    +keyring_page_update_settings(PidginPrefsWindow *win)
    {
    - if (keyring_settings != NULL)
    - purple_request_fields_destroy(keyring_settings);
    - keyring_settings = purple_keyring_read_settings();
    - if (!keyring_settings)
    + g_clear_pointer(&win->keyring.settings, purple_request_fields_destroy);
    + win->keyring.settings = purple_keyring_read_settings();
    + if (!win->keyring.settings) {
    return;
    -
    - keyring_settings_fields = keyring_page_add_settings(keyring_settings);
    -
    - keyring_apply = gtk_button_new_with_mnemonic(_("_Apply"));
    - gtk_box_pack_start(keyring_vbox, keyring_apply, FALSE, FALSE, 1);
    - gtk_widget_set_sensitive(keyring_apply, FALSE);
    - keyring_settings_fields = g_list_prepend(keyring_settings_fields,
    - keyring_apply);
    - g_signal_connect(G_OBJECT(keyring_apply), "clicked",
    - G_CALLBACK(keyring_page_settings_apply), NULL);
    -
    - gtk_widget_show_all(keyring_page_instance);
    + }
    +
    + keyring_page_add_settings(win);
    +
    + win->keyring.apply = gtk_button_new_with_mnemonic(_("_Apply"));
    + gtk_box_pack_start(GTK_BOX(win->keyring.settings_box),
    + win->keyring.apply, FALSE, FALSE, 1);
    + gtk_widget_set_sensitive(win->keyring.apply, FALSE);
    + g_signal_connect(G_OBJECT(win->keyring.apply), "clicked",
    + G_CALLBACK(keyring_page_settings_apply), win);
    +
    + gtk_widget_show_all(win->keyring.settings_box);
    }
    static void
    -keyring_page_pref_set_inuse(GError *error, gpointer _keyring_page_instance)
    +keyring_page_pref_set_inuse(GError *error, G_GNUC_UNUSED gpointer unused)
    {
    PurpleKeyring *in_use = purple_keyring_get_inuse();
    - if (_keyring_page_instance != keyring_page_instance) {
    + if (prefs == NULL) {
    purple_debug_info("gtkprefs", "pref window already closed\n");
    return;
    }
    - gtk_widget_set_sensitive(GTK_WIDGET(keyring_combo), TRUE);
    + gtk_widget_set_sensitive(GTK_WIDGET(prefs->keyring.active.combo), TRUE);
    if (error != NULL) {
    - pidgin_prefs_dropdown_revert_active(keyring_combo);
    + pidgin_prefs_bind_dropdown_revert_active(
    + &prefs->keyring.active);
    purple_notify_error(NULL, _("Keyring"),
    _("Failed to set new keyring"), error->message, NULL);
    return;
    @@ -2682,23 +2478,21 @@
    purple_prefs_set_string("/purple/keyring/active",
    purple_keyring_get_id(in_use));
    - keyring_page_update_settings();
    + keyring_page_update_settings(prefs);
    }
    static void
    -keyring_page_pref_changed(GtkComboBox *combo_box, PidginPrefValue value)
    +keyring_page_pref_changed(GtkComboBox *combo_box, PidginPrefCombo *combo)
    {
    const char *keyring_id;
    PurpleKeyring *keyring;
    - GList *it;
    g_return_if_fail(combo_box != NULL);
    - g_return_if_fail(value.type == PURPLE_PREF_STRING);
    -
    - keyring_id = value.value.string;
    +
    + keyring_id = combo->value.string;
    keyring = purple_keyring_find_keyring_by_id(keyring_id);
    if (keyring == NULL) {
    - pidgin_prefs_dropdown_revert_active(keyring_combo);
    + pidgin_prefs_bind_dropdown_revert_active(combo);
    purple_notify_error(NULL, _("Keyring"),
    _("Selected keyring is disabled"), NULL, NULL);
    return;
    @@ -2706,70 +2500,71 @@
    gtk_widget_set_sensitive(GTK_WIDGET(combo_box), FALSE);
    - for (it = keyring_settings_fields; it != NULL; it = g_list_next(it))
    - {
    - GtkWidget *widget = it->data;
    - gtk_container_remove(
    - GTK_CONTAINER(gtk_widget_get_parent(widget)), widget);
    - }
    - gtk_widget_show_all(keyring_page_instance);
    - g_list_free(keyring_settings_fields);
    - keyring_settings_fields = NULL;
    - if (keyring_settings)
    - purple_request_fields_destroy(keyring_settings);
    - keyring_settings = NULL;
    + g_clear_pointer(&prefs->keyring.settings_box, gtk_widget_destroy);
    + g_clear_pointer(&prefs->keyring.settings,
    + purple_request_fields_destroy);
    purple_keyring_set_inuse(keyring, FALSE, keyring_page_pref_set_inuse,
    - keyring_page_instance);
    + NULL);
    +}
    +
    +static void
    +keyring_page_cleanup(PidginPrefsWindow *win)
    +{
    + g_clear_pointer(&win->keyring.settings, purple_request_fields_destroy);
    }
    static void
    -keyring_page_cleanup(void)
    -{
    - keyring_page_instance = NULL;
    - keyring_combo = NULL;
    - keyring_vbox = NULL;
    - g_list_free(keyring_settings_fields);
    - keyring_settings_fields = NULL;
    - if (keyring_settings)
    - purple_request_fields_destroy(keyring_settings);
    - keyring_settings = NULL;
    - keyring_apply = NULL;
    -}
    -
    -static GtkWidget *
    -keyring_page(void)
    +bind_keyring_page(PidginPrefsWindow *win)
    {
    GList *names;
    - PidginPrefValue initial;
    -
    - g_return_val_if_fail(keyring_page_instance == NULL,
    - keyring_page_instance);
    -
    - keyring_page_instance = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_CAT_SPACE);
    - gtk_container_set_border_width(GTK_CONTAINER(keyring_page_instance),
    - PIDGIN_HIG_BORDER);
    /* Keyring selection */
    - keyring_vbox = GTK_BOX(pidgin_make_frame(keyring_page_instance,
    - _("Keyring")));
    names = purple_keyring_get_options();
    - initial.type = PURPLE_PREF_STRING;
    - initial.value.string = purple_prefs_get_string("/purple/keyring/active");
    - pidgin_prefs_dropdown_from_list_with_cb(GTK_WIDGET(keyring_vbox),
    - _("Keyring:"), &keyring_combo, names, initial,
    - keyring_page_pref_changed);
    + win->keyring.active.type = PURPLE_PREF_STRING;
    + win->keyring.active.key = "/purple/keyring/active";
    + pidgin_prefs_bind_dropdown_from_list(&win->keyring.active, names);
    + /* Override the usual callback to defer changing the pref. */
    + win->keyring.active.cb = keyring_page_pref_changed;
    g_list_free(names);
    - keyring_page_update_settings();
    -
    - gtk_widget_show_all(keyring_page_instance);
    -
    - return keyring_page_instance;
    + keyring_page_update_settings(win);
    }
    /*** keyring page - end *************************************************/
    +static gboolean
    +sound_method_filter(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
    +{
    + gboolean any = FALSE;
    + gboolean gstreamer = FALSE;
    + gboolean win32 = FALSE;
    +
    + gtk_tree_model_get(model, iter, 2, &any, 3, &gstreamer, 4, &win32, -1);
    +
    + if (any) {
    + return TRUE;
    + }
    +
    + if (gstreamer) {
    +#ifdef USE_GSTREAMER
    +#ifdef _WIN32
    + return win32;
    +#else
    + return !win32;
    +#endif
    +#else
    + return FALSE;
    +#endif
    + }
    +
    +#ifdef _WIN32
    + return win32;
    +#else
    + return !win32;
    +#endif
    +}
    +
    static gint
    sound_cmd_yeah(GtkEntry *entry, gpointer d)
    {
    @@ -2850,8 +2645,9 @@
    * Resets a sound file back to default.
    */
    static void
    -reset_sound(GtkWidget *button, gpointer i_am_also_NULL)
    +reset_sound(GtkWidget *button, gpointer data)
    {
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    gchar *pref;
    pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
    @@ -2859,7 +2655,7 @@
    purple_prefs_set_path(pref, "");
    g_free(pref);
    - gtk_entry_set_text(GTK_ENTRY(sound_entry), _("(default)"));
    + gtk_entry_set_text(GTK_ENTRY(win->sound.entry), _("(default)"));
    pref_sound_generate_markup();
    }
    @@ -2883,7 +2679,7 @@
    * sound, then update the box showing the file name.
    */
    if (sound == sound_row_sel)
    - gtk_entry_set_text(GTK_ENTRY(sound_entry), filename);
    + gtk_entry_set_text(GTK_ENTRY(prefs->sound.entry), filename);
    pref_sound_generate_markup();
    }
    @@ -2908,8 +2704,10 @@
    }
    static void
    -prefs_sound_sel(GtkTreeSelection *sel, GtkTreeModel *model)
    +prefs_sound_sel(GtkTreeSelection *sel, gpointer data)
    {
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    + GtkTreeModel *model;
    GtkTreeIter iter;
    GValue val;
    const char *file;
    @@ -2926,147 +2724,81 @@
    pidgin_sound_get_event_option(sound_row_sel));
    file = purple_prefs_get_path(pref);
    g_free(pref);
    - if (sound_entry)
    - gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)"));
    + if (win->sound.entry) {
    + gtk_entry_set_text(GTK_ENTRY(win->sound.entry),
    + (file && *file != '\0') ? file
    + : _("(default)"));
    + }
    g_value_unset (&val);
    pref_sound_generate_markup();
    }
    -
    static void
    -mute_changed_cb(const char *pref_name,
    - PurplePrefType pref_type,
    - gconstpointer val,
    - gpointer data)
    +bind_sound_page(PidginPrefsWindow *win)
    {
    - GtkToggleButton *button = data;
    - gboolean muted = GPOINTER_TO_INT(val);
    -
    - g_return_if_fail(purple_strequal (pref_name, PIDGIN_PREFS_ROOT "/sound/mute"));
    -
    - /* Block the handler that re-sets the preference. */
    - g_signal_handlers_block_matched(button, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (gpointer)pref_name);
    - gtk_toggle_button_set_active (button, muted);
    - g_signal_handlers_unblock_matched(button, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, (gpointer)pref_name);
    -}
    -
    -
    -static GtkWidget *
    -sound_page(void)
    -{
    - GtkWidget *ret;
    - GtkWidget *vbox, *vbox2, *button, *parent, *parent_parent, *parent_parent_parent;
    - GtkSizeGroup *sg;
    - GtkTreeIter iter;
    - GtkWidget *event_view;
    - GtkListStore *event_store;
    - GtkCellRenderer *rend;
    - GtkTreeViewColumn *col;
    + GtkTreeModel *model;
    GtkTreeSelection *sel;
    GtkTreePath *path;
    - GtkWidget *hbox;
    int j;
    const char *file;
    char *pref;
    - GtkWidget *dd;
    - GtkWidget *entry;
    const char *cmd;
    - ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_CAT_SPACE);
    - gtk_container_set_border_width (GTK_CONTAINER (ret), PIDGIN_HIG_BORDER);
    -
    - sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    -
    - vbox2 = pidgin_make_frame(ret, _("Sound Options"));
    -
    - vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BOX_SPACE);
    - gtk_box_pack_start(GTK_BOX(vbox2), vbox, FALSE, FALSE, 0);
    -
    - dd = pidgin_prefs_dropdown(vbox2, _("_Method:"), PURPLE_PREF_STRING,
    - PIDGIN_PREFS_ROOT "/sound/method",
    - _("Automatic"), "automatic",
    -#ifdef USE_GSTREAMER
    -#ifdef _WIN32
    -/* "WaveForm", "waveform", */
    - "DirectSound", "directsound",
    -#else
    - "ESD", "esd",
    - "ALSA", "alsa",
    -#endif /* _WIN32 */
    -#endif /* USE_GSTREAMER */
    -#ifdef _WIN32
    - "PlaySound", "playsoundw",
    -#else
    - _("Console beep"), "beep",
    - _("Command"), "custom",
    -#endif /* _WIN32 */
    - _("No sounds"), "none",
    - NULL);
    - gtk_size_group_add_widget(sg, dd);
    - gtk_label_set_xalign(GTK_LABEL(dd), 0.0);
    - gtk_label_set_yalign(GTK_LABEL(dd), 0.5);
    -
    - entry = gtk_entry_new();
    - gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);
    + win->sound.method.type = PURPLE_PREF_STRING;
    + win->sound.method.key = PIDGIN_PREFS_ROOT "/sound/method";
    + pidgin_prefs_bind_dropdown(&win->sound.method);
    + model = gtk_combo_box_get_model(GTK_COMBO_BOX(win->sound.method.combo));
    + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(model), sound_method_filter, NULL, NULL);
    + gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(model));
    +
    + gtk_widget_set_sensitive(
    + win->sound.method_vbox,
    + !purple_strequal(purple_prefs_get_string(PIDGIN_PREFS_ROOT
    + "/sound/method"),
    + "none"));
    + purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
    + sound_changed2_cb,
    + win->sound.method_vbox);
    +
    + gtk_widget_set_sensitive(
    + win->sound.command_hbox,
    + purple_strequal(purple_prefs_get_string(PIDGIN_PREFS_ROOT
    + "/sound/method"),
    + "custom"));
    + purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
    + sound_changed1_cb,
    + win->sound.command_hbox);
    +
    cmd = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/sound/command");
    - if(cmd)
    - gtk_entry_set_text(GTK_ENTRY(entry), cmd);
    - g_signal_connect(G_OBJECT(entry), "changed",
    - G_CALLBACK(sound_cmd_yeah), NULL);
    -
    - hbox = pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Sound c_ommand:\n(%s for filename)"), sg, entry, TRUE, NULL);
    - purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
    - sound_changed1_cb, hbox);
    - gtk_widget_set_sensitive(hbox,
    - purple_strequal(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"),
    - "custom"));
    -
    - button = pidgin_prefs_checkbox(_("M_ute sounds"), PIDGIN_PREFS_ROOT "/sound/mute", vbox);
    - purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/mute", mute_changed_cb, button);
    -
    - pidgin_prefs_checkbox(_("Sounds when conversation has _focus"),
    - PIDGIN_PREFS_ROOT "/sound/conv_focus", vbox);
    - pidgin_prefs_dropdown(vbox, _("_Enable sounds:"),
    - PURPLE_PREF_INT, "/purple/sound/while_status",
    - _("Only when available"), PURPLE_SOUND_STATUS_AVAILABLE,
    - _("Only when not available"), PURPLE_SOUND_STATUS_AWAY,
    - _("Always"), PURPLE_SOUND_STATUS_ALWAYS,
    - NULL);
    -
    - gtk_widget_set_sensitive(vbox,
    - !purple_strequal(purple_prefs_get_string(PIDGIN_PREFS_ROOT "/sound/method"), "none"));
    - purple_prefs_connect_callback(prefs, PIDGIN_PREFS_ROOT "/sound/method",
    - sound_changed2_cb, vbox);
    - vbox = pidgin_make_frame(ret, _("Sound Events"));
    -
    - /* The following is an ugly hack to make the frame expand so the
    - * sound events list is big enough to be usable */
    - parent = gtk_widget_get_parent(vbox);
    - parent_parent = gtk_widget_get_parent(parent);
    - parent_parent_parent = gtk_widget_get_parent(parent_parent);
    - gtk_box_set_child_packing(GTK_BOX(parent), vbox, TRUE, TRUE, 0,
    - GTK_PACK_START);
    - gtk_box_set_child_packing(GTK_BOX(parent_parent),
    - parent, TRUE, TRUE, 0, GTK_PACK_START);
    - gtk_box_set_child_packing(GTK_BOX(parent_parent_parent),
    - parent_parent, TRUE, TRUE, 0, GTK_PACK_START);
    + if (cmd) {
    + gtk_entry_set_text(GTK_ENTRY(win->sound.command), cmd);
    + }
    +
    + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/sound/mute",
    + win->sound.mute);
    +
    + pidgin_prefs_bind_checkbox(PIDGIN_PREFS_ROOT "/sound/conv_focus",
    + win->sound.conv_focus);
    +
    + win->sound.while_status.type = PURPLE_PREF_INT;
    + win->sound.while_status.key = "/purple/sound/while_status";
    + pidgin_prefs_bind_dropdown(&win->sound.while_status);
    /* SOUND SELECTION */
    - event_store = gtk_list_store_new (4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
    -
    for (j=0; j < PURPLE_NUM_SOUNDS; j++) {
    char *pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/enabled/%s",
    pidgin_sound_get_event_option(j));
    const char *label = pidgin_sound_get_event_label(j);
    + GtkTreeIter iter;
    if (label == NULL) {
    g_free(pref);
    continue;
    }
    - gtk_list_store_append (event_store, &iter);
    - gtk_list_store_set(event_store, &iter,
    + gtk_list_store_append(win->sound.event.store, &iter);
    + gtk_list_store_set(win->sound.event.store, &iter,
    0, purple_prefs_get_bool(pref),
    1, _(label),
    2, pref,
    @@ -3075,63 +2807,17 @@
    g_free(pref);
    }
    - event_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL(event_store));
    -
    - rend = gtk_cell_renderer_toggle_new();
    - sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
    - g_signal_connect (G_OBJECT (sel), "changed",
    - G_CALLBACK (prefs_sound_sel),
    - NULL);
    - g_signal_connect (G_OBJECT(rend), "toggled",
    - G_CALLBACK(event_toggled), event_store);
    + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(win->sound.event.view));
    path = gtk_tree_path_new_first();
    gtk_tree_selection_select_path(sel, path);
    gtk_tree_path_free(path);
    - col = gtk_tree_view_column_new_with_attributes (_("Play"),
    - rend,
    - "active", 0,
    - NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
    -
    - rend = gtk_cell_renderer_text_new();
    - col = gtk_tree_view_column_new_with_attributes (_("Event"),
    - rend,
    - "text", 1,
    - NULL);
    - gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
    - g_object_unref(G_OBJECT(event_store));
    - gtk_box_pack_start(GTK_BOX(vbox),
    - pidgin_make_scrollable(event_view, GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 100),
    - TRUE, TRUE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_BOX_SPACE);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - sound_entry = gtk_entry_new();
    pref = g_strdup_printf(PIDGIN_PREFS_ROOT "/sound/file/%s",
    pidgin_sound_get_event_option(0));
    file = purple_prefs_get_path(pref);
    g_free(pref);
    - gtk_entry_set_text(GTK_ENTRY(sound_entry), (file && *file != '\0') ? file : _("(default)"));
    - gtk_editable_set_editable(GTK_EDITABLE(sound_entry), FALSE);
    - gtk_box_pack_start(GTK_BOX(hbox), sound_entry, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
    -
    - button = gtk_button_new_with_mnemonic(_("_Browse..."));
    - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(select_sound), NULL);
    - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
    -
    - button = gtk_button_new_with_mnemonic(_("Pre_view"));
    - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(test_sound), NULL);
    - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
    -
    - button = gtk_button_new_with_mnemonic(_("_Reset"));
    - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(reset_sound), NULL);
    - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 1);
    -
    - gtk_widget_show_all(ret);
    - g_object_unref(sg);
    -
    - return ret;
    + gtk_entry_set_text(GTK_ENTRY(win->sound.entry),
    + (file && *file != '\0') ? file : _("(default)"));
    }
    @@ -3228,10 +2914,18 @@
    }
    static void
    -vv_test_switch_page_cb(GtkNotebook *notebook, GtkWidget *page, guint num, gpointer data)
    +vv_test_switch_page_cb(GtkStack *stack, G_GNUC_UNUSED GParamSpec *pspec,
    + gpointer data)
    {
    - GtkWidget *test = data;
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(test), FALSE);
    + PidginPrefsWindow *win = data;
    +
    + if (!g_str_equal(gtk_stack_get_visible_child_name(stack), "vv")) {
    + /* Disable any running test pipelines. */
    + gtk_toggle_button_set_active(
    + GTK_TOGGLE_BUTTON(win->vv.voice.test), FALSE);
    + gtk_toggle_button_set_active(
    + GTK_TOGGLE_BUTTON(win->vv.video.test), FALSE);
    + }
    }
    static GstElement *
    @@ -3265,12 +2959,14 @@
    static void
    on_volume_change_cb(GtkWidget *w, gdouble value, gpointer data)
    {
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    GstElement *volume;
    - if (!voice_pipeline)
    + if (!win->vv.voice.pipeline) {
    return;
    -
    - volume = gst_bin_get_by_name(GST_BIN(voice_pipeline), "volume");
    + }
    +
    + volume = gst_bin_get_by_name(GST_BIN(win->vv.voice.pipeline), "volume");
    g_object_set(volume, "volume",
    gtk_scale_button_get_value(GTK_SCALE_BUTTON(w)) * 10.0, NULL);
    }
    @@ -3284,13 +2980,9 @@
    gdouble percent;
    list = gst_structure_get_value(gst_message_get_structure(msg), value_name);
    -#if GST_CHECK_VERSION(1,0,0)
    G_GNUC_BEGIN_IGNORE_DEPRECATIONS
    value = g_value_array_get_nth(g_value_get_boxed(list), 0);
    G_GNUC_END_IGNORE_DEPRECATIONS
    -#else
    - value = gst_value_list_get_value(list, 0);
    -#endif
    value_db = g_value_get_double(value);
    percent = pow(10, value_db / 20);
    return (percent > 1.0) ? 1.0 : percent;
    @@ -3299,6 +2991,8 @@
    static gboolean
    gst_bus_cb(GstBus *bus, GstMessage *msg, gpointer data)
    {
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    +
    if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT &&
    gst_structure_has_name(gst_message_get_structure(msg), "level")) {
    @@ -3311,14 +3005,18 @@
    GstElement *valve;
    percent = gst_msg_db_to_percent(msg, "rms");
    - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(voice_level), percent);
    + gtk_progress_bar_set_fraction(
    + GTK_PROGRESS_BAR(win->vv.voice.level), percent);
    percent = gst_msg_db_to_percent(msg, "decay");
    - threshold = gtk_range_get_value(GTK_RANGE(voice_threshold)) / 100.0;
    + threshold = gtk_range_get_value(GTK_RANGE(
    + win->vv.voice.threshold)) /
    + 100.0;
    valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve");
    g_object_set(valve, "drop", (percent < threshold), NULL);
    - g_object_set(voice_level, "text",
    - (percent < threshold) ? _("DROP") : " ", NULL);
    + g_object_set(win->vv.voice.level, "text",
    + (percent < threshold) ? _("DROP") : " ",
    + NULL);
    }
    g_free(name);
    @@ -3330,23 +3028,25 @@
    static void
    voice_test_destroy_cb(GtkWidget *w, gpointer data)
    {
    - if (!voice_pipeline)
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    +
    + if (!win->vv.voice.pipeline) {
    return;
    -
    - gst_element_set_state(voice_pipeline, GST_STATE_NULL);
    - gst_object_unref(voice_pipeline);
    - voice_pipeline = NULL;
    + }
    +
    + gst_element_set_state(win->vv.voice.pipeline, GST_STATE_NULL);
    + g_clear_pointer(&win->vv.voice.pipeline, gst_object_unref);
    }
    static void
    -enable_voice_test(void)
    +enable_voice_test(PidginPrefsWindow *win)
    {
    GstBus *bus;
    - voice_pipeline = create_voice_pipeline();
    - bus = gst_pipeline_get_bus(GST_PIPELINE(voice_pipeline));
    + win->vv.voice.pipeline = create_voice_pipeline();
    + bus = gst_pipeline_get_bus(GST_PIPELINE(win->vv.voice.pipeline));
    gst_bus_add_signal_watch(bus);
    - g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), NULL);
    + g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), win);
    gst_object_unref(bus);
    }
    @@ -3356,28 +3056,24 @@
    PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    if (gtk_toggle_button_get_active(test)) {
    - gtk_widget_set_sensitive(voice_level, TRUE);
    - enable_voice_test();
    -
    - g_signal_connect(voice_volume, "value-changed",
    - G_CALLBACK(on_volume_change_cb), NULL);
    + gtk_widget_set_sensitive(win->vv.voice.level, TRUE);
    + enable_voice_test(win);
    +
    + g_signal_connect(win->vv.voice.volume, "value-changed",
    + G_CALLBACK(on_volume_change_cb), win);
    g_signal_connect(test, "destroy",
    - G_CALLBACK(voice_test_destroy_cb), NULL);
    - g_signal_connect(win->notebook, "switch-page",
    - G_CALLBACK(vv_test_switch_page_cb), test);
    + G_CALLBACK(voice_test_destroy_cb), win);
    } else {
    - gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(voice_level), 0.0);
    - gtk_widget_set_sensitive(voice_level, FALSE);
    - g_object_disconnect(voice_volume, "any-signal::value-changed",
    - G_CALLBACK(on_volume_change_cb), NULL,
    - NULL);
    + gtk_progress_bar_set_fraction(
    + GTK_PROGRESS_BAR(win->vv.voice.level), 0.0);
    + gtk_widget_set_sensitive(win->vv.voice.level, FALSE);
    + g_object_disconnect(win->vv.voice.volume,
    + "any-signal::value-changed",
    + G_CALLBACK(on_volume_change_cb), win, NULL);
    g_object_disconnect(test, "any-signal::destroy",
    - G_CALLBACK(voice_test_destroy_cb), NULL,
    + G_CALLBACK(voice_test_destroy_cb), win,
    NULL);
    - g_object_disconnect(win->notebook, "any-signal::switch-page",
    - G_CALLBACK(vv_test_switch_page_cb), test,
    - NULL);
    - voice_test_destroy_cb(NULL, NULL);
    + voice_test_destroy_cb(NULL, win);
    }
    }
    @@ -3402,58 +3098,41 @@
    }
    static void
    -make_voice_test(PidginPrefsWindow *win, GtkWidget *vbox)
    +bind_voice_test(PidginPrefsWindow *win, GtkBuilder *builder)
    {
    - GtkWidget *test;
    - GtkWidget *hbox;
    - GtkWidget *label;
    - GtkWidget *level;
    - GtkWidget *volume;
    - GtkWidget *threshold;
    + GObject *test;
    + GObject *label;
    + GObject *volume;
    + GObject *threshold;
    char *tmp;
    - label = gtk_label_new(NULL);
    - gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_CAT_SPACE);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new(_("Volume:"));
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    - volume = gtk_volume_button_new();
    - gtk_box_pack_start(GTK_BOX(hbox), volume, TRUE, TRUE, 0);
    + volume = gtk_builder_get_object(builder, "vv.voice.volume");
    + win->vv.voice.volume = GTK_WIDGET(volume);
    gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume),
    purple_prefs_get_int("/purple/media/audio/volume/input") / 100.0);
    g_signal_connect(volume, "value-changed",
    G_CALLBACK(volume_changed_cb), NULL);
    + label = gtk_builder_get_object(builder, "vv.voice.threshold_label");
    tmp = g_strdup_printf(_("Silence threshold: %d%%"),
    purple_prefs_get_int("/purple/media/audio/silence_threshold"));
    - label = gtk_label_new(tmp);
    - gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
    - gtk_label_set_xalign(GTK_LABEL(label), 0.0);
    - gtk_label_set_yalign(GTK_LABEL(label), 0.5);
    + gtk_label_set_text(GTK_LABEL(label), tmp);
    g_free(tmp);
    - threshold = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,
    - 0, 100, 1);
    - gtk_box_pack_start(GTK_BOX(vbox), threshold, FALSE, FALSE, 0);
    +
    + threshold = gtk_builder_get_object(builder, "vv.voice.threshold");
    + win->vv.voice.threshold = GTK_WIDGET(threshold);
    gtk_range_set_value(GTK_RANGE(threshold),
    purple_prefs_get_int("/purple/media/audio/silence_threshold"));
    - gtk_scale_set_draw_value(GTK_SCALE(threshold), FALSE);
    g_signal_connect(threshold, "value-changed",
    G_CALLBACK(threshold_value_changed_cb), label);
    - test = gtk_toggle_button_new_with_label(_("Test Audio"));
    - gtk_box_pack_start(GTK_BOX(vbox), test, FALSE, FALSE, 0);
    -
    - level = gtk_progress_bar_new();
    - gtk_box_pack_start(GTK_BOX(vbox), level, FALSE, FALSE, 0);
    - gtk_widget_set_sensitive(level, FALSE);
    -
    - voice_volume = volume;
    - voice_level = level;
    - voice_threshold = threshold;
    + win->vv.voice.level =
    + GTK_WIDGET(gtk_builder_get_object(builder, "vv.voice.level"));
    +
    + test = gtk_builder_get_object(builder, "vv.voice.test");
    g_signal_connect(test, "toggled",
    G_CALLBACK(toggle_voice_test_cb), win);
    + win->vv.voice.test = GTK_WIDGET(test);
    }
    static GstElement *
    @@ -3482,48 +3161,38 @@
    static void
    video_test_destroy_cb(GtkWidget *w, gpointer data)
    {
    - if (!video_pipeline)
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    +
    + if (!win->vv.video.pipeline) {
    return;
    -
    - gst_element_set_state(video_pipeline, GST_STATE_NULL);
    - gst_object_unref(video_pipeline);
    - video_pipeline = NULL;
    + }
    +
    + gst_element_set_state(win->vv.video.pipeline, GST_STATE_NULL);
    + g_clear_pointer(&win->vv.video.pipeline, gst_object_unref);
    }
    static void
    window_id_cb(GstBus *bus, GstMessage *msg, gulong window_id)
    {
    - if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT
    -#if GST_CHECK_VERSION(1,0,0)
    - || !gst_is_video_overlay_prepare_window_handle_message(msg))
    -#else
    - /* there may be have-xwindow-id also, in case something went wrong */
    - || !gst_structure_has_name(msg->structure, "prepare-xwindow-id"))
    -#endif
    + if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
    + !gst_is_video_overlay_prepare_window_handle_message(msg)) {
    return;
    + }
    g_signal_handlers_disconnect_matched(bus,
    G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
    0, 0, NULL, window_id_cb,
    (gpointer)window_id);
    -#if GST_CHECK_VERSION(1,0,0)
    gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)),
    window_id);
    -#elif GST_CHECK_VERSION(0,10,31)
    - gst_x_overlay_set_window_handle(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)),
    - window_id);
    -#else
    - gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)),
    - window_id);
    -#endif
    }
    static void
    -enable_video_test(void)
    +enable_video_test(PidginPrefsWindow *win)
    {
    GstBus *bus;
    - GdkWindow *window = gtk_widget_get_window(video_drawing_area);
    + GdkWindow *window = gtk_widget_get_window(win->vv.video.drawing_area);
    gulong window_id = 0;
    #ifdef GDK_WINDOWING_WIN32
    @@ -3548,18 +3217,15 @@
    # error "Unsupported GDK windowing system"
    #endif
    - video_pipeline = create_video_pipeline();
    - bus = gst_pipeline_get_bus(GST_PIPELINE(video_pipeline));
    -#if GST_CHECK_VERSION(1,0,0)
    + win->vv.video.pipeline = create_video_pipeline();
    + bus = gst_pipeline_get_bus(GST_PIPELINE(win->vv.video.pipeline));
    gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL, NULL);
    -#else
    - gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL);
    -#endif
    g_signal_connect(bus, "sync-message::element",
    G_CALLBACK(window_id_cb), (gpointer)window_id);
    gst_object_unref(bus);
    - gst_element_set_state(GST_ELEMENT(video_pipeline), GST_STATE_PLAYING);
    + gst_element_set_state(GST_ELEMENT(win->vv.video.pipeline),
    + GST_STATE_PLAYING);
    }
    static void
    @@ -3568,43 +3234,53 @@
    PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    if (gtk_toggle_button_get_active(test)) {
    - enable_video_test();
    + enable_video_test(win);
    g_signal_connect(test, "destroy",
    - G_CALLBACK(video_test_destroy_cb), NULL);
    - g_signal_connect(win->notebook, "switch-page",
    - G_CALLBACK(vv_test_switch_page_cb), test);
    + G_CALLBACK(video_test_destroy_cb), win);
    } else {
    g_object_disconnect(test, "any-signal::destroy",
    - G_CALLBACK(video_test_destroy_cb), NULL,
    + G_CALLBACK(video_test_destroy_cb), win,
    NULL);
    - g_object_disconnect(win->notebook, "any-signal::switch-page",
    - G_CALLBACK(vv_test_switch_page_cb), test,
    - NULL);
    - video_test_destroy_cb(NULL, NULL);
    + video_test_destroy_cb(NULL, win);
    }
    }
    static void
    -make_video_test(PidginPrefsWindow *win, GtkWidget *vbox)
    +bind_video_test(PidginPrefsWindow *win, GtkBuilder *builder)
    {
    - GtkWidget *test;
    GtkWidget *video;
    -
    - video_drawing_area = video = pidgin_create_video_widget();
    - gtk_box_pack_start(GTK_BOX(vbox), video, TRUE, TRUE, 0);
    - gtk_widget_set_size_request(GTK_WIDGET(video), 240, 180);
    -
    - test = gtk_toggle_button_new_with_label(_("Test Video"));
    - gtk_box_pack_start(GTK_BOX(vbox), test, FALSE, FALSE, 0);
    -
    + GObject *test;
    + GdkRGBA color = {0.0, 0.0, 0.0, 1.0};
    +
    + win->vv.video.drawing_area = video = GTK_WIDGET(
    + gtk_builder_get_object(builder, "vv.video.test_area"));
    + gtk_widget_override_background_color(video, GTK_STATE_FLAG_NORMAL,
    + &color);
    +
    + /* In order to enable client shadow decorations, GtkDialog from GTK+ 3.0
    + * uses ARGB visual which by default gets inherited by its child
    + * widgets. XVideo adaptors on the other hand often support just depth
    + * 24 and rendering video through xvimagesink onto a widget inside a
    + * GtkDialog then results in no visible output.
    + *
    + * This ensures the default system visual of the drawing area doesn't
    + * get overridden by the widget's parent.
    + */
    + gtk_widget_set_visual(video, gdk_screen_get_system_visual(
    + gtk_widget_get_screen(video)));
    +
    + test = gtk_builder_get_object(builder, "vv.video.test");
    g_signal_connect(test, "toggled",
    G_CALLBACK(toggle_video_test_cb), win);
    + win->vv.video.test = GTK_WIDGET(test);
    }
    static void
    vv_device_changed_cb(const gchar *name, PurplePrefType type,
    gconstpointer value, gpointer data)
    {
    + PidginPrefsWindow *win = PIDGIN_PREFS_WINDOW(data);
    +
    PurpleMediaManager *manager;
    PurpleMediaElementInfo *info;
    @@ -3613,12 +3289,12 @@
    purple_media_manager_set_active_element(manager, info);
    /* Refresh test viewers */
    - if (strstr(name, "audio") && voice_pipeline) {
    - voice_test_destroy_cb(NULL, NULL);
    - enable_voice_test();
    - } else if(strstr(name, "video") && video_pipeline) {
    - video_test_destroy_cb(NULL, NULL);
    - enable_video_test();
    + if (strstr(name, "audio") && win->vv.voice.pipeline) {
    + voice_test_destroy_cb(NULL, win);
    + enable_voice_test(win);
    + } else if (strstr(name, "video") && win->vv.video.pipeline) {
    + video_test_destroy_cb(NULL, win);
    + enable_video_test(win);
    }
    }
    @@ -3642,11 +3318,9 @@
    return NULL;
    }
    -static GtkWidget *
    -make_vv_dropdown(GtkWidget *parent, GtkSizeGroup *size_group,
    - PurpleMediaElementType element_type)
    +static void
    +bind_vv_dropdown(PidginPrefCombo *combo, PurpleMediaElementType element_type)
    {
    - GtkWidget *label;
    const gchar *preference_key;
    GList *devices;
    @@ -3661,158 +3335,134 @@
    purple_prefs_set_string(preference_key, next->data);
    }
    - label = pidgin_prefs_dropdown_from_list(parent, _("_Device"),
    - PURPLE_PREF_STRING, preference_key, devices);
    -
    + combo->type = PURPLE_PREF_STRING;
    + combo->key = preference_key;
    + pidgin_prefs_bind_dropdown_from_list(combo, devices);
    g_list_free_full(devices, g_free);
    -
    - gtk_size_group_add_widget(size_group, label);
    - gtk_label_set_xalign(GTK_LABEL(label), 0.0);
    - gtk_label_set_yalign(GTK_LABEL(label), 0.5);
    -
    - /* Return the parent GtkBox of dropdown and label, which was created
    - * in pidgin_prefs_dropdown_from_list(). */
    - return gtk_widget_get_parent(label);
    }
    -static GtkWidget *
    -make_vv_frame(GtkWidget *parent, GtkSizeGroup *sg,
    - const gchar *name, PurpleMediaElementType type)
    +static void
    +bind_vv_frame(PidginPrefsWindow *win, PidginPrefCombo *combo,
    + PurpleMediaElementType type)
    {
    - GtkWidget *vbox;
    - GtkWidget *dropdown;
    -
    - vbox = pidgin_make_frame(parent, name);
    -
    - dropdown = make_vv_dropdown(vbox, sg, type);
    -
    - purple_prefs_connect_callback(vbox,
    - purple_media_type_to_preference_key(type),
    - vv_device_changed_cb, vbox);
    - g_signal_connect_swapped(vbox, "destroy",
    - G_CALLBACK(purple_prefs_disconnect_by_handle), vbox);
    -
    - g_object_set_data(G_OBJECT(vbox), "vv_frame", vbox);
    - g_object_set_data(G_OBJECT(vbox), "vv_dropdown", dropdown);
    - g_object_set_data(G_OBJECT(vbox), "vv_size_group", sg);
    - g_object_set_data(G_OBJECT(vbox), "vv_media_type", (gpointer)type);
    -
    - return vbox;
    + bind_vv_dropdown(combo, type);
    +
    + purple_prefs_connect_callback(combo->combo,
    + purple_media_type_to_preference_key(type),
    + vv_device_changed_cb, win);
    + g_signal_connect_swapped(combo->combo, "destroy",
    + G_CALLBACK(purple_prefs_disconnect_by_handle),
    + combo->combo);
    +
    + g_object_set_data(G_OBJECT(combo->combo), "vv_media_type",
    + (gpointer)type);
    + g_object_set_data(G_OBJECT(combo->combo), "vv_combo", combo);
    }
    static void
    device_list_changed_cb(PurpleMediaManager *manager, GtkWidget *widget)
    {
    - GtkWidget *frame;
    - GtkWidget *dropdown;
    + PidginPrefCombo *combo;
    PurpleMediaElementType media_type;
    -
    - gtk_widget_destroy(g_object_get_data(G_OBJECT(widget), "vv_dropdown"));
    -
    - frame = g_object_get_data(G_OBJECT(widget), "vv_frame");
    + GtkTreeModel *model;
    +
    + combo = g_object_get_data(G_OBJECT(widget), "vv_combo");
    media_type = (PurpleMediaElementType)g_object_get_data(G_OBJECT(widget),
    "vv_media_type");
    - dropdown = make_vv_dropdown(frame,
    - g_object_get_data(G_OBJECT(widget), "vv_size_group"),
    - media_type);
    -
    - g_object_set_data(G_OBJECT(widget), "vv_dropdown", dropdown);
    + /* Unbind original connections so we can repopulate the combo box. */
    + g_object_disconnect(combo->combo, "any-signal::changed",
    + G_CALLBACK(bind_dropdown_set), combo, NULL);
    + model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo->combo));
    + gtk_list_store_clear(GTK_LIST_STORE(model));
    +
    + bind_vv_dropdown(combo, media_type);
    }
    static GtkWidget *
    vv_page(PidginPrefsWindow *win)
    {
    + GtkBuilder *builder;
    GtkWidget *ret;
    - GtkWidget *vbox;
    - GtkWidget *frame;
    - GtkSizeGroup *sg;
    PurpleMediaManager *manager;
    - ret = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PIDGIN_HIG_CAT_SPACE);
    - gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER);
    -
    - sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    + builder = gtk_builder_new_from_resource("/im/pidgin/Pidgin/Prefs/vv.ui");
    + gtk_builder_set_translation_domain(builder, PACKAGE);
    +
    + ret = GTK_WIDGET(gtk_builder_get_object(builder, "vv.page"));
    manager = purple_media_manager_get();
    - vbox = pidgin_make_frame(ret, _("Audio"));
    - frame = make_vv_frame(vbox, sg, _("Input"),
    - PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC);
    + win->vv.voice.input.combo = GTK_WIDGET(
    + gtk_builder_get_object(builder, "vv.voice.input.combo"));
    + bind_vv_frame(win, &win->vv.voice.input,
    + PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC);
    g_signal_connect_object(manager, "elements-changed::audiosrc",
    - G_CALLBACK(device_list_changed_cb), frame, 0);
    -
    - frame = make_vv_frame(vbox, sg, _("Output"),
    - PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK);
    + G_CALLBACK(device_list_changed_cb),
    + win->vv.voice.input.combo, 0);
    +
    + win->vv.voice.output.combo = GTK_WIDGET(
    + gtk_builder_get_object(builder, "vv.voice.output.combo"));
    + bind_vv_frame(win, &win->vv.voice.output,
    + PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK);
    g_signal_connect_object(manager, "elements-changed::audiosink",
    - G_CALLBACK(device_list_changed_cb), frame, 0);
    -
    - make_voice_test(win, vbox);
    -
    - vbox = pidgin_make_frame(ret, _("Video"));
    - frame = make_vv_frame(vbox, sg, _("Input"),
    + G_CALLBACK(device_list_changed_cb),
    + win->vv.voice.output.combo, 0);
    +
    + bind_voice_test(win, builder);
    +
    + win->vv.video.input.combo = GTK_WIDGET(
    + gtk_builder_get_object(builder, "vv.video.input.combo"));
    + bind_vv_frame(win, &win->vv.video.input,
    PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC);
    g_signal_connect_object(manager, "elements-changed::videosrc",
    - G_CALLBACK(device_list_changed_cb), frame, 0);
    -
    - frame = make_vv_frame(vbox, sg, _("Output"),
    - PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK);
    + G_CALLBACK(device_list_changed_cb),
    + win->vv.video.input.combo, 0);
    +
    + win->vv.video.output.combo = GTK_WIDGET(
    + gtk_builder_get_object(builder, "vv.video.output.combo"));
    + bind_vv_frame(win, &win->vv.video.output,
    + PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK);
    g_signal_connect_object(manager, "elements-changed::videosink",
    - G_CALLBACK(device_list_changed_cb), frame, 0);
    -
    - make_video_test(win, vbox);
    -
    - gtk_widget_show_all(ret);
    + G_CALLBACK(device_list_changed_cb),
    + win->vv.video.output.combo, 0);
    +
    + bind_video_test(win, builder);
    +
    + g_signal_connect(win->stack, "notify::visible-child",
    + G_CALLBACK(vv_test_switch_page_cb), win);
    +
    + g_object_ref(ret);
    + g_object_unref(builder);
    return ret;
    }
    #endif
    -static int
    -prefs_notebook_add_page(GtkNotebook *notebook, const char *text,
    - GtkWidget *page, int ind)
    +static void
    +prefs_stack_init(PidginPrefsWindow *win)
    {
    - return gtk_notebook_insert_page(notebook, page, gtk_label_new(text), ind);
    -}
    -
    -static void
    -prefs_notebook_init(PidginPrefsWindow *win)
    -{
    - GtkNotebook *notebook = GTK_NOTEBOOK(win->notebook);
    - int notebook_page = 0;
    +#ifdef USE_VV
    + GtkStack *stack = GTK_STACK(win->stack);
    + GtkWidget *vv;
    +#endif
    bind_interface_page(win);
    - notebook_page++;
    -
    -#ifdef _WIN32
    - /* We use the registered default browser in windows */
    - gtk_widget_hide(win->browser.page);
    -#else
    - /* if the user is running Mac OS X, hide the browsers tab */
    - if (purple_running_osx()) {
    - gtk_widget_hide(win->browser.page);
    - } else {
    - bind_browser_page(win);
    - notebook_page++;
    - }
    -#endif
    -
    + bind_browser_page(win);
    bind_conv_page(win);
    - notebook_page++;
    bind_logging_page(win);
    - notebook_page++;
    bind_network_page(win);
    - notebook_page++;
    bind_proxy_page(win);
    - notebook_page++;
    - prefs_notebook_add_page(notebook, _("Password Storage"), keyring_page(), notebook_page++);
    -
    - prefs_notebook_add_page(notebook, _("Sounds"), sound_page(), notebook_page++);
    + bind_keyring_page(win);
    + bind_sound_page(win);
    bind_away_page(win);
    - notebook_page++;
    - prefs_notebook_add_page(notebook, _("Themes"), theme_page(), notebook_page++);
    + bind_theme_page(win);
    #ifdef USE_VV
    - prefs_notebook_add_page(notebook, _("Voice/Video"), vv_page(win), notebook_page++);
    + vv = vv_page(win);
    + gtk_container_add_with_properties(GTK_CONTAINER(stack), vv, "name",
    + "vv", "title", _("Voice/Video"),
    + NULL);
    + g_object_unref(vv);
    #endif
    }
    @@ -3827,8 +3477,8 @@
    );
    /* Main window */
    - gtk_widget_class_bind_template_child(
    - widget_class, PidginPrefsWindow, notebook);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + stack);
    gtk_widget_class_bind_template_callback(widget_class, delete_prefs);
    /* Interface page */
    @@ -4028,6 +3678,40 @@
    gtk_widget_class_bind_template_callback(widget_class,
    proxy_print_option);
    + /* Keyrings page */
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginPrefsWindow, keyring.active.combo);
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginPrefsWindow, keyring.vbox);
    +
    + /* Sounds page */
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.method.combo);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.method_vbox);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.command);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.command_hbox);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.mute);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.conv_focus);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.while_status.combo);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.event.view);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.event.store);
    + gtk_widget_class_bind_template_child(widget_class, PidginPrefsWindow,
    + sound.entry);
    + gtk_widget_class_bind_template_callback(widget_class, sound_cmd_yeah);
    + gtk_widget_class_bind_template_callback(widget_class, prefs_sound_sel);
    + gtk_widget_class_bind_template_callback(widget_class, event_toggled);
    + gtk_widget_class_bind_template_callback(widget_class, select_sound);
    + gtk_widget_class_bind_template_callback(widget_class, test_sound);
    + gtk_widget_class_bind_template_callback(widget_class, reset_sound);
    +
    /* Away page */
    gtk_widget_class_bind_template_child(
    widget_class, PidginPrefsWindow,
    @@ -4049,6 +3733,24 @@
    widget_class, PidginPrefsWindow, away.startup_hbox);
    gtk_widget_class_bind_template_child(
    widget_class, PidginPrefsWindow, away.startup_label);
    +
    + /* Themes page */
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginPrefsWindow, theme.blist);
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginPrefsWindow, theme.status);
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginPrefsWindow, theme.sound);
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginPrefsWindow, theme.smiley);
    + gtk_widget_class_bind_template_callback(widget_class,
    + prefs_set_blist_theme_cb);
    + gtk_widget_class_bind_template_callback(widget_class,
    + prefs_set_status_icon_theme_cb);
    + gtk_widget_class_bind_template_callback(widget_class,
    + prefs_set_sound_theme_cb);
    + gtk_widget_class_bind_template_callback(widget_class,
    + prefs_set_smiley_theme_cb);
    }
    static void
    @@ -4063,7 +3765,7 @@
    /* Create the window */
    gtk_widget_init_template(GTK_WIDGET(win));
    - prefs_notebook_init(win);
    + prefs_stack_init(win);
    /* Refresh the list of themes before showing the preferences window */
    prefs_themes_refresh();
    @@ -4132,10 +3834,6 @@
    /* Themes */
    prefs_themes_init();
    - /* Conversation Themes */
    - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations");
    - purple_prefs_add_string(PIDGIN_PREFS_ROOT "/conversations/theme", "Default");
    -
    /* Smiley Themes */
    purple_prefs_add_none(PIDGIN_PREFS_ROOT "/smileys");
    purple_prefs_add_string(PIDGIN_PREFS_ROOT "/smileys/theme", "Default");
    --- a/pidgin/gtkprivacy.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkprivacy.c Tue Oct 08 21:48:28 2019 -0500
    @@ -30,6 +30,7 @@
    #include "gtkblist.h"
    #include "gtkprivacy.h"
    #include "gtkutils.h"
    +#include "pidginaccountchooser.h"
    #include "gtk3compat.h"
    @@ -200,9 +201,9 @@
    }
    static void
    -select_account_cb(GtkWidget *dropdown, PurpleAccount *account,
    - PidginPrivacyDialog *dialog)
    +select_account_cb(GtkWidget *chooser, PidginPrivacyDialog *dialog)
    {
    + PurpleAccount *account = pidgin_account_chooser_get_selected(chooser);
    gsize i;
    dialog->account = account;
    @@ -250,7 +251,7 @@
    gtk_widget_show(dialog->button_box);
    purple_blist_schedule_save();
    - pidgin_blist_refresh(purple_blist_get_buddy_list());
    + pidgin_blist_refresh(purple_blist_get_default());
    }
    static void
    @@ -355,10 +356,11 @@
    gtk_widget_show(label);
    /* Accounts drop-down */
    - dropdown = pidgin_account_option_menu_new(NULL, FALSE,
    - G_CALLBACK(select_account_cb), NULL, dialog);
    + dropdown = pidgin_account_chooser_new(NULL, FALSE);
    pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("Set privacy for:"), NULL, dropdown, TRUE, NULL);
    - dialog->account = pidgin_account_option_menu_get_selected(dropdown);
    + g_signal_connect(dropdown, "changed", G_CALLBACK(select_account_cb),
    + dialog);
    + dialog->account = pidgin_account_chooser_get_selected(dropdown);
    /* Add the drop-down list with the allow/block types. */
    dialog->type_menu = gtk_combo_box_text_new();
    --- a/pidgin/gtkrequest.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkrequest.c Tue Oct 08 21:48:28 2019 -0500
    @@ -33,6 +33,7 @@
    #include "pidginstock.h"
    #include "gtkblist.h"
    #include "gtkinternal.h"
    +#include "pidginaccountchooser.h"
    #include <gdk/gdkkeysyms.h>
    @@ -43,7 +44,8 @@
    PurpleRequestType type;
    void *user_data;
    - GtkWidget *dialog;
    + /* May be GtkWidget or GtkNativeDialog */
    + gpointer dialog;
    GtkWidget *ok_button;
    @@ -267,9 +269,10 @@
    }
    static void
    -field_account_cb(GObject *w, PurpleAccount *account, PurpleRequestField *field)
    +field_account_cb(GObject *w, PurpleRequestField *field)
    {
    - purple_request_field_account_set_value(field, account);
    + purple_request_field_account_set_value(
    + field, pidgin_account_chooser_get_selected(GTK_WIDGET(w)));
    }
    static void
    @@ -1350,12 +1353,14 @@
    {
    GtkWidget *widget;
    - widget = pidgin_account_option_menu_new(
    - purple_request_field_account_get_default_value(field),
    - purple_request_field_account_get_show_all(field),
    - G_CALLBACK(field_account_cb),
    - purple_request_field_account_get_filter(field),
    - field);
    + widget = pidgin_account_chooser_new(
    + purple_request_field_account_get_default_value(field),
    + purple_request_field_account_get_show_all(field));
    + pidgin_account_chooser_set_filter_func(
    + PIDGIN_ACCOUNT_CHOOSER(widget),
    + purple_request_field_account_get_filter(field));
    + g_signal_connect(widget, "changed", G_CALLBACK(field_account_cb),
    + field);
    gtk_widget_set_tooltip_text(widget, purple_request_field_get_tooltip(field));
    g_signal_connect(widget, "changed",
    @@ -1565,8 +1570,7 @@
    sel_list = gtk_tree_selection_get_selected_rows(selection, &model);
    gtk_tree_model_get_iter(model, &iter, sel_list->data);
    - g_list_foreach(sel_list, (GFunc)gtk_tree_path_free, NULL);
    - g_list_free(sel_list);
    + g_list_free_full(sel_list, (GDestroyNotify)gtk_tree_path_free);
    gtk_tree_model_get(model, &iter, 0, &key, -1);
    @@ -2289,13 +2293,8 @@
    gtk_widget_set_hexpand(widget, TRUE);
    gtk_widget_set_vexpand(widget, TRUE);
    -#if GTK_CHECK_VERSION(3,12,0)
    gtk_widget_set_margin_start(widget, 5);
    gtk_widget_set_margin_end(widget, 5);
    -#else
    - gtk_widget_set_margin_left(widget, 5);
    - gtk_widget_set_margin_right(widget, 5);
    -#endif
    if (type == PURPLE_REQUEST_FIELD_STRING &&
    purple_request_field_string_is_multiline(field))
    @@ -2348,27 +2347,10 @@
    }
    static void
    -file_yes_no_cb(PidginRequestData *data, gint id)
    -{
    - /* Only call the callback if yes was selected, otherwise the request
    - * (eg. file transfer) will be cancelled, then when a new filename is chosen
    - * things go BOOM */
    - if (id == 1) {
    - if (data->cbs[1] != NULL)
    - ((PurpleRequestFileCb)data->cbs[1])(data->user_data, data->u.file.name);
    - purple_request_close(data->type, data);
    - } else {
    - pidgin_clear_cursor(GTK_WIDGET(data->dialog));
    - }
    -}
    -
    -static void
    file_ok_check_if_exists_cb(GtkWidget *widget, gint response, PidginRequestData *data)
    {
    gchar *current_folder;
    - generic_response_start(data);
    -
    if (response != GTK_RESPONSE_ACCEPT) {
    if (data->cbs[0] != NULL)
    ((PurpleRequestFileCb)data->cbs[0])(data->user_data, NULL);
    @@ -2386,16 +2368,10 @@
    }
    g_free(current_folder);
    }
    - if ((data->u.file.savedialog == TRUE) &&
    - (g_file_test(data->u.file.name, G_FILE_TEST_EXISTS))) {
    - purple_request_action(data, NULL, _("That file already exists"),
    - _("Would you like to overwrite it?"), 0,
    - NULL,
    - data, 2,
    - _("Overwrite"), G_CALLBACK(file_yes_no_cb),
    - _("Choose New Name"), G_CALLBACK(file_yes_no_cb));
    - } else
    - file_yes_no_cb(data, 1);
    + if (data->cbs[1] != NULL) {
    + ((PurpleRequestFileCb)data->cbs[1])(data->user_data, data->u.file.name);
    + }
    + purple_request_close(data->type, data);
    }
    static void *
    @@ -2404,7 +2380,7 @@
    PurpleRequestCommonParameters *cpar, void *user_data)
    {
    PidginRequestData *data;
    - GtkWidget *filesel;
    + GtkFileChooserNative *filesel;
    #ifdef _WIN32
    const gchar *current_folder;
    gboolean folder_set = FALSE;
    @@ -2419,20 +2395,15 @@
    data->cbs[1] = ok_cb;
    data->u.file.savedialog = savedialog;
    - filesel = gtk_file_chooser_dialog_new(
    - title ? title : (savedialog ? _("Save File...")
    - : _("Open File...")),
    - NULL,
    - savedialog ? GTK_FILE_CHOOSER_ACTION_SAVE
    - : GTK_FILE_CHOOSER_ACTION_OPEN,
    - _("_Cancel"), GTK_RESPONSE_CANCEL,
    - savedialog ? _("_Save")
    - : _("_Open"),
    - GTK_RESPONSE_ACCEPT,
    - NULL);
    - gtk_dialog_set_default_response(GTK_DIALOG(filesel), GTK_RESPONSE_ACCEPT);
    -
    - pidgin_request_add_help(GTK_DIALOG(filesel), cpar);
    + filesel = gtk_file_chooser_native_new(
    + title ? title
    + : (savedialog ? _("Save File...") : _("Open File...")),
    + NULL,
    + savedialog ? GTK_FILE_CHOOSER_ACTION_SAVE
    + : GTK_FILE_CHOOSER_ACTION_OPEN,
    + savedialog ? _("_Save") : _("_Open"), _("_Cancel"));
    + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(filesel),
    + TRUE);
    if ((filename != NULL) && (*filename != '\0')) {
    if (savedialog)
    @@ -2468,10 +2439,13 @@
    g_signal_connect(G_OBJECT(GTK_FILE_CHOOSER(filesel)), "response",
    G_CALLBACK(file_ok_check_if_exists_cb), data);
    +#if 0
    + /* FIXME: Not implemented for native dialogs. */
    pidgin_auto_parent_window(filesel);
    +#endif
    data->dialog = filesel;
    - gtk_widget_show(filesel);
    + gtk_native_dialog_show(GTK_NATIVE_DIALOG(filesel));
    return (void *)data;
    }
    @@ -2482,7 +2456,7 @@
    void *user_data)
    {
    PidginRequestData *data;
    - GtkWidget *dirsel;
    + GtkFileChooserNative *dirsel;
    data = g_new0(PidginRequestData, 1);
    data->type = PURPLE_REQUEST_FOLDER;
    @@ -2493,16 +2467,9 @@
    data->cbs[1] = ok_cb;
    data->u.file.savedialog = FALSE;
    - dirsel = gtk_file_chooser_dialog_new(
    - title ? title : _("Select Folder..."),
    - NULL,
    - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
    - _("_Cancel"), GTK_RESPONSE_CANCEL,
    - _("_OK"), GTK_RESPONSE_ACCEPT,
    - NULL);
    - gtk_dialog_set_default_response(GTK_DIALOG(dirsel), GTK_RESPONSE_ACCEPT);
    -
    - pidgin_request_add_help(GTK_DIALOG(dirsel), cpar);
    + dirsel = gtk_file_chooser_native_new(
    + title ? title : _("Select Folder..."), NULL,
    + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, _("_OK"), _("_Cancel"));
    if ((dirname != NULL) && (*dirname != '\0'))
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dirsel), dirname);
    @@ -2511,9 +2478,12 @@
    G_CALLBACK(file_ok_check_if_exists_cb), data);
    data->dialog = dirsel;
    +#if 0
    + /* FIXME: Not implemented for native dialogs. */
    pidgin_auto_parent_window(dirsel);
    -
    - gtk_widget_show(dirsel);
    +#endif
    +
    + gtk_native_dialog_show(GTK_NATIVE_DIALOG(dirsel));
    return (void *)data;
    }
    @@ -2553,9 +2523,14 @@
    g_free(data->cbs);
    - pidgin_window_detach_children(GTK_WINDOW(data->dialog));
    -
    - gtk_widget_destroy(data->dialog);
    + if (type == PURPLE_REQUEST_FILE || type == PURPLE_REQUEST_FOLDER) {
    + /* Will be a GtkNativeDialog, not GtkDialog. */
    + g_object_unref(data->dialog);
    + } else {
    + pidgin_window_detach_children(GTK_WINDOW(data->dialog));
    +
    + gtk_widget_destroy(data->dialog);
    + }
    if (type == PURPLE_REQUEST_FIELDS)
    purple_request_fields_destroy(data->u.multifield.fields);
    @@ -2573,6 +2548,13 @@
    g_return_val_if_fail(
    purple_request_is_valid_ui_handle(data, NULL), NULL);
    + if (data->type == PURPLE_REQUEST_FILE ||
    + data->type == PURPLE_REQUEST_FOLDER) {
    + /* Not a GtkWidget, but a GtkFileChooserNative. Eventually this function
    + * should not be needed, once we don't need to auto-parent. */
    + return NULL;
    + }
    +
    return GTK_WINDOW(data->dialog);
    }
    --- a/pidgin/gtkroomlist.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkroomlist.c Tue Oct 08 21:48:28 2019 -0500
    @@ -22,6 +22,7 @@
    #include "internal.h"
    #include "pidgin.h"
    #include "gtkutils.h"
    +#include "pidginaccountchooser.h"
    #include "pidginstock.h"
    #include "pidgintooltip.h"
    @@ -104,9 +105,10 @@
    return FALSE;
    }
    -static void dialog_select_account_cb(GObject *w, PurpleAccount *account,
    - PidginRoomlistDialog *dialog)
    +static void
    +dialog_select_account_cb(GtkWidget *chooser, PidginRoomlistDialog *dialog)
    {
    + PurpleAccount *account = pidgin_account_chooser_get_selected(chooser);
    gboolean change = (account != dialog->account);
    dialog->account = account;
    @@ -231,7 +233,7 @@
    if(gc != NULL)
    protocol = purple_connection_get_protocol(gc);
    - if(protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, room_serialize))
    + if(protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, room_serialize))
    name = purple_protocol_roomlist_iface_room_serialize(protocol, info->room);
    else
    name = g_strdup(purple_roomlist_room_get_name(info->room));
    @@ -503,7 +505,7 @@
    if (conn && PURPLE_CONNECTION_IS_CONNECTED(conn))
    protocol = purple_connection_get_protocol(conn);
    - return (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST_IFACE, get_list));
    + return (protocol && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, get_list));
    }
    gboolean
    @@ -545,10 +547,16 @@
    gtk_widget_show(vbox2);
    /* accounts dropdown list */
    - dialog->account_widget = pidgin_account_option_menu_new(dialog->account, FALSE,
    - G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog);
    + dialog->account_widget =
    + pidgin_account_chooser_new(dialog->account, FALSE);
    + pidgin_account_chooser_set_filter_func(
    + PIDGIN_ACCOUNT_CHOOSER(dialog->account_widget),
    + account_filter_func);
    + g_signal_connect(dialog->account_widget, "changed",
    + G_CALLBACK(dialog_select_account_cb), dialog);
    if (!dialog->account) /* this is normally null, and we normally don't care what the first selected item is */
    - dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget);
    + dialog->account = pidgin_account_chooser_get_selected(
    + dialog->account_widget);
    pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL);
    /* scrolled window */
    --- a/pidgin/gtksavedstatuses.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtksavedstatuses.c Tue Oct 08 21:48:28 2019 -0500
    @@ -209,8 +209,7 @@
    purple_savedstatus_activate(saved_status);
    }
    - g_list_foreach(list, (GFunc)gtk_tree_path_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, (GDestroyNotify)gtk_tree_path_free);
    }
    static void
    @@ -247,8 +246,7 @@
    status_window_delete_cancel_cb(gpointer data)
    {
    GList *sel_titles = data;
    - g_list_foreach(sel_titles, (GFunc) g_free, NULL);
    - g_list_free(sel_titles);
    + g_list_free_full(sel_titles, g_free);
    }
    static void
    --- a/pidgin/gtkscrollbook.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkscrollbook.c Tue Oct 08 21:48:28 2019 -0500
    @@ -255,6 +255,15 @@
    static void
    pidgin_scroll_book_init (PidginScrollBook *scroll_book)
    {
    + const gchar *left_arrow_icon_names[] = {
    + "pan-start-symbolic",
    + "pan-left-symbolic",
    + };
    + const gchar *right_arrow_icon_names[] = {
    + "pan-end-symbolic",
    + "pan-right-symbolic",
    + };
    + GIcon *icon;
    GtkWidget *eb;
    GtkWidget *close_button;
    @@ -276,11 +285,11 @@
    /* Right arrow */
    eb = gtk_event_box_new();
    gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
    -#if GTK_CHECK_VERSION(3,14,0)
    - scroll_book->right_arrow = gtk_image_new_from_icon_name("pan-right-symbolic", GTK_ICON_SIZE_BUTTON);
    -#else
    - scroll_book->right_arrow = gtk_arrow_new(GTK_ARROW_RIGHT, GTK_SHADOW_NONE);
    -#endif
    + icon = g_themed_icon_new_from_names((char **)right_arrow_icon_names,
    + G_N_ELEMENTS(right_arrow_icon_names));
    + scroll_book->right_arrow =
    + gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_BUTTON);
    + g_object_unref(icon);
    gtk_container_add(GTK_CONTAINER(eb), scroll_book->right_arrow);
    g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_right_cb), scroll_book);
    @@ -291,11 +300,11 @@
    /* Left arrow */
    eb = gtk_event_box_new();
    gtk_box_pack_end(GTK_BOX(scroll_book->hbox), eb, FALSE, FALSE, 0);
    -#if GTK_CHECK_VERSION(3,14,0)
    - scroll_book->left_arrow = gtk_image_new_from_icon_name("pan-left-symbolic", GTK_ICON_SIZE_BUTTON);
    -#else
    - scroll_book->left_arrow = gtk_arrow_new(GTK_ARROW_LEFT, GTK_SHADOW_NONE);
    -#endif
    + icon = g_themed_icon_new_from_names((char **)left_arrow_icon_names,
    + G_N_ELEMENTS(left_arrow_icon_names));
    + scroll_book->left_arrow =
    + gtk_image_new_from_gicon(icon, GTK_ICON_SIZE_BUTTON);
    + g_object_unref(icon);
    gtk_container_add(GTK_CONTAINER(eb), scroll_book->left_arrow);
    g_signal_connect_swapped(G_OBJECT(eb), "button-press-event", G_CALLBACK(scroll_left_cb), scroll_book);
    --- a/pidgin/gtksmiley-manager.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtksmiley-manager.c Tue Oct 08 21:48:28 2019 -0500
    @@ -180,14 +180,15 @@
    static void
    edit_dialog_image_choose(GtkWidget *widget, gpointer _edit_dialog)
    {
    - GtkWidget *file_chooser;
    + GtkFileChooserNative *file_chooser;
    file_chooser = pidgin_buddy_icon_chooser_new(
    GTK_WINDOW(gtk_widget_get_toplevel(widget)),
    edit_dialog_image_choosen, _edit_dialog);
    gtk_window_set_title(GTK_WINDOW(file_chooser), _("Custom Smiley"));
    gtk_window_set_role(GTK_WINDOW(file_chooser),
    "file-selector-custom-smiley");
    - gtk_widget_show_all(file_chooser);
    + gtk_native_dialog_run(GTK_NATIVE_DIALOG(file_chooser));
    + g_object_unref(file_chooser);
    }
    --- a/pidgin/gtksmiley-theme.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtksmiley-theme.c Tue Oct 08 21:48:28 2019 -0500
    @@ -385,32 +385,32 @@
    const gchar *
    pidgin_smiley_theme_get_name(PidginSmileyTheme *theme)
    {
    - PidginSmileyThemePrivate *priv =
    - pidgin_smiley_theme_get_instance_private(theme);
    + PidginSmileyThemePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme), NULL);
    + priv = pidgin_smiley_theme_get_instance_private(theme);
    return priv->name;
    }
    const gchar *
    pidgin_smiley_theme_get_description(PidginSmileyTheme *theme)
    {
    - PidginSmileyThemePrivate *priv =
    - pidgin_smiley_theme_get_instance_private(theme);
    + PidginSmileyThemePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme), NULL);
    + priv = pidgin_smiley_theme_get_instance_private(theme);
    return priv->desc;
    }
    GdkPixbuf *
    pidgin_smiley_theme_get_icon(PidginSmileyTheme *theme)
    {
    - PidginSmileyThemePrivate *priv =
    - pidgin_smiley_theme_get_instance_private(theme);
    + PidginSmileyThemePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme), NULL);
    + priv = pidgin_smiley_theme_get_instance_private(theme);
    if (priv->icon == NULL)
    return NULL;
    @@ -428,11 +428,11 @@
    const gchar *
    pidgin_smiley_theme_get_author(PidginSmileyTheme *theme)
    {
    - PidginSmileyThemePrivate *priv =
    - pidgin_smiley_theme_get_instance_private(theme);
    + PidginSmileyThemePrivate *priv = NULL;
    - g_return_val_if_fail(priv != NULL, NULL);
    + g_return_val_if_fail(PIDGIN_IS_SMILEY_THEME(theme), NULL);
    + priv = pidgin_smiley_theme_get_instance_private(theme);
    return priv->author;
    }
    @@ -465,8 +465,6 @@
    GHashTable *smap;
    GList *it, *it2, *it3;
    - g_return_if_fail(priv != NULL);
    -
    if (priv->smiley_lists_map)
    return;
    @@ -557,8 +555,8 @@
    probe_dirs = g_new0(gchar*, 3);
    probe_dirs[0] = g_build_filename(
    PURPLE_DATADIR, "pixmaps", "pidgin", "emotes", NULL);
    - user_smileys_dir = probe_dirs[1] = g_build_filename(
    - purple_user_dir(), "smileys", NULL);
    + user_smileys_dir = probe_dirs[1] =
    + g_build_filename(purple_data_dir(), "smileys", NULL);
    if (!g_file_test(user_smileys_dir, G_FILE_TEST_IS_DIR)) {
    if (g_mkdir(user_smileys_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0) {
    --- a/pidgin/gtkstatusbox.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkstatusbox.c Tue Oct 08 21:48:28 2019 -0500
    @@ -445,7 +445,7 @@
    g_clear_object(&statusbox->buddy_icon);
    g_clear_object(&statusbox->buddy_icon_hover);
    - g_clear_pointer(&statusbox->buddy_icon_sel, gtk_widget_destroy);
    + g_clear_object(&statusbox->buddy_icon_sel);
    g_clear_pointer(&statusbox->icon_box_menu, gtk_widget_destroy);
    @@ -713,7 +713,7 @@
    }
    static PurpleStatusType *
    -find_status_type_by_index(const PurpleAccount *account, gint active)
    +find_status_type_by_index(PurpleAccount *account, gint active)
    {
    GList *l = purple_account_get_status_types(account);
    gint i;
    @@ -1241,24 +1241,13 @@
    static gboolean
    popup_grab_on_window(GdkWindow *window, GdkEvent *event)
    {
    - guint32 activate_time = gdk_event_get_time(event);
    - GdkDevice *device = gdk_event_get_device(event);
    + GdkSeat *seat = gdk_event_get_seat(event);
    GdkGrabStatus status;
    - status = gdk_device_grab(device, window, GDK_OWNERSHIP_WINDOW, TRUE,
    - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
    - GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK |
    - GDK_KEY_RELEASE_MASK, NULL, activate_time);
    + status = gdk_seat_grab(seat, window, GDK_SEAT_CAPABILITY_ALL, TRUE, NULL,
    + event, NULL, NULL);
    if (status == GDK_GRAB_SUCCESS) {
    - status = gdk_device_grab(gdk_device_get_associated_device(device),
    - window, GDK_OWNERSHIP_WINDOW, TRUE,
    - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
    - GDK_POINTER_MOTION_MASK | GDK_KEY_PRESS_MASK |
    - GDK_KEY_RELEASE_MASK, NULL, activate_time);
    - if (status == GDK_GRAB_SUCCESS)
    - return TRUE;
    - else
    - gdk_device_ungrab(device, activate_time);
    + return TRUE;
    }
    return FALSE;
    @@ -1294,16 +1283,13 @@
    static void
    pidgin_status_box_popdown(PidginStatusBox *box, GdkEvent *event)
    {
    - guint32 time;
    - GdkDevice *device;
    + GdkSeat *seat;
    gtk_widget_hide(box->popup_window);
    box->popup_in_progress = FALSE;
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(box->toggle_button), FALSE);
    gtk_grab_remove(box->popup_window);
    - time = gdk_event_get_time(event);
    - device = gdk_event_get_device(event);
    - gdk_device_ungrab(device, time);
    - gdk_device_ungrab(gdk_device_get_associated_device(device), time);
    + seat = gdk_event_get_seat(event);
    + gdk_seat_ungrab(seat);
    }
    static gboolean
    @@ -1410,12 +1396,10 @@
    static void
    choose_buddy_icon_cb(GtkWidget *w, PidginStatusBox *box)
    {
    - if (box->buddy_icon_sel) {
    - gtk_window_present(GTK_WINDOW(box->buddy_icon_sel));
    - } else {
    + if (box->buddy_icon_sel == NULL) {
    box->buddy_icon_sel = pidgin_buddy_icon_chooser_new(GTK_WINDOW(gtk_widget_get_toplevel(w)), icon_choose_cb, box);
    - gtk_widget_show_all(box->buddy_icon_sel);
    }
    + gtk_native_dialog_show(GTK_NATIVE_DIALOG(box->buddy_icon_sel));
    }
    static void
    @@ -1430,7 +1414,7 @@
    buddy_icon_set_cb(filename, box);
    }
    - box->buddy_icon_sel = NULL;
    + g_clear_object(&box->buddy_icon_sel);
    }
    static void
    @@ -1646,11 +1630,7 @@
    status_box->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
    status_box->cell_view = gtk_cell_view_new();
    status_box->vsep = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
    -#if GTK_CHECK_VERSION(3,14,0)
    status_box->arrow = gtk_image_new_from_icon_name("pan-down-symbolic", GTK_ICON_SIZE_BUTTON);
    -#else
    - status_box->arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE);
    -#endif
    status_box->store = gtk_list_store_new(NUM_COLUMNS, G_TYPE_INT, G_TYPE_STRING, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
    G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN);
    @@ -1667,7 +1647,7 @@
    gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->vsep, FALSE, FALSE, 0);
    gtk_box_pack_start(GTK_BOX(status_box->hbox), status_box->arrow, FALSE, FALSE, 0);
    gtk_widget_show_all(status_box->toggle_button);
    - gtk_button_set_focus_on_click(GTK_BUTTON(status_box->toggle_button), FALSE);
    + gtk_widget_set_focus_on_click(status_box->toggle_button, FALSE);
    text_rend = gtk_cell_renderer_text_new();
    icon_rend = gtk_cell_renderer_pixbuf_new();
    --- a/pidgin/gtkstatusbox.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkstatusbox.h Tue Oct 08 21:48:28 2019 -0500
    @@ -98,7 +98,7 @@
    PurpleImage *buddy_icon_img;
    GdkPixbuf *buddy_icon;
    GdkPixbuf *buddy_icon_hover;
    - GtkWidget *buddy_icon_sel;
    + GtkFileChooserNative *buddy_icon_sel;
    GtkWidget *icon;
    GtkWidget *icon_box;
    GtkWidget *icon_box_menu;
    --- a/pidgin/gtkutils.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkutils.c Tue Oct 08 21:48:28 2019 -0500
    @@ -18,7 +18,6 @@
    * along with this program; if not, write to the Free Software
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#define _PIDGIN_GTKUTILS_C_
    #include "internal.h"
    #include "glibcompat.h"
    @@ -31,6 +30,8 @@
    #include <gdk/gdkkeysyms.h>
    +#include <talkatu.h>
    +
    #include "conversation.h"
    #include "debug.h"
    #include "notify.h"
    @@ -49,8 +50,6 @@
    #include "pidginstock.h"
    #include "gtkrequest.h"
    #include "gtkutils.h"
    -#include "gtkwebview.h"
    -#include "gtkwebviewtoolbar.h"
    #include "pidgin/minidialog.h"
    #include "gtk3compat.h"
    @@ -109,7 +108,7 @@
    } PidginCompletionData;
    struct _icon_chooser {
    - GtkWidget *icon_filesel;
    + GtkFileChooserNative *icon_filesel;
    GtkWidget *icon_preview;
    GtkWidget *icon_text;
    @@ -128,50 +127,11 @@
    *****************************************************************************/
    static guint accels_save_timer = 0;
    -static GSList *registered_url_handlers = NULL;
    static GSList *minidialogs = NULL;
    /******************************************************************************
    * Code
    *****************************************************************************/
    -
    -static gboolean
    -url_clicked_idle_cb(gpointer data)
    -{
    - purple_notify_uri(NULL, data);
    - g_free(data);
    - return FALSE;
    -}
    -
    -static gboolean
    -url_clicked_cb(PidginWebView *unused, const char *uri)
    -{
    - g_idle_add(url_clicked_idle_cb, g_strdup(uri));
    - return TRUE;
    -}
    -
    -void
    -pidgin_setup_webview(GtkWidget *webview)
    -{
    - g_return_if_fail(webview != NULL);
    - g_return_if_fail(PIDGIN_IS_WEBVIEW(webview));
    -
    -#ifdef _WIN32
    - if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/use_theme_font")) {
    - WebKitWebSettings *settings = webkit_web_settings_new();
    - g_object_set(G_OBJECT(settings), "default-font-size",
    - purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size"),
    - NULL);
    - g_object_set(G_OBJECT(settings), "default-font-family",
    - purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/custom_font"),
    - NULL);
    -
    - webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webview), settings);
    - g_object_unref(settings);
    - }
    -#endif
    -}
    -
    static
    void pidgin_window_init(GtkWindow *wnd, const char *title, guint border_width, const char *role, gboolean resizable)
    {
    @@ -207,7 +167,7 @@
    gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
    /* don't allow focus on the close button */
    - gtk_button_set_focus_on_click(GTK_BUTTON(button), FALSE);
    + gtk_widget_set_focus_on_click(button, FALSE);
    gtk_widget_show(image);
    @@ -292,61 +252,6 @@
    return button;
    }
    -GtkWidget *
    -pidgin_create_webview(gboolean editable, GtkWidget **webview_ret, GtkWidget **sw_ret)
    -{
    - GtkWidget *frame;
    - GtkWidget *webview;
    - GtkWidget *sep;
    - GtkWidget *sw;
    - GtkWidget *toolbar = NULL;
    - GtkWidget *vbox;
    -
    - frame = gtk_frame_new(NULL);
    - gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
    -
    - vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    - gtk_container_add(GTK_CONTAINER(frame), vbox);
    - gtk_widget_show(vbox);
    -
    - if (editable) {
    - toolbar = pidgin_webviewtoolbar_new();
    - gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
    - gtk_widget_show(toolbar);
    -
    - sep = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
    - gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
    - g_signal_connect_swapped(G_OBJECT(toolbar), "show", G_CALLBACK(gtk_widget_show), sep);
    - g_signal_connect_swapped(G_OBJECT(toolbar), "hide", G_CALLBACK(gtk_widget_hide), sep);
    - gtk_widget_show(sep);
    - }
    -
    - webview = pidgin_webview_new(editable);
    - if (editable && purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/spellcheck"))
    - pidgin_webview_set_spellcheck(PIDGIN_WEBVIEW(webview), TRUE);
    - gtk_widget_show(webview);
    -
    - if (editable) {
    - pidgin_webviewtoolbar_attach(PIDGIN_WEBVIEWTOOLBAR(toolbar), webview);
    - pidgin_webview_set_toolbar(PIDGIN_WEBVIEW(webview), toolbar);
    - }
    - pidgin_setup_webview(webview);
    -
    - sw = pidgin_make_scrollable(webview, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_NONE, -1, -1);
    - gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
    -
    - pidgin_webview_set_vadjustment(PIDGIN_WEBVIEW(webview),
    - gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)));
    -
    - if (webview_ret != NULL)
    - *webview_ret = webview;
    -
    - if (sw_ret != NULL)
    - *sw_ret = sw;
    -
    - return frame;
    -}
    -
    void
    pidgin_set_sensitive_if_input(GtkWidget *entry, GtkWidget *dialog)
    {
    @@ -685,164 +590,6 @@
    return (const char *)aop_option_menu_get_selected(optmenu);
    }
    -PurpleAccount *
    -pidgin_account_option_menu_get_selected(GtkWidget *optmenu)
    -{
    - return (PurpleAccount *)aop_option_menu_get_selected(optmenu);
    -}
    -
    -static AopMenu *
    -create_account_menu(PurpleAccount *default_account,
    - PurpleFilterAccountFunc filter_func, gboolean show_all)
    -{
    - AopMenu *aop_menu = NULL;
    - PurpleAccount *account;
    - GdkPixbuf *pixbuf = NULL;
    - GList *list;
    - GList *p;
    - GtkListStore *ls;
    - GtkTreeIter iter;
    - int i;
    - char buf[256];
    -
    - if (show_all)
    - list = purple_accounts_get_all();
    - else
    - list = purple_connections_get_all();
    -
    - ls = gtk_list_store_new(AOP_COLUMN_COUNT, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
    -
    - aop_menu = g_malloc0(sizeof(AopMenu));
    - aop_menu->default_item = 0;
    - aop_menu->model = GTK_TREE_MODEL(ls);
    -
    - for (p = list, i = 0; p != NULL; p = p->next, i++) {
    - if (show_all)
    - account = (PurpleAccount *)p->data;
    - else {
    - PurpleConnection *gc = (PurpleConnection *)p->data;
    -
    - account = purple_connection_get_account(gc);
    - }
    -
    - if (filter_func && !filter_func(account)) {
    - i--;
    - continue;
    - }
    -
    - pixbuf = pidgin_create_protocol_icon(account, PIDGIN_PROTOCOL_ICON_SMALL);
    -
    - if (pixbuf) {
    - if (purple_account_is_disconnected(account) && show_all &&
    - purple_connections_get_all())
    - gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
    - }
    -
    - if (purple_account_get_private_alias(account)) {
    - g_snprintf(buf, sizeof(buf), "%s (%s) (%s)",
    - purple_account_get_username(account),
    - purple_account_get_private_alias(account),
    - purple_account_get_protocol_name(account));
    - } else {
    - g_snprintf(buf, sizeof(buf), "%s (%s)",
    - purple_account_get_username(account),
    - purple_account_get_protocol_name(account));
    - }
    -
    - gtk_list_store_append(ls, &iter);
    - gtk_list_store_set(ls, &iter,
    - AOP_ICON_COLUMN, pixbuf,
    - AOP_NAME_COLUMN, buf,
    - AOP_DATA_COLUMN, account,
    - -1);
    -
    - if (pixbuf)
    - g_object_unref(pixbuf);
    -
    - if (default_account && account == default_account)
    - aop_menu->default_item = i;
    - }
    -
    - return aop_menu;
    -}
    -
    -static void
    -regenerate_account_menu(GtkWidget *optmenu)
    -{
    - gboolean show_all;
    - PurpleAccount *account;
    - PurpleFilterAccountFunc filter_func;
    -
    - account = (PurpleAccount *)aop_option_menu_get_selected(optmenu);
    - show_all = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(optmenu), "show_all"));
    - filter_func = g_object_get_data(G_OBJECT(optmenu), "filter_func");
    -
    - aop_option_menu_replace_menu(optmenu, create_account_menu(account, filter_func, show_all));
    -}
    -
    -static void
    -account_menu_sign_on_off_cb(PurpleConnection *gc, GtkWidget *optmenu)
    -{
    - regenerate_account_menu(optmenu);
    -}
    -
    -static void
    -account_menu_added_removed_cb(PurpleAccount *account, GtkWidget *optmenu)
    -{
    - regenerate_account_menu(optmenu);
    -}
    -
    -static gboolean
    -account_menu_destroyed_cb(GtkWidget *optmenu, GdkEvent *event,
    - void *user_data)
    -{
    - purple_signals_disconnect_by_handle(optmenu);
    -
    - return FALSE;
    -}
    -
    -void
    -pidgin_account_option_menu_set_selected(GtkWidget *optmenu, PurpleAccount *account)
    -{
    - aop_option_menu_select_by_data(optmenu, account);
    -}
    -
    -GtkWidget *
    -pidgin_account_option_menu_new(PurpleAccount *default_account,
    - gboolean show_all, GCallback cb,
    - PurpleFilterAccountFunc filter_func,
    - gpointer user_data)
    -{
    - GtkWidget *optmenu;
    -
    - /* Create the option menu */
    - optmenu = aop_option_menu_new(create_account_menu(default_account, filter_func, show_all), cb, user_data);
    -
    - g_signal_connect(G_OBJECT(optmenu), "destroy",
    - G_CALLBACK(account_menu_destroyed_cb), NULL);
    -
    - /* Register the purple sign on/off event callbacks. */
    - purple_signal_connect(purple_connections_get_handle(), "signed-on",
    - optmenu, PURPLE_CALLBACK(account_menu_sign_on_off_cb),
    - optmenu);
    - purple_signal_connect(purple_connections_get_handle(), "signed-off",
    - optmenu, PURPLE_CALLBACK(account_menu_sign_on_off_cb),
    - optmenu);
    - purple_signal_connect(purple_accounts_get_handle(), "account-added",
    - optmenu, PURPLE_CALLBACK(account_menu_added_removed_cb),
    - optmenu);
    - purple_signal_connect(purple_accounts_get_handle(), "account-removed",
    - optmenu, PURPLE_CALLBACK(account_menu_added_removed_cb),
    - optmenu);
    -
    - /* Set some data. */
    - g_object_set_data(G_OBJECT(optmenu), "user_data", user_data);
    - g_object_set_data(G_OBJECT(optmenu), "show_all", GINT_TO_POINTER(show_all));
    - g_object_set_data(G_OBJECT(optmenu), "filter_func", filter_func);
    -
    - return optmenu;
    -}
    -
    void
    pidgin_save_accels_cb(GtkAccelGroup *accel_group, guint arg1,
    GdkModifierType arg2, GClosure *arg3,
    @@ -861,8 +608,7 @@
    {
    char *filename = NULL;
    - filename = g_build_filename(purple_user_dir(), G_DIR_SEPARATOR_S,
    - "accels", NULL);
    + filename = g_build_filename(purple_config_dir(), "accels", NULL);
    purple_debug(PURPLE_DEBUG_MISC, "accels", "saving accels to %s\n", filename);
    gtk_accel_map_save(filename);
    g_free(filename);
    @@ -876,8 +622,7 @@
    {
    char *filename = NULL;
    - filename = g_build_filename(purple_user_dir(), G_DIR_SEPARATOR_S,
    - "accels", NULL);
    + filename = g_build_filename(purple_config_dir(), "accels", NULL);
    gtk_accel_map_load(filename);
    g_free(filename);
    }
    @@ -1260,19 +1005,12 @@
    *y = CLAMP (*y, monitor.y,
    monitor.y + monitor.height - requisition.height);
    - }
    - else if (requisition.height > space_below &&
    - requisition.height > space_above)
    - {
    + } else {
    if (space_below >= space_above)
    *y = monitor.y + monitor.height - requisition.height;
    else
    *y = monitor.y;
    }
    - else
    - {
    - *y = monitor.y;
    - }
    }
    @@ -1337,7 +1075,6 @@
    GStatBuf st;
    GError *err = NULL;
    PurpleConversation *conv;
    - PidginConversation *gtkconv;
    PurpleBuddy *buddy;
    PurpleContact *contact;
    PurpleImage *img;
    @@ -1369,7 +1106,6 @@
    break;
    case DND_IM_IMAGE:
    conv = PURPLE_CONVERSATION(purple_im_conversation_new(data->account, data->who));
    - gtkconv = PIDGIN_CONVERSATION(conv);
    if (!g_file_get_contents(data->filename, &filedata, &size,
    &err)) {
    @@ -1389,7 +1125,8 @@
    img = purple_image_new_from_data((guint8 *)filedata, size);
    purple_image_set_friendly_filename(img, shortname);
    - pidgin_webview_insert_image(PIDGIN_WEBVIEW(gtkconv->entry), img);
    +# warning fix this when talkatu has a way to programmatically insert an image
    + // pidgin_webview_insert_image(PIDGIN_WEBVIEW(gtkconv->entry), img);
    g_object_unref(img);
    break;
    @@ -1424,7 +1161,7 @@
    {
    PurpleConnection *gc = purple_account_get_connection(account);
    PurpleProtocol *protocol = NULL;
    - _DndData *data = g_malloc(sizeof(_DndData));
    + _DndData *data = g_new0(_DndData, 1);
    gboolean ft = FALSE, im = FALSE;
    data->who = g_strdup(who);
    @@ -1540,7 +1277,7 @@
    /* If any of this is null, do nothing. */
    - if (!name || !type || url) {
    + if (!name || !type || !url) {
    g_free(type);
    g_free(name);
    g_free(url);
    @@ -1564,11 +1301,19 @@
    "launcher itself."), NULL);
    } else {
    + GtkTextBuffer *buffer = NULL;
    + GtkTextMark *mark = NULL;
    + GtkTextIter iter;
    conv = PURPLE_CONVERSATION(purple_im_conversation_new(account, who));
    gtkconv = PIDGIN_CONVERSATION(conv);
    - pidgin_webview_insert_link(PIDGIN_WEBVIEW(gtkconv->entry),
    - url, name);
    +
    + buffer = talkatu_editor_get_buffer(TALKATU_EDITOR(gtkconv->editor));
    + mark = gtk_text_buffer_get_insert(buffer);
    +
    + gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
    +
    + talkatu_buffer_insert_link(TALKATU_BUFFER(buffer), &iter, name, url);
    }
    g_free(type);
    @@ -1759,29 +1504,13 @@
    {
    GtkWidget *menuitem;
    GList *list;
    - const gchar *stock_id;
    - GtkWidget *icon_image = NULL;
    if (act == NULL) {
    return pidgin_separator(menu);
    }
    - stock_id = purple_action_menu_get_stock_icon(act);
    - if (stock_id) {
    - icon_image = gtk_image_new_from_stock(stock_id,
    - gtk_icon_size_from_name(
    - PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
    - }
    -
    - if (icon_image) {
    - menuitem = gtk_image_menu_item_new_with_mnemonic(
    - purple_action_menu_get_label(act));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem),
    - icon_image);
    - } else {
    - menuitem = gtk_menu_item_new_with_mnemonic(
    - purple_action_menu_get_label(act));
    - }
    + menuitem = gtk_menu_item_new_with_mnemonic(
    + purple_action_menu_get_label(act));
    list = purple_action_menu_get_children(act);
    @@ -2001,8 +1730,8 @@
    gtk_list_store_clear(data->store);
    - for (gnode = purple_blist_get_buddy_list()->root; gnode != NULL; gnode = gnode->next)
    - {
    + for (gnode = purple_blist_get_default_root(); gnode != NULL;
    + gnode = gnode->next) {
    if (!PURPLE_IS_GROUP(gnode))
    continue;
    @@ -2053,7 +1782,9 @@
    }
    void
    -pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *accountopt, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data)
    +pidgin_setup_screenname_autocomplete(
    + GtkWidget *entry, GtkWidget *chooser,
    + PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data)
    {
    PidginCompletionData *data;
    @@ -2072,7 +1803,7 @@
    G_TYPE_POINTER);
    data->entry = entry;
    - data->accountopt = accountopt;
    + data->accountopt = chooser;
    if (filter_func == NULL) {
    data->filter_func = pidgin_screenname_autocomplete_default_filter;
    data->filter_func_user_data = NULL;
    @@ -2160,10 +1891,6 @@
    char *filename, *current_folder;
    if (response != GTK_RESPONSE_ACCEPT) {
    - if (response == GTK_RESPONSE_CANCEL) {
    - gtk_widget_destroy(dialog->icon_filesel);
    - }
    - dialog->icon_filesel = NULL;
    if (dialog->callback)
    dialog->callback(NULL, dialog->data);
    g_free(dialog);
    @@ -2180,7 +1907,6 @@
    if (dialog->callback)
    dialog->callback(filename, dialog->data);
    - gtk_widget_destroy(dialog->icon_filesel);
    g_free(filename);
    g_free(dialog);
    }
    @@ -2208,7 +1934,7 @@
    gdk_pixbuf_get_file_info(filename, &width, &height);
    basename = g_path_get_basename(filename);
    - size = purple_str_size_to_units(st.st_size);
    + size = g_format_size(st.st_size);
    markup = g_strdup_printf(_("<b>File:</b> %s\n"
    "<b>File size:</b> %s\n"
    "<b>Image size:</b> %dx%d"),
    @@ -2224,8 +1950,11 @@
    g_free(markup);
    }
    -
    -GtkWidget *pidgin_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char *, gpointer), gpointer data) {
    +GtkFileChooserNative *
    +pidgin_buddy_icon_chooser_new(GtkWindow *parent,
    + void (*callback)(const char *, gpointer),
    + gpointer data)
    +{
    struct _icon_chooser *dialog = g_new0(struct _icon_chooser, 1);
    GtkWidget *vbox;
    @@ -2236,13 +1965,9 @@
    current_folder = purple_prefs_get_path(PIDGIN_PREFS_ROOT "/filelocations/last_icon_folder");
    - dialog->icon_filesel = gtk_file_chooser_dialog_new(_("Buddy Icon"),
    - parent,
    - GTK_FILE_CHOOSER_ACTION_OPEN,
    - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
    - NULL);
    - gtk_dialog_set_default_response(GTK_DIALOG(dialog->icon_filesel), GTK_RESPONSE_ACCEPT);
    + dialog->icon_filesel = gtk_file_chooser_native_new(
    + _("Buddy Icon"), parent, GTK_FILE_CHOOSER_ACTION_OPEN, _("_Open"),
    + _("_Cancel"));
    if ((current_folder != NULL) && (*current_folder != '\0'))
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog->icon_filesel),
    current_folder);
    @@ -2266,11 +1991,6 @@
    G_CALLBACK(icon_filesel_choose_cb), dialog);
    icon_preview_change_cb(NULL, dialog);
    -#ifdef _WIN32
    - g_signal_connect(G_OBJECT(dialog->icon_filesel), "show",
    - G_CALLBACK(winpidgin_ensure_onscreen), dialog->icon_filesel);
    -#endif
    -
    return dialog->icon_filesel;
    }
    @@ -3119,355 +2839,6 @@
    return dst;
    }
    -static void
    -url_copy(GtkWidget *w, gchar *url)
    -{
    - GtkClipboard *clipboard;
    -
    - clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY);
    - gtk_clipboard_set_text(clipboard, url, -1);
    -
    - clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_CLIPBOARD);
    - gtk_clipboard_set_text(clipboard, url, -1);
    -}
    -
    -static gboolean
    -link_context_menu(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
    -{
    - GtkWidget *img, *item;
    - char *url;
    -
    - url = webkit_dom_html_anchor_element_get_href(link);
    -
    - /* Open Link */
    - img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
    - item = gtk_image_menu_item_new_with_mnemonic(_("_Open Link"));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
    - g_signal_connect_swapped(G_OBJECT(item), "activate",
    - G_CALLBACK(pidgin_webview_activate_anchor), link);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    -
    - /* Copy Link Location */
    - img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
    - item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Link Location"));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
    - /* The signal owns url now: */
    - g_signal_connect_data(G_OBJECT(item), "activate", G_CALLBACK(url_copy),
    - (gpointer)url, (GClosureNotify)g_free, 0);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -copy_email_address(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
    -{
    - GtkWidget *img, *item;
    - char *text;
    - char *address;
    -#define MAILTOSIZE (sizeof("mailto:") - 1)
    -
    - text = webkit_dom_html_anchor_element_get_href(link);
    - if (!text || strlen(text) <= MAILTOSIZE) {
    - g_free(text);
    - return FALSE;
    - }
    - address = text + MAILTOSIZE;
    -
    - /* Copy Email Address */
    - img = gtk_image_new_from_stock(GTK_STOCK_COPY, GTK_ICON_SIZE_MENU);
    - item = gtk_image_menu_item_new_with_mnemonic(_("_Copy Email Address"));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
    - g_signal_connect_data(G_OBJECT(item), "activate", G_CALLBACK(url_copy),
    - g_strdup(address), (GClosureNotify)g_free, 0);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    -
    - g_free(text);
    -
    - return TRUE;
    -}
    -
    -/*
    - * open_file:
    - * @filename: The path to a file. Specifically this is the link target
    - * from a link in an IM window with the leading "file://" removed.
    - */
    -static void
    -open_file(PidginWebView *webview, const char *filename)
    -{
    - /* Copied from gtkft.c:open_button_cb */
    -#ifdef _WIN32
    - /* If using Win32... */
    - int code;
    - /* Escape URI by replacing double-quote with 2 double-quotes. */
    - gchar *escaped = purple_strreplace(filename, "\"", "\"\"");
    - gchar *param = g_strconcat("/select,\"", escaped, "\"", NULL);
    - wchar_t *wc_param = g_utf8_to_utf16(param, -1, NULL, NULL, NULL);
    -
    - /* TODO: Better to use SHOpenFolderAndSelectItems()? */
    - code = (int)ShellExecuteW(NULL, L"OPEN", L"explorer.exe", wc_param, NULL, SW_NORMAL);
    -
    - g_free(wc_param);
    - g_free(param);
    - g_free(escaped);
    -
    - if (code == SE_ERR_ASSOCINCOMPLETE || code == SE_ERR_NOASSOC)
    - {
    - purple_notify_error(webview, NULL,
    - _("There is no application configured to open this type of file."),
    - NULL, NULL);
    - }
    - else if (code < 32)
    - {
    - purple_notify_error(webview, NULL,
    - _("An error occurred while opening the file."), NULL, NULL);
    - purple_debug_warning("gtkutils", "filename: %s; code: %d\n",
    - filename, code);
    - }
    -#else
    - char *command = NULL;
    - GError *error = NULL;
    -
    - if (purple_running_gnome())
    - {
    - char *escaped = g_shell_quote(filename);
    - command = g_strdup_printf("gnome-open %s", escaped);
    - g_free(escaped);
    - }
    - else if (purple_running_kde())
    - {
    - char *escaped = g_shell_quote(filename);
    -
    - if (purple_str_has_suffix(filename, ".desktop"))
    - command = g_strdup_printf("kfmclient openURL %s 'text/plain'", escaped);
    - else
    - command = g_strdup_printf("kfmclient openURL %s", escaped);
    - g_free(escaped);
    - }
    - else
    - {
    - purple_notify_uri(NULL, filename);
    - return;
    - }
    -
    - if (purple_program_is_valid(command))
    - {
    - gint exit_status;
    - if (!g_spawn_command_line_sync(command, NULL, NULL, &exit_status, &error))
    - {
    - gchar *tmp = g_strdup_printf(_("Error launching %s: %s"),
    - filename, error->message);
    - purple_notify_error(webview, NULL, _("Unable to open file."), tmp, NULL);
    - g_free(tmp);
    - g_error_free(error);
    - }
    - if (exit_status != 0)
    - {
    - char *primary = g_strdup_printf(_("Error running %s"), command);
    - char *secondary = g_strdup_printf(_("Process returned error code %d"),
    - exit_status);
    - purple_notify_error(webview, NULL, primary, secondary, NULL);
    - g_free(primary);
    - g_free(secondary);
    - }
    - }
    -#endif
    -}
    -
    -#define FILELINKSIZE (sizeof("file://") - 1)
    -static gboolean
    -file_clicked_cb(PidginWebView *webview, const char *uri)
    -{
    - /* Strip "file://" from the URI. */
    - open_file(webview, uri + FILELINKSIZE);
    - return TRUE;
    -}
    -
    -static gboolean
    -open_containing_cb(PidginWebView *webview, const char *uri)
    -{
    - char *dir = g_path_get_dirname(uri + FILELINKSIZE);
    - open_file(webview, dir);
    - g_free(dir);
    - return TRUE;
    -}
    -
    -static gboolean
    -file_context_menu(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
    -{
    - GtkWidget *img, *item;
    - char *url;
    -
    - url = webkit_dom_html_anchor_element_get_href(link);
    -
    - /* Open File */
    - img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU);
    - item = gtk_image_menu_item_new_with_mnemonic(_("_Open File"));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
    - g_signal_connect_swapped(G_OBJECT(item), "activate",
    - G_CALLBACK(pidgin_webview_activate_anchor), link);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    -
    - /* Open Containing Directory */
    - img = gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU);
    - item = gtk_image_menu_item_new_with_mnemonic(_("Open _Containing Directory"));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
    - /* The signal owns url now: */
    - g_signal_connect_data(G_OBJECT(item), "activate",
    - G_CALLBACK(open_containing_cb), (gpointer)url,
    - (GClosureNotify)g_free, 0);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    -
    - return TRUE;
    -}
    -
    -#define AUDIOLINKSIZE (sizeof("audio://") - 1)
    -static gboolean
    -audio_clicked_cb(PidginWebView *webview, const char *uri)
    -{
    - PidginConversation *conv = g_object_get_data(G_OBJECT(webview), "gtkconv");
    - if (!conv) /* no playback in debug window */
    - return TRUE;
    - purple_sound_play_file(uri + AUDIOLINKSIZE, NULL);
    - return TRUE;
    -}
    -
    -static void
    -savefile_write_cb(gpointer user_data, char *file)
    -{
    - char *temp_file = user_data;
    - gchar *contents;
    - gsize length;
    - GError *error = NULL;
    -
    - if (!g_file_get_contents(temp_file, &contents, &length, &error)) {
    - purple_debug_error("gtkutils", "Unable to read contents of %s: %s\n",
    - temp_file, error->message);
    - g_error_free(error);
    - g_free(temp_file);
    - return;
    - }
    - g_free(temp_file);
    -
    - if (!purple_util_write_data_to_file_absolute(file, contents, length)) {
    - purple_debug_error("gtkutils", "Unable to write contents to %s\n",
    - file);
    - }
    -}
    -
    -static gboolean
    -save_file_cb(GtkWidget *item, const char *url)
    -{
    - PidginConversation *gtkconv = g_object_get_data(G_OBJECT(item), "gtkconv");
    - PurpleConversation *conv;
    - if (!gtkconv)
    - return TRUE;
    - conv = gtkconv->active_conv;
    - purple_request_file(conv, _("Save File"), NULL, TRUE,
    - G_CALLBACK(savefile_write_cb), G_CALLBACK(g_free),
    - purple_request_cpar_from_conversation(conv),
    - (gpointer)g_strdup(url));
    - return TRUE;
    -}
    -
    -static gboolean
    -audio_context_menu(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
    -{
    - GtkWidget *img, *item;
    - char *url;
    - PidginConversation *conv = g_object_get_data(G_OBJECT(webview), "gtkconv");
    - if (!conv) /* No menu in debug window */
    - return TRUE;
    -
    - url = webkit_dom_html_anchor_element_get_href(link);
    -
    - /* Play Sound */
    - img = gtk_image_new_from_stock(GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_MENU);
    - item = gtk_image_menu_item_new_with_mnemonic(_("_Play Sound"));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
    - g_signal_connect_swapped(G_OBJECT(item), "activate",
    - G_CALLBACK(pidgin_webview_activate_anchor), link);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    -
    - /* Save File */
    - img = gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU);
    - item = gtk_image_menu_item_new_with_mnemonic(_("_Save File"));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img);
    - g_signal_connect_data(G_OBJECT(item), "activate",
    - G_CALLBACK(save_file_cb), g_strdup(url+AUDIOLINKSIZE),
    - (GClosureNotify)g_free, 0);
    - g_object_set_data(G_OBJECT(item), "gtkconv", conv);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    -
    - g_free(url);
    -
    - return TRUE;
    -}
    -
    -/* XXX: The following two functions are for demonstration purposes only! */
    -static gboolean
    -open_dialog(PidginWebView *webview, const char *url)
    -{
    - const char *str;
    -
    - if (!url || strlen(url) < sizeof("open://")) {
    - return FALSE;
    - }
    -
    - str = url + sizeof("open://") - 1;
    -
    - if (purple_strequal(str, "accounts"))
    - pidgin_accounts_window_show();
    - else if (purple_strequal(str, "prefs"))
    - pidgin_prefs_show();
    - else
    - return FALSE;
    - return TRUE;
    -}
    -
    -static gboolean
    -dummy(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu)
    -{
    - return TRUE;
    -}
    -
    -#ifdef _WIN32
    -static void
    -winpidgin_register_win32_url_handlers(void)
    -{
    - int idx = 0;
    - LONG ret = ERROR_SUCCESS;
    -
    - do {
    - DWORD nameSize = 256;
    - wchar_t start[256];
    - ret = RegEnumKeyExW(HKEY_CLASSES_ROOT, idx++, start, &nameSize,
    - NULL, NULL, NULL, NULL);
    - if (ret == ERROR_SUCCESS) {
    - HKEY reg_key = NULL;
    - ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, start, 0, KEY_READ, &reg_key);
    - if (ret == ERROR_SUCCESS) {
    - ret = RegQueryValueExW(reg_key, L"URL Protocol", NULL, NULL, NULL, NULL);
    - if (ret == ERROR_SUCCESS) {
    - gchar *utf8 = g_utf16_to_utf8(start, -1, NULL, NULL, NULL);
    - gchar *protocol = g_strdup_printf("%s:", utf8);
    - g_free(utf8);
    - registered_url_handlers = g_slist_prepend(registered_url_handlers, protocol);
    - /* We still pass everything to the "http" "open" handler for security reasons */
    - pidgin_webview_class_register_protocol(protocol, url_clicked_cb, link_context_menu);
    - }
    - RegCloseKey(reg_key);
    - }
    - ret = ERROR_SUCCESS;
    - }
    - } while (ret == ERROR_SUCCESS);
    -
    - if (ret != ERROR_NO_MORE_ITEMS)
    - purple_debug_error("winpidgin", "Error iterating HKEY_CLASSES_ROOT subkeys: %ld\n",
    - ret);
    -}
    -#endif
    -
    GtkWidget *
    pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, GtkShadowType shadow_type, int width, int height)
    {
    @@ -3488,50 +2859,3 @@
    return child;
    }
    -void pidgin_utils_init(void)
    -{
    - pidgin_webview_class_register_protocol("http://", url_clicked_cb, link_context_menu);
    - pidgin_webview_class_register_protocol("https://", url_clicked_cb, link_context_menu);
    - pidgin_webview_class_register_protocol("ftp://", url_clicked_cb, link_context_menu);
    - pidgin_webview_class_register_protocol("gopher://", url_clicked_cb, link_context_menu);
    - pidgin_webview_class_register_protocol("mailto:", url_clicked_cb, copy_email_address);
    -
    - pidgin_webview_class_register_protocol("file://", file_clicked_cb, file_context_menu);
    - pidgin_webview_class_register_protocol("audio://", audio_clicked_cb, audio_context_menu);
    -
    - /* Example custom URL handler. */
    - pidgin_webview_class_register_protocol("open://", open_dialog, dummy);
    -
    -#ifdef _WIN32
    - winpidgin_register_win32_url_handlers();
    -#endif
    -
    -}
    -
    -void pidgin_utils_uninit(void)
    -{
    - pidgin_webview_class_register_protocol("open://", NULL, NULL);
    -
    - /* If we have GNOME handlers registered, unregister them. */
    - if (registered_url_handlers)
    - {
    - GSList *l;
    - for (l = registered_url_handlers; l; l = l->next)
    - {
    - pidgin_webview_class_register_protocol((char *)l->data, NULL, NULL);
    - g_free(l->data);
    - }
    - g_slist_free(registered_url_handlers);
    - registered_url_handlers = NULL;
    - return;
    - }
    -
    - pidgin_webview_class_register_protocol("audio://", NULL, NULL);
    - pidgin_webview_class_register_protocol("file://", NULL, NULL);
    -
    - pidgin_webview_class_register_protocol("http://", NULL, NULL);
    - pidgin_webview_class_register_protocol("https://", NULL, NULL);
    - pidgin_webview_class_register_protocol("ftp://", NULL, NULL);
    - pidgin_webview_class_register_protocol("mailto:", NULL, NULL);
    - pidgin_webview_class_register_protocol("gopher://", NULL, NULL);
    -}
    --- a/pidgin/gtkutils.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkutils.h Tue Oct 08 21:48:28 2019 -0500
    @@ -82,34 +82,6 @@
    G_BEGIN_DECLS
    /**
    - * pidgin_setup_webview:
    - * @webview: The gtkwebview widget to setup.
    - *
    - * Sets up a gtkwebview widget and sets the default signal handlers.
    - */
    -void pidgin_setup_webview(GtkWidget *webview);
    -
    -/**
    - * pidgin_create_webview:
    - * @editable: %TRUE if this webview should be editable. If this is
    - * %FALSE, then the toolbar will NOT be created. If this webview
    - * should be read-only at first, but may become editable later, then
    - * pass in %TRUE here and then manually call
    - * webkit_web_view_set_editable() later.
    - * @webview_ret: A pointer to a pointer to a GtkWidget. This pointer
    - * will be set to the webview when this function exits.
    - * @sw_ret: This will be filled with a pointer to the scrolled window
    - * widget which contains the webview.
    - *
    - * Create an PidginWebView widget and associated PidginWebViewToolbar widget. This
    - * function puts both widgets in a nice GtkFrame. They're separated by an
    - * attractive GtkSeparator.
    - *
    - * Returns: (transfer full): The GtkFrame containing the toolbar and webview.
    - */
    -GtkWidget *pidgin_create_webview(gboolean editable, GtkWidget **webview_ret, GtkWidget **sw_ret);
    -
    -/**
    * pidgin_create_small_button:
    * @image: A button image.
    *
    @@ -303,7 +275,7 @@
    /**
    * pidgin_protocol_option_menu_get_selected:
    * @optmenu: The drop-down option menu created by
    - * pidgin_account_option_menu_new.
    + * pidgin_protocol_option_menu_new().
    *
    * Gets the currently selected protocol from a protocol drop down box.
    *
    @@ -312,57 +284,21 @@
    const char *pidgin_protocol_option_menu_get_selected(GtkWidget *optmenu);
    /**
    - * pidgin_account_option_menu_new:
    - * @default_account: The account to select by default.
    - * @show_all: Whether or not to show all accounts, or just
    - * active accounts.
    - * @cb: (scope call): The callback to call when an account is selected.
    - * @filter_func: (scope call): A function for checking if an account should
    - * be shown. This can be NULL.
    - * @user_data: Data to pass to the callback function.
    - *
    - * Creates a drop-down option menu filled with accounts.
    - *
    - * Returns: (transfer full): The drop-down option menu.
    - */
    -GtkWidget *pidgin_account_option_menu_new(PurpleAccount *default_account,
    - gboolean show_all, GCallback cb,
    - PurpleFilterAccountFunc filter_func, gpointer user_data);
    -
    -/**
    - * pidgin_account_option_menu_get_selected:
    - * @optmenu: The drop-down option menu created by
    - * pidgin_account_option_menu_new.
    - *
    - * Gets the currently selected account from an account drop down box.
    - *
    - * Returns: (transfer none): Returns the PurpleAccount that is currently selected.
    - */
    -PurpleAccount *pidgin_account_option_menu_get_selected(GtkWidget *optmenu);
    -
    -/**
    - * pidgin_account_option_menu_set_selected:
    - * @optmenu: The GtkOptionMenu created by
    - * pidgin_account_option_menu_new.
    - * @account: The PurpleAccount to select.
    - *
    - * Sets the currently selected account for an account drop down box.
    - */
    -void pidgin_account_option_menu_set_selected(GtkWidget *optmenu, PurpleAccount *account);
    -
    -/**
    * pidgin_setup_screenname_autocomplete:
    * @entry: The GtkEntry on which to setup autocomplete.
    - * @optmenu: A menu for accounts, returned by pidgin_account_option_menu_new().
    - * If @optmenu is not %NULL, it'll be updated when a username is chosen
    - * from the autocomplete list.
    + * @chooser: A menu for accounts, returned by pidgin_account_chooser_new(). If
    + * @chooser is not %NULL, it'll be updated when a username is chosen
    + * from the autocomplete list.
    * @filter_func: (scope call): A function for checking if an autocomplete entry
    * should be shown. This can be %NULL.
    * @user_data: The data to be passed to the filter_func function.
    *
    - * Add autocompletion of screenames to an entry, supporting a filtering function.
    + * Add autocompletion of screenames to an entry, supporting a filtering
    + * function.
    */
    -void pidgin_setup_screenname_autocomplete(GtkWidget *entry, GtkWidget *optmenu, PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data);
    +void pidgin_setup_screenname_autocomplete(
    + GtkWidget *entry, GtkWidget *chooser,
    + PidginFilterBuddyCompletionEntryFunc filter_func, gpointer user_data);
    /**
    * pidgin_screenname_autocomplete_default_filter:
    @@ -602,7 +538,9 @@
    *
    * Returns: (transfer full): The file dialog
    */
    -GtkWidget *pidgin_buddy_icon_chooser_new(GtkWindow *parent, void(*callback)(const char*,gpointer), gpointer data);
    +GtkFileChooserNative *pidgin_buddy_icon_chooser_new(
    + GtkWindow *parent, void (*callback)(const char *, gpointer),
    + gpointer data);
    /**
    * pidgin_convert_buddy_icon:
    @@ -920,20 +858,6 @@
    */
    GtkWidget *pidgin_make_scrollable(GtkWidget *child, GtkPolicyType hscrollbar_policy, GtkPolicyType vscrollbar_policy, GtkShadowType shadow_type, int width, int height);
    -/**
    - * pidgin_utils_init:
    - *
    - * Initialize some utility functions.
    - */
    -void pidgin_utils_init(void);
    -
    -/**
    - * pidgin_utils_uninit:
    - *
    - * Uninitialize some utility functions.
    - */
    -void pidgin_utils_uninit(void);
    -
    G_END_DECLS
    #endif /* _PIDGINUTILS_H_ */
    --- a/pidgin/gtkwebview.c Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2369 +0,0 @@
    -/* pidgin
    - *
    - * Pidgin is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include "internal.h"
    -#include "debug.h"
    -#include "glibcompat.h"
    -#include "image-store.h"
    -#include "pidgin.h"
    -#include "pidginstock.h"
    -
    -#include <gdk/gdkkeysyms.h>
    -#ifdef USE_ENCHANT
    -#include <enchant.h>
    -#endif
    -
    -#include "gtkutils.h"
    -#include "gtksmiley-manager.h"
    -#include "gtkwebview.h"
    -#include "gtkwebviewtoolbar.h"
    -
    -#include "gtkinternal.h"
    -#include "gtk3compat.h"
    -
    -#define MAX_FONT_SIZE 7
    -#define MAX_SCROLL_TIME 0.4 /* seconds */
    -#define SCROLL_DELAY 33 /* milliseconds */
    -#define PIDGIN_WEBVIEW_MAX_PROCESS_TIME 100000 /* microseconds */
    -
    -enum {
    - LOAD_HTML,
    - LOAD_JS
    -};
    -
    -enum {
    - BUTTONS_UPDATE,
    - TOGGLE_FORMAT,
    - CLEAR_FORMAT,
    - UPDATE_FORMAT,
    - CHANGED,
    - HTML_APPENDED,
    - INSERT_IMAGE,
    - LAST_SIGNAL
    -};
    -static guint signals[LAST_SIGNAL] = { 0 };
    -
    -/******************************************************************************
    - * Structs
    - *****************************************************************************/
    -
    -typedef struct {
    - WebKitWebInspector *inspector;
    - WebKitDOMNode *node;
    -} PidginWebViewInspectData;
    -
    -typedef struct {
    - WebKitWebView *webview;
    - gunichar ch;
    -} PidginWebViewInsertData;
    -
    -typedef struct {
    - const char *label;
    - gunichar ch;
    -} GtkUnicodeMenuEntry;
    -
    -typedef struct {
    - char *name;
    - int length;
    -
    - gboolean (*activate)(PidginWebView *webview, const char *uri);
    - gboolean (*context_menu)(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu);
    -} PidginWebViewProtocol;
    -
    -typedef struct _PidginWebViewPrivate {
    - /* Processing queues */
    - gboolean is_loading;
    - GQueue *load_queue;
    - guint loader;
    -
    - /* Scroll adjustments */
    - GtkAdjustment *vadj;
    - gboolean autoscroll;
    - guint scroll_src;
    - GTimer *scroll_time;
    -
    - /* Format options */
    - PidginWebViewButtons format_functions;
    - PidginWebViewToolbar *toolbar;
    - struct {
    - gboolean wbfo:1; /* Whole buffer formatting only. */
    - gboolean block_changed:1;
    - } edit;
    -
    - /* WebKit inspector */
    - WebKitWebView *inspector_view;
    - GtkWindow *inspector_win;
    -
    - /* helper scripts */
    - gboolean refresh_spell_installed;
    -} PidginWebViewPrivate;
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -
    -G_DEFINE_TYPE_WITH_PRIVATE(PidginWebView, pidgin_webview,
    - webkit_web_view_get_type());
    -
    -static GRegex *smileys_re = NULL;
    -static GRegex *empty_html_re = NULL;
    -
    -/* Resources cache.
    - *
    - * It's global, because gtkwebkit calls "resource-load-finished" only once
    - * for each static resource.
    - */
    -static GHashTable *globally_loaded_images = NULL;
    -guint globally_loaded_images_refcnt = 0;
    -
    -static GList *spellcheck_languages = NULL;
    -
    -/******************************************************************************
    - * Helpers
    - *****************************************************************************/
    -
    -static void
    -webview_resource_loading(WebKitWebView *webview,
    - WebKitWebFrame *frame,
    - WebKitWebResource *resource,
    - WebKitNetworkRequest *request,
    - WebKitNetworkResponse *response,
    - gpointer user_data)
    -{
    - const gchar *uri;
    - PurpleImage *img = NULL;
    - const gchar *path;
    -
    - uri = webkit_network_request_get_uri(request);
    - if ((img = purple_image_store_get_from_uri(uri)) != NULL) {
    - /* noop */
    - } else if (purple_str_has_prefix(uri, PURPLE_IMAGE_STORE_STOCK_PROTOCOL)) {
    - gchar *p_uri, *found;
    - const gchar *domain, *stock_name;
    -
    - uri += sizeof(PURPLE_IMAGE_STORE_STOCK_PROTOCOL) - 1;
    -
    - p_uri = g_strdup(uri);
    - found = strchr(p_uri, '/');
    - if (!found) {
    - purple_debug_warning("webview", "Invalid purple stock "
    - "image uri: %s", uri);
    - g_free(p_uri);
    - return;
    - }
    -
    - found[0] = '\0';
    - domain = p_uri;
    - stock_name = found + 1;
    -
    - if (g_strcmp0(domain, "e2ee") == 0) {
    - img = _pidgin_e2ee_stock_icon_get(stock_name);
    - if (!img) {
    - g_free(p_uri);
    - return;
    - }
    - } else {
    - purple_debug_warning("webview", "Invalid purple stock "
    - "image domain: %s", domain);
    -
    - g_free(p_uri);
    - return;
    - }
    - } else
    - return;
    -
    - if (img != NULL) {
    - /* At the time of this comment, purple_image_get_path()
    - * always returns something, whether it's the actual
    - * path or a unique identifier, such as derived from a
    - * hash. That API will probably be reviewed after which
    - * this code can probably be simplified.
    - */
    - gchar *uri = NULL;
    -
    - path = purple_image_get_path(img);
    -
    - if (path) {
    - uri = g_filename_to_uri(path, NULL, NULL);
    - }
    -
    - if (uri != NULL) {
    - webkit_network_request_set_uri(request, uri);
    - g_free(uri);
    - } else {
    - gchar *b64, *src;
    - const gchar *type;
    -
    - b64 = g_base64_encode(purple_image_get_data(img),
    - purple_image_get_data_size(img));
    - type = purple_image_get_mimetype(img);
    - src = g_strdup_printf("data:%s;base64,%s", type, b64);
    - g_free(b64);
    - webkit_network_request_set_uri(request, src);
    - g_free(src);
    - }
    - }
    -}
    -
    -static void
    -webview_resource_loaded(WebKitWebView *web_view, WebKitWebFrame *web_frame,
    - WebKitWebResource *web_resource, gpointer user_data)
    -{
    - const gchar *uri;
    - GString *data;
    - PurpleImage *image = NULL;
    -
    - if (!purple_str_has_caseprefix(
    - webkit_web_resource_get_mime_type(web_resource), "image/"))
    - {
    - return;
    - }
    -
    - uri = webkit_web_resource_get_uri(web_resource);
    - if (g_hash_table_lookup(globally_loaded_images, uri))
    - return;
    -
    - data = webkit_web_resource_get_data(web_resource);
    - if (data->len == 0)
    - return;
    -
    - image = purple_image_store_get_from_uri(uri);
    - if (image) {
    - g_object_ref(image);
    - } else {
    - image = purple_image_new_from_data(
    - g_memdup(data->str, data->len), data->len);
    - if (purple_str_has_prefix(uri, "file:"))
    - purple_image_set_friendly_filename(image, uri);
    - g_return_if_fail(image != NULL);
    - }
    -
    - g_hash_table_insert(globally_loaded_images, g_strdup(uri), image);
    -}
    -
    -static PurpleImage *
    -webview_resource_get_loaded(WebKitWebView *web_view, const gchar *uri)
    -{
    - PidginWebViewPrivate *priv = pidgin_webview_get_instance_private(
    - PIDGIN_WEBVIEW(web_view));
    -
    - g_return_val_if_fail(priv != NULL, NULL);
    -
    - return g_hash_table_lookup(globally_loaded_images, uri);
    -}
    -
    -static void
    -process_load_queue_element(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv =
    - pidgin_webview_get_instance_private(webview);
    - int type;
    - char *str;
    - WebKitDOMDocument *doc;
    - WebKitDOMHTMLElement *body;
    - WebKitDOMNode *start, *end;
    - WebKitDOMRange *range;
    - gboolean require_scroll = FALSE;
    -
    - type = GPOINTER_TO_INT(g_queue_pop_head(priv->load_queue));
    - str = g_queue_pop_head(priv->load_queue);
    -
    - switch (type) {
    - case LOAD_HTML:
    - doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - body = webkit_dom_document_get_body(doc);
    - start = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
    -
    - if (priv->autoscroll) {
    - require_scroll = (gtk_adjustment_get_value(priv->vadj)
    - >= (gtk_adjustment_get_upper(priv->vadj) -
    - 1.5*gtk_adjustment_get_page_size(priv->vadj)));
    - }
    -
    - webkit_dom_html_element_insert_adjacent_html(body, "beforeend",
    - str, NULL);
    -
    - range = webkit_dom_document_create_range(doc);
    - if (start) {
    - end = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body));
    - webkit_dom_range_set_start_after(range,
    - WEBKIT_DOM_NODE(start),
    - NULL);
    - webkit_dom_range_set_end_after(range,
    - WEBKIT_DOM_NODE(end),
    - NULL);
    - } else {
    - webkit_dom_range_select_node_contents(range,
    - WEBKIT_DOM_NODE(body),
    - NULL);
    - }
    -
    - if (require_scroll) {
    - if (start)
    - webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(start),
    - TRUE);
    - else
    - webkit_dom_element_scroll_into_view(WEBKIT_DOM_ELEMENT(body),
    - TRUE);
    - }
    -
    - g_signal_emit(webview, signals[HTML_APPENDED], 0, range);
    -
    - break;
    -
    - case LOAD_JS:
    - webkit_web_view_execute_script(WEBKIT_WEB_VIEW(webview), str);
    - break;
    -
    - default:
    - purple_debug_error("webview",
    - "Got unknown loading queue type: %d\n", type);
    - break;
    - }
    -
    - g_free(str);
    -}
    -
    -static gboolean
    -process_load_queue(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv =
    - pidgin_webview_get_instance_private(webview);
    - gint64 start_time;
    -
    - if (priv->is_loading) {
    - priv->loader = 0;
    - return FALSE;
    - }
    - if (!priv->load_queue || g_queue_is_empty(priv->load_queue)) {
    - priv->loader = 0;
    - return FALSE;
    - }
    -
    - start_time = g_get_monotonic_time();
    - while (!g_queue_is_empty(priv->load_queue)) {
    - process_load_queue_element(webview);
    - if (g_get_monotonic_time() - start_time >
    - PIDGIN_WEBVIEW_MAX_PROCESS_TIME)
    - break;
    - }
    -
    - if (g_queue_is_empty(priv->load_queue)) {
    - priv->loader = 0;
    - return FALSE;
    - }
    - return TRUE;
    -}
    -
    -static void
    -webview_load_started(WebKitWebView *webview, WebKitWebFrame *frame,
    - gpointer userdata)
    -{
    - PidginWebViewPrivate *priv = pidgin_webview_get_instance_private(
    - PIDGIN_WEBVIEW(webview));
    -
    - /* is there a better way to test for is_loading? */
    - priv->is_loading = TRUE;
    -}
    -
    -static void
    -webview_load_finished(WebKitWebView *webview, WebKitWebFrame *frame,
    - gpointer userdata)
    -{
    - PidginWebViewPrivate *priv = pidgin_webview_get_instance_private(
    - PIDGIN_WEBVIEW(webview));
    -
    - priv->is_loading = FALSE;
    - if (priv->loader == 0)
    - priv->loader = g_idle_add((GSourceFunc)process_load_queue, webview);
    -}
    -
    -static void
    -webview_inspector_inspect_element(GtkWidget *item, PidginWebViewInspectData *data)
    -{
    - webkit_web_inspector_inspect_node(data->inspector, data->node);
    -}
    -
    -static void
    -webview_inspector_destroy(GtkWindow *window, PidginWebViewPrivate *priv)
    -{
    - g_return_if_fail(priv->inspector_win == window);
    -
    - priv->inspector_win = NULL;
    - priv->inspector_view = NULL;
    -}
    -
    -static WebKitWebView *
    -webview_inspector_create(WebKitWebInspector *inspector,
    - WebKitWebView *webview, gpointer _unused)
    -{
    - PidginWebViewPrivate *priv = pidgin_webview_get_instance_private(
    - PIDGIN_WEBVIEW(webview));
    -
    - if (priv->inspector_view != NULL)
    - return priv->inspector_view;
    -
    - priv->inspector_win = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
    - gtk_window_set_title(priv->inspector_win, _("WebKit inspector"));
    - gtk_window_set_default_size(priv->inspector_win, 600, 400);
    -
    - priv->inspector_view = WEBKIT_WEB_VIEW(webkit_web_view_new());
    - gtk_container_add(GTK_CONTAINER(priv->inspector_win),
    - GTK_WIDGET(priv->inspector_view));
    -
    - g_signal_connect(priv->inspector_win, "destroy",
    - G_CALLBACK(webview_inspector_destroy), priv);
    -
    - return priv->inspector_view;
    -}
    -
    -static gboolean
    -webview_inspector_show(WebKitWebInspector *inspector, GtkWidget *webview)
    -{
    - PidginWebViewPrivate *priv = pidgin_webview_get_instance_private(
    - PIDGIN_WEBVIEW(webview));
    -
    - gtk_widget_show_all(GTK_WIDGET(priv->inspector_win));
    -
    - return TRUE;
    -}
    -
    -static PidginWebViewProtocol *
    -webview_find_protocol(const char *url, gboolean reverse)
    -{
    - PidginWebViewClass *klass;
    - GList *iter;
    - PidginWebViewProtocol *proto = NULL;
    - gssize length = reverse ? (gssize)strlen(url) : -1;
    -
    - klass = g_type_class_ref(PIDGIN_TYPE_WEBVIEW);
    - for (iter = klass->protocols; iter; iter = iter->next) {
    - proto = iter->data;
    - if (g_ascii_strncasecmp(url, proto->name, reverse ? MIN(length, proto->length) : proto->length) == 0) {
    - g_type_class_unref(klass);
    - return proto;
    - }
    - }
    -
    - g_type_class_unref(klass);
    - return NULL;
    -}
    -
    -static gboolean
    -webview_navigation_decision(WebKitWebView *webview,
    - WebKitWebFrame *frame,
    - WebKitNetworkRequest *request,
    - WebKitWebNavigationAction *navigation_action,
    - WebKitWebPolicyDecision *policy_decision,
    - gpointer userdata)
    -{
    - const gchar *uri;
    - WebKitWebNavigationReason reason;
    -
    - uri = webkit_network_request_get_uri(request);
    - reason = webkit_web_navigation_action_get_reason(navigation_action);
    -
    - if (reason == WEBKIT_WEB_NAVIGATION_REASON_LINK_CLICKED) {
    - PidginWebViewProtocol *proto = webview_find_protocol(uri, FALSE);
    - if (proto) {
    - /* XXX: Do something with the return value? */
    - proto->activate(PIDGIN_WEBVIEW(webview), uri);
    - }
    - webkit_web_policy_decision_ignore(policy_decision);
    - } else if (reason == WEBKIT_WEB_NAVIGATION_REASON_OTHER)
    - webkit_web_policy_decision_use(policy_decision);
    - else
    - webkit_web_policy_decision_ignore(policy_decision);
    -
    - return TRUE;
    -}
    -
    -static GtkWidget *
    -get_input_methods_menu(WebKitWebView *webview)
    -{
    - GtkSettings *settings;
    - gboolean show = TRUE;
    - GtkWidget *item;
    - GtkWidget *menu;
    - GtkIMContext *im;
    -
    - settings = webview ? gtk_widget_get_settings(GTK_WIDGET(webview)) : gtk_settings_get_default();
    -
    - if (settings)
    - g_object_get(settings, "gtk-show-input-method-menu", &show, NULL);
    - if (!show)
    - return NULL;
    -
    - item = gtk_image_menu_item_new_with_mnemonic(_("Input _Methods"));
    -
    - g_object_get(webview, "im-context", &im, NULL);
    - menu = gtk_menu_new();
    - gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(im),
    - GTK_MENU_SHELL(menu));
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
    -
    - return item;
    -}
    -
    -/* Values taken from gtktextutil.c */
    -static const GtkUnicodeMenuEntry bidi_menu_entries[] = {
    - { N_("LRM _Left-to-right mark"), 0x200E },
    - { N_("RLM _Right-to-left mark"), 0x200F },
    - { N_("LRE Left-to-right _embedding"), 0x202A },
    - { N_("RLE Right-to-left e_mbedding"), 0x202B },
    - { N_("LRO Left-to-right _override"), 0x202D },
    - { N_("RLO Right-to-left o_verride"), 0x202E },
    - { N_("PDF _Pop directional formatting"), 0x202C },
    - { N_("ZWS _Zero width space"), 0x200B },
    - { N_("ZWJ Zero width _joiner"), 0x200D },
    - { N_("ZWNJ Zero width _non-joiner"), 0x200C }
    -};
    -
    -static void
    -insert_control_character_cb(GtkMenuItem *item, PidginWebViewInsertData *data)
    -{
    - WebKitWebView *webview = data->webview;
    - gunichar ch = data->ch;
    - PidginWebViewPrivate *priv;
    - WebKitDOMDocument *dom;
    - char buf[6];
    -
    - priv = pidgin_webview_get_instance_private(PIDGIN_WEBVIEW(webview));
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    -
    - g_unichar_to_utf8(ch, buf);
    - priv->edit.block_changed = TRUE;
    - webkit_dom_document_exec_command(dom, "insertHTML", FALSE, buf);
    - priv->edit.block_changed = FALSE;
    -}
    -
    -static GtkWidget *
    -get_unicode_menu(WebKitWebView *webview)
    -{
    - GtkSettings *settings;
    - gboolean show = TRUE;
    - GtkWidget *menuitem;
    - GtkWidget *menu;
    - gsize i;
    -
    - settings = webview ? gtk_widget_get_settings(GTK_WIDGET(webview)) : gtk_settings_get_default();
    -
    - if (settings)
    - g_object_get(settings, "gtk-show-unicode-menu", &show, NULL);
    - if (!show)
    - return NULL;
    -
    - menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Insert Unicode Control Character"));
    -
    - menu = gtk_menu_new();
    - for (i = 0; i < G_N_ELEMENTS(bidi_menu_entries); i++) {
    - PidginWebViewInsertData *data;
    - GtkWidget *item;
    -
    - data = g_new0(PidginWebViewInsertData, 1);
    - data->webview = webview;
    - data->ch = bidi_menu_entries[i].ch;
    -
    - item = gtk_menu_item_new_with_mnemonic(_(bidi_menu_entries[i].label));
    - g_signal_connect_data(item, "activate",
    - G_CALLBACK(insert_control_character_cb), data,
    - (GClosureNotify)g_free, 0);
    - gtk_widget_show(item);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    - }
    -
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
    -
    - return menuitem;
    -}
    -
    -#ifdef USE_ENCHANT
    -
    -static void
    -webview_refresh_spellcheck(WebKitWebView *webview)
    -{
    - PidginWebViewPrivate *priv = pidgin_webview_get_instance_private(
    - PIDGIN_WEBVIEW(webview));
    - static const gchar jsfunc[] =
    - "var pidgin_refresh_spellcheck = function() {"
    - "var selection = window.getSelection();"
    - "var originalSelection = selection.getRangeAt(0);"
    - "for (var i = 0; i < 5; i++)"
    - "selection.modify('move', 'backward', 'line');"
    - "for (i = 0; i < 100; i++)"
    - "selection.modify('move', 'forward', 'word');"
    - "selection.removeAllRanges();"
    - "selection.addRange(originalSelection);"
    - "};";
    -
    - if (!priv->refresh_spell_installed) {
    - priv->refresh_spell_installed = TRUE;
    - webkit_web_view_execute_script(webview, jsfunc);
    - }
    -
    - webkit_web_view_execute_script(webview, "pidgin_refresh_spellcheck()");
    -}
    -
    -static void
    -webview_lang_select(GtkMenuItem *item, const gchar *lang)
    -{
    - WebKitWebView *webview = g_object_get_data(G_OBJECT(item), "gtkwebview");
    - WebKitWebSettings *settings;
    -
    - g_return_if_fail(lang != NULL);
    - g_return_if_fail(webview != NULL);
    -
    - settings = webkit_web_view_get_settings(webview);
    - g_object_set(G_OBJECT(settings),
    - "spell-checking-languages", lang, NULL);
    - webview_refresh_spellcheck(webview);
    -}
    -
    -static GtkWidget *
    -get_spelldict_menu(WebKitWebView *webview)
    -{
    - GtkWidget *menuitem;
    - GtkWidget *menu;
    - GList *it;
    -
    - if (spellcheck_languages == NULL)
    - return NULL;
    -
    - menuitem = gtk_image_menu_item_new_with_mnemonic(_("_Language"));
    - menu = gtk_menu_new();
    - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu);
    - for (it = spellcheck_languages; it; it = g_list_next(it)) {
    - GtkWidget *item;
    - const gchar *lang = it->data;
    -
    - /* we could convert lang id to name here */
    - item = gtk_menu_item_new_with_label(lang);
    - g_object_set_data(G_OBJECT(item), "gtkwebview", webview);
    - g_signal_connect(item, "activate",
    - G_CALLBACK(webview_lang_select), (gpointer)lang);
    - gtk_widget_show(item);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    - }
    -
    - return menuitem;
    -}
    -
    -#else
    -static GtkWidget *
    -get_spelldict_menu(WebKitWebView *webview)
    -{
    - return NULL;
    -}
    -#endif
    -
    -static void
    -webview_image_saved(GtkWidget *dialog, gint response, gpointer _unused)
    -{
    - PurpleImage *image;
    - gchar *filename;
    -
    - if (response != GTK_RESPONSE_ACCEPT) {
    - gtk_widget_destroy(dialog);
    - return;
    - }
    -
    - image = g_object_get_data(G_OBJECT(dialog), "pidgin-gtkwebview-image");
    - g_return_if_fail(image != NULL);
    -
    - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
    - g_return_if_fail(filename != NULL);
    - g_return_if_fail(filename[0] != '\0');
    -
    - if (!purple_image_save(image, filename)) {
    - purple_debug_error("gtkwebview", "Failed saving image");
    - /* TODO: we should display a notification here */
    - }
    -
    - g_free(filename);
    - gtk_widget_destroy(dialog);
    -}
    -
    -static void
    -webview_image_save(GtkWidget *item, WebKitDOMHTMLImageElement *image_node)
    -{
    - const gchar *src;
    - WebKitWebView *webview;
    - PurpleImage *image;
    - GtkFileChooserDialog *dialog;
    - const gchar *filename;
    - GtkWidget *parent;
    -
    - webview = g_object_get_data(G_OBJECT(image_node), "pidgin-gtkwebview");
    - g_return_if_fail(webview != NULL);
    -
    - src = webkit_dom_html_image_element_get_src(image_node); /* XXX: a leak or not? */
    - image = webview_resource_get_loaded(webview, src);
    - g_return_if_fail(image != NULL);
    -
    - parent = gtk_widget_get_ancestor(item, GTK_TYPE_WINDOW);
    - dialog = GTK_FILE_CHOOSER_DIALOG(gtk_file_chooser_dialog_new(
    - _("Save Image"),
    - parent ? GTK_WINDOW(parent) : NULL,
    - GTK_FILE_CHOOSER_ACTION_SAVE,
    - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    - GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
    - NULL));
    - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
    - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
    -
    - filename = purple_image_get_friendly_filename(image);
    - g_warn_if_fail(filename != NULL);
    - gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), filename);
    -
    - g_signal_connect(G_OBJECT(dialog), "response",
    - G_CALLBACK(webview_image_saved), NULL);
    -
    - g_object_ref(image);
    - g_object_set_data_full(G_OBJECT(dialog), "pidgin-gtkwebview-image",
    - image, g_object_unref);
    -
    - gtk_widget_show(GTK_WIDGET(dialog));
    -}
    -
    -static void
    -webview_image_add_smiley(GtkWidget *item, WebKitDOMHTMLImageElement *image_node)
    -{
    - const gchar *src;
    - WebKitWebView *webview;
    - PurpleImage *image;
    -
    - src = webkit_dom_html_image_element_get_src(image_node);
    - webview = g_object_get_data(G_OBJECT(image_node), "pidgin-gtkwebview");
    - g_return_if_fail(webview != NULL);
    -
    - image = webview_resource_get_loaded(webview, src);
    - g_return_if_fail(image != NULL);
    -
    - pidgin_smiley_manager_add(image,
    - webkit_dom_html_image_element_get_alt(image_node));
    -}
    -
    -static void
    -do_popup_menu(WebKitWebView *webview, GdkEvent *event, int context,
    - WebKitDOMNode *node, const char *uri)
    -{
    - GtkWidget *menu;
    - GtkWidget *cut, *copy, *paste, *delete, *select;
    - gboolean show_clipboard = TRUE;
    - WebKitDOMHTMLImageElement *image_node = NULL;
    -
    - menu = gtk_menu_new();
    - g_signal_connect(menu, "selection-done",
    - G_CALLBACK(gtk_widget_destroy), NULL);
    -
    - if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK) {
    - PidginWebViewProtocol *proto = NULL;
    - GList *children;
    - WebKitDOMNode *link_node = node;
    -
    - while (link_node && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link_node)) {
    - link_node = webkit_dom_node_get_parent_node(link_node);
    - }
    -
    - if (uri && link_node)
    - proto = webview_find_protocol(uri, FALSE);
    -
    - if (proto && proto->context_menu) {
    - proto->context_menu(PIDGIN_WEBVIEW(webview),
    - WEBKIT_DOM_HTML_ANCHOR_ELEMENT(link_node), menu);
    - }
    -
    - children = gtk_container_get_children(GTK_CONTAINER(menu));
    - if (!children) {
    - GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available"));
    - gtk_widget_show(item);
    - gtk_widget_set_sensitive(item, FALSE);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    - } else {
    - g_list_free(children);
    - }
    - gtk_widget_show_all(menu);
    -
    - show_clipboard = FALSE;
    - }
    -
    - if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE) {
    - WebKitDOMNode *_image_node = node;
    -
    - while (_image_node && !WEBKIT_DOM_IS_HTML_IMAGE_ELEMENT(_image_node)) {
    - _image_node = webkit_dom_node_get_parent_node(_image_node);
    - }
    - if (_image_node)
    - image_node = WEBKIT_DOM_HTML_IMAGE_ELEMENT(_image_node);
    - /* don't do it on our theme smileys */
    - }
    - if (image_node && webkit_dom_html_image_element_get_complete(image_node)) {
    - GtkWidget *menu_item;
    - int width, height;
    -
    - width = webkit_dom_html_image_element_get_width(image_node);
    - height = webkit_dom_html_image_element_get_height(image_node);
    -
    - /* XXX */
    - g_object_set_data(G_OBJECT(image_node), "pidgin-gtkwebview", webview);
    -
    - menu_item = gtk_image_menu_item_new_with_mnemonic(
    - _("_Save Image..."));
    - gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
    - gtk_image_new_from_stock(GTK_STOCK_SAVE, GTK_ICON_SIZE_MENU));
    - g_signal_connect_object(G_OBJECT(menu_item), "activate",
    - G_CALLBACK(webview_image_save), image_node, 0);
    - gtk_widget_show(menu_item);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
    -
    - /* TODO: check, if it's not *our* custom smiley (use css) */
    - if (width <= 96 && height <= 96) {
    - menu_item = gtk_image_menu_item_new_with_mnemonic(
    - _("_Add Custom Smiley..."));
    - gtk_image_menu_item_set_image(
    - GTK_IMAGE_MENU_ITEM(menu_item),
    - gtk_image_new_from_stock(GTK_STOCK_ADD,
    - GTK_ICON_SIZE_MENU));
    - g_signal_connect_object(G_OBJECT(menu_item), "activate",
    - G_CALLBACK(webview_image_add_smiley),
    - image_node, 0);
    - gtk_widget_show(menu_item);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
    - }
    -
    - show_clipboard = FALSE;
    - }
    -
    - if (show_clipboard) {
    - /* Using connect_swapped means we don't need any wrapper functions */
    - cut = pidgin_new_menu_item(menu, _("Cu_t"), GTK_STOCK_CUT,
    - NULL, NULL);
    - g_signal_connect_swapped(G_OBJECT(cut), "activate",
    - G_CALLBACK(webkit_web_view_cut_clipboard),
    - webview);
    -
    - copy = pidgin_new_menu_item(menu, _("_Copy"), GTK_STOCK_COPY,
    - NULL, NULL);
    - g_signal_connect_swapped(G_OBJECT(copy), "activate",
    - G_CALLBACK(webkit_web_view_copy_clipboard),
    - webview);
    -
    - paste = pidgin_new_menu_item(menu, _("_Paste"), GTK_STOCK_PASTE,
    - NULL, NULL);
    - g_signal_connect_swapped(G_OBJECT(paste), "activate",
    - G_CALLBACK(webkit_web_view_paste_clipboard),
    - webview);
    -
    - delete = pidgin_new_menu_item(menu, _("_Delete"), GTK_STOCK_DELETE,
    - NULL, NULL);
    - g_signal_connect_swapped(G_OBJECT(delete), "activate",
    - G_CALLBACK(webkit_web_view_delete_selection),
    - webview);
    -
    - pidgin_separator(menu);
    -
    - select = pidgin_new_menu_item(menu, _("Select _All"),
    - GTK_STOCK_SELECT_ALL, NULL, NULL);
    - g_signal_connect_swapped(G_OBJECT(select), "activate",
    - G_CALLBACK(webkit_web_view_select_all),
    - webview);
    -
    - gtk_widget_set_sensitive(cut,
    - webkit_web_view_can_cut_clipboard(webview));
    - gtk_widget_set_sensitive(copy,
    - webkit_web_view_can_copy_clipboard(webview));
    - gtk_widget_set_sensitive(paste,
    - webkit_web_view_can_paste_clipboard(webview));
    - gtk_widget_set_sensitive(delete,
    - webkit_web_view_can_cut_clipboard(webview));
    - }
    -
    - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT
    - "/webview/inspector_enabled"))
    - {
    - WebKitWebSettings *settings;
    - GtkWidget *inspect;
    - PidginWebViewInspectData *data;
    -
    - settings = webkit_web_view_get_settings(webview);
    - g_object_set(G_OBJECT(settings), "enable-developer-extras", TRUE, NULL);
    -
    - data = g_new0(PidginWebViewInspectData, 1);
    - data->inspector = webkit_web_view_get_inspector(webview);
    - data->node = node;
    -
    - pidgin_separator(menu);
    -
    - inspect = pidgin_new_menu_item(menu, _("Inspect _Element"),
    - PIDGIN_STOCK_DEBUG, NULL, NULL);
    - g_signal_connect_data(G_OBJECT(inspect), "activate",
    - G_CALLBACK(webview_inspector_inspect_element),
    - data, (GClosureNotify)g_free, 0);
    - }
    -
    - if (webkit_web_view_get_editable(webview)) {
    - GtkWidget *im = get_input_methods_menu(webview);
    - GtkWidget *unicode = get_unicode_menu(webview);
    - GtkWidget *spelldict = get_spelldict_menu(webview);
    -
    - if (im || unicode || spelldict)
    - pidgin_separator(menu);
    -
    - if (im) {
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), im);
    - gtk_widget_show(im);
    - }
    -
    - if (unicode) {
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), unicode);
    - gtk_widget_show(unicode);
    - }
    -
    - if (spelldict) {
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), spelldict);
    - gtk_widget_show(spelldict);
    - }
    - }
    -
    - g_signal_emit_by_name(G_OBJECT(webview), "populate-popup", menu);
    -
    - gtk_menu_attach_to_widget(GTK_MENU(menu), GTK_WIDGET(webview), NULL);
    - gtk_menu_popup_at_pointer(GTK_MENU(menu), event);
    -}
    -
    -static gboolean
    -webview_popup_menu(WebKitWebView *webview)
    -{
    - WebKitDOMDocument *doc;
    - WebKitDOMNode *node = NULL;
    - int context = WEBKIT_HIT_TEST_RESULT_CONTEXT_DOCUMENT;
    - char *uri = NULL;
    -
    - doc = webkit_web_view_get_dom_document(webview);
    -
    - /* it's unlikely, at least for webkit 1.x */
    - if (WEBKIT_DOM_IS_HTML_DOCUMENT(doc)) {
    - WebKitDOMElement *active;
    - WebKitDOMElement *link;
    - active = webkit_dom_html_document_get_active_element(
    - WEBKIT_DOM_HTML_DOCUMENT(doc));
    -
    - link = active;
    - while (link && !WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link))
    - link = webkit_dom_node_get_parent_element(WEBKIT_DOM_NODE(link));
    - if (WEBKIT_DOM_IS_HTML_ANCHOR_ELEMENT(link)) {
    - context |= WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK;
    - uri = webkit_dom_html_anchor_element_get_href(WEBKIT_DOM_HTML_ANCHOR_ELEMENT(link));
    - }
    - }
    -
    - do_popup_menu(webview, NULL, context, node, uri);
    -
    - g_free(uri);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -webview_button_pressed(WebKitWebView *webview, GdkEventButton *event)
    -{
    - if (gdk_event_triggers_context_menu((GdkEvent *)event)) {
    - WebKitHitTestResult *hit;
    - int context;
    - WebKitDOMNode *node;
    - char *uri;
    -
    - hit = webkit_web_view_get_hit_test_result(webview, event);
    - g_object_get(G_OBJECT(hit),
    - "context", &context,
    - "inner-node", &node,
    - "link-uri", &uri,
    - NULL);
    -
    - do_popup_menu(webview, (GdkEvent *)event, context,
    - node, uri);
    -
    - g_free(uri);
    - g_object_unref(hit);
    -
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -/*
    - * Smoothly scroll a WebView.
    - *
    - * @return TRUE if the window needs to be scrolled further, FALSE if we're at the bottom.
    - */
    -static gboolean
    -smooth_scroll_cb(gpointer data)
    -{
    - PidginWebViewPrivate *priv = data;
    - GtkAdjustment *adj;
    - gdouble max_val;
    - gdouble scroll_val;
    -
    - g_return_val_if_fail(priv->scroll_time != NULL, FALSE);
    -
    - adj = priv->vadj;
    - max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj);
    - scroll_val = gtk_adjustment_get_value(adj) +
    - ((max_val - gtk_adjustment_get_value(adj)) / 3);
    -
    - if (g_timer_elapsed(priv->scroll_time, NULL) > MAX_SCROLL_TIME
    - || scroll_val >= max_val) {
    - /* time's up. jump to the end and kill the timer */
    - gtk_adjustment_set_value(adj, max_val);
    - g_timer_destroy(priv->scroll_time);
    - priv->scroll_time = NULL;
    - priv->scroll_src = 0;
    - return FALSE;
    - }
    -
    - /* scroll by 1/3rd the remaining distance */
    - gtk_adjustment_set_value(adj, scroll_val);
    - return TRUE;
    -}
    -
    -static gboolean
    -scroll_idle_cb(gpointer data)
    -{
    - PidginWebViewPrivate *priv = data;
    - GtkAdjustment *adj = priv->vadj;
    - gdouble max_val;
    -
    - if (adj) {
    - max_val = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj);
    - gtk_adjustment_set_value(adj, max_val);
    - }
    -
    - priv->scroll_src = 0;
    - return FALSE;
    -}
    -
    -static void
    -emit_format_signal(PidginWebView *webview, PidginWebViewButtons buttons)
    -{
    - g_object_ref(webview);
    - g_signal_emit(webview, signals[TOGGLE_FORMAT], 0, buttons);
    - g_object_unref(webview);
    -}
    -
    -static void
    -do_formatting(PidginWebView *webview, const char *name, const char *value)
    -{
    - PidginWebViewPrivate *priv =
    - pidgin_webview_get_instance_private(webview);
    - WebKitDOMDocument *dom;
    - WebKitDOMDOMWindow *win;
    - WebKitDOMDOMSelection *sel = NULL;
    - WebKitDOMRange *range = NULL;
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    -
    - if (priv->edit.wbfo) {
    - win = webkit_dom_document_get_default_view(dom);
    - sel = webkit_dom_dom_window_get_selection(win);
    - if (webkit_dom_dom_selection_get_range_count(sel) > 0)
    - range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL);
    - webkit_web_view_select_all(WEBKIT_WEB_VIEW(webview));
    - }
    -
    - priv->edit.block_changed = TRUE;
    - webkit_dom_document_exec_command(dom, (gchar *)name, FALSE, (gchar *)value);
    - priv->edit.block_changed = FALSE;
    -
    - if (priv->edit.wbfo) {
    - if (range) {
    - webkit_dom_dom_selection_remove_all_ranges(sel);
    - webkit_dom_dom_selection_add_range(sel, range);
    - } else {
    - webkit_dom_dom_selection_collapse_to_end(sel, NULL);
    - }
    - }
    -}
    -
    -static void
    -webview_font_shrink(PidginWebView *webview)
    -{
    - gint fontsize;
    - char *tmp;
    -
    - fontsize = pidgin_webview_get_current_fontsize(webview);
    - fontsize = MAX(fontsize - 1, 1);
    -
    - tmp = g_strdup_printf("%d", fontsize);
    - do_formatting(webview, "fontSize", tmp);
    - g_free(tmp);
    -}
    -
    -static void
    -webview_font_grow(PidginWebView *webview)
    -{
    - gint fontsize;
    - char *tmp;
    -
    - fontsize = pidgin_webview_get_current_fontsize(webview);
    - fontsize = MIN(fontsize + 1, MAX_FONT_SIZE);
    -
    - tmp = g_strdup_printf("%d", fontsize);
    - do_formatting(webview, "fontSize", tmp);
    - g_free(tmp);
    -}
    -
    -static void
    -webview_clear_formatting(PidginWebView *webview)
    -{
    - if (!webkit_web_view_get_editable(WEBKIT_WEB_VIEW(webview)))
    - return;
    -
    - do_formatting(webview, "removeFormat", "");
    - do_formatting(webview, "unlink", "");
    - do_formatting(webview, "backColor", "inherit");
    -}
    -
    -static void
    -webview_toggle_format(PidginWebView *webview, PidginWebViewButtons buttons)
    -{
    - /* since this function is the handler for the formatting keystrokes,
    - we need to check here that the formatting attempted is permitted */
    - buttons &= pidgin_webview_get_format_functions(webview);
    -
    - switch (buttons) {
    - case PIDGIN_WEBVIEW_BOLD:
    - do_formatting(webview, "bold", "");
    - break;
    - case PIDGIN_WEBVIEW_ITALIC:
    - do_formatting(webview, "italic", "");
    - break;
    - case PIDGIN_WEBVIEW_UNDERLINE:
    - do_formatting(webview, "underline", "");
    - break;
    - case PIDGIN_WEBVIEW_STRIKE:
    - do_formatting(webview, "strikethrough", "");
    - break;
    - case PIDGIN_WEBVIEW_SHRINK:
    - webview_font_shrink(webview);
    - break;
    - case PIDGIN_WEBVIEW_GROW:
    - webview_font_grow(webview);
    - break;
    - default:
    - break;
    - }
    -}
    -
    -static void
    -editable_input_cb(PidginWebView *webview, gpointer data)
    -{
    - PidginWebViewPrivate *priv =
    - pidgin_webview_get_instance_private(webview);
    - if (!priv->edit.block_changed && gtk_widget_is_sensitive(GTK_WIDGET(webview)))
    - g_signal_emit(webview, signals[CHANGED], 0);
    -}
    -
    -/******************************************************************************
    - * GObject Stuff
    - *****************************************************************************/
    -
    -GtkWidget *
    -pidgin_webview_new(gboolean editable)
    -{
    - GtkWidget *result;
    - WebKitWebView *webview;
    - WebKitWebSettings *settings;
    -
    - result = g_object_new(pidgin_webview_get_type(), NULL);
    - webview = WEBKIT_WEB_VIEW(result);
    - settings = webkit_web_view_get_settings(webview);
    -
    - g_object_set(G_OBJECT(settings), "default-encoding", "utf-8", NULL);
    -#ifdef _WIN32
    - /* XXX: win32 WebKitGTK replaces backslash with yen sign for
    - * "sans-serif" font. We should figure out, how to disable this
    - * behavior, but for now I will just apply this simple hack (using other
    - * font family).
    - */
    - g_object_set(G_OBJECT(settings), "default-font-family", "Verdana", NULL);
    -#endif
    - webkit_web_view_set_settings(webview, settings);
    -
    - if (editable) {
    - webkit_web_view_set_editable(WEBKIT_WEB_VIEW(webview), editable);
    -
    - g_signal_connect(G_OBJECT(webview), "user-changed-contents",
    - G_CALLBACK(editable_input_cb), NULL);
    - }
    -
    - return result;
    -}
    -
    -static void
    -pidgin_webview_finalize(GObject *webview)
    -{
    - PidginWebViewPrivate *priv = pidgin_webview_get_instance_private(
    - PIDGIN_WEBVIEW(webview));
    -
    - if (priv->inspector_win != NULL)
    - gtk_widget_destroy(GTK_WIDGET(priv->inspector_win));
    -
    - if (priv->loader)
    - g_source_remove(priv->loader);
    -
    - while (!g_queue_is_empty(priv->load_queue)) {
    - g_queue_pop_head(priv->load_queue);
    - g_free(g_queue_pop_head(priv->load_queue));
    - }
    - g_queue_free(priv->load_queue);
    -
    - if (--globally_loaded_images_refcnt == 0) {
    - g_assert(globally_loaded_images != NULL);
    - g_hash_table_destroy(globally_loaded_images);
    - globally_loaded_images = NULL;
    - }
    -
    - G_OBJECT_CLASS(pidgin_webview_parent_class)->finalize(webview);
    -}
    -
    -enum {
    - PROP_0,
    - PROP_EXPAND
    -};
    -
    -static void
    -pidgin_webview_set_property(GObject *object, guint prop_id, const GValue *value,
    - GParamSpec *pspec)
    -{
    - g_return_if_fail(PIDGIN_IS_WEBVIEW(object));
    -
    - switch (prop_id) {
    - case PROP_EXPAND:
    - purple_debug_misc("webview",
    - "Ignored expand property (set to %d)",
    - g_value_get_boolean(value));
    - break;
    - default:
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id,
    - pspec);
    - }
    -}
    -
    -static void
    -pidgin_webview_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
    -{
    - g_return_if_fail(PIDGIN_IS_WEBVIEW(object));
    -
    - G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
    -}
    -
    -#ifdef USE_ENCHANT
    -
    -static void
    -fill_spellcheck_dicts_cb(const gchar *lang_tag, const gchar *provider_name,
    - const gchar *provider_desc, const gchar *provider_file,
    - void *_unused)
    -{
    - gboolean is_dialect;
    - GList *it;
    -
    - /* It's not super efficient, but even with large number of installed
    - * dictionaries (100?) it won't hurt us. */
    -
    - is_dialect = (strchr(lang_tag, '_') != NULL);
    -
    - if (is_dialect) {
    - for (it = spellcheck_languages; it; it = g_list_next(it)) {
    - gchar *it_lang = it->data;
    -
    - if (purple_str_has_prefix(lang_tag, it_lang))
    - return;
    - }
    - } else {
    - GList *next;
    - for (it = spellcheck_languages; it; it = next) {
    - gchar *it_lang = it->data;
    - next = g_list_next(it);
    -
    - if (!purple_str_has_prefix(it_lang, lang_tag))
    - continue;
    -
    - g_free(it_lang);
    - spellcheck_languages =
    - g_list_delete_link(spellcheck_languages, it);
    - }
    - }
    -
    - spellcheck_languages = g_list_prepend(spellcheck_languages,
    - g_strdup(lang_tag));
    -}
    -
    -static void
    -fill_spellcheck_dicts(void)
    -{
    - EnchantBroker *eb;
    -
    - eb = enchant_broker_init();
    - enchant_broker_list_dicts(eb, fill_spellcheck_dicts_cb, NULL);
    - enchant_broker_free(eb);
    - spellcheck_languages = g_list_sort(spellcheck_languages,
    - (GCompareFunc)strcmp);
    -}
    -
    -#endif
    -
    -static gboolean
    -pidgin_webview_insert_image_accu(GSignalInvocationHint *ihint,
    - GValue *return_accu, const GValue *handler_return, gpointer _unused)
    -{
    - gboolean cancel;
    -
    - cancel = g_value_get_boolean(handler_return);
    - if (!cancel)
    - return FALSE;
    -
    - g_value_set_boolean(return_accu, TRUE);
    - return TRUE;
    -}
    -
    -static void
    -pidgin_webview_class_init(PidginWebViewClass *klass)
    -{
    - GObjectClass *gobject_class;
    - GtkBindingSet *binding_set;
    -
    - gobject_class = G_OBJECT_CLASS(klass);
    -
    - /* Signals */
    -
    - signals[BUTTONS_UPDATE] = g_signal_new("allowed-formats-updated",
    - G_TYPE_FROM_CLASS(gobject_class),
    - G_SIGNAL_RUN_FIRST,
    - G_STRUCT_OFFSET(PidginWebViewClass, buttons_update),
    - NULL, 0, NULL,
    - G_TYPE_NONE, 1, G_TYPE_INT);
    - signals[TOGGLE_FORMAT] = g_signal_new("format-toggled",
    - G_TYPE_FROM_CLASS(gobject_class),
    - G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
    - G_STRUCT_OFFSET(PidginWebViewClass, toggle_format),
    - NULL, 0, NULL,
    - G_TYPE_NONE, 1, G_TYPE_INT);
    - signals[CLEAR_FORMAT] = g_signal_new("format-cleared",
    - G_TYPE_FROM_CLASS(gobject_class),
    - G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
    - G_STRUCT_OFFSET(PidginWebViewClass, clear_format),
    - NULL, 0, NULL,
    - G_TYPE_NONE, 0);
    - signals[UPDATE_FORMAT] = g_signal_new("format-updated",
    - G_TYPE_FROM_CLASS(gobject_class),
    - G_SIGNAL_RUN_FIRST,
    - G_STRUCT_OFFSET(PidginWebViewClass, update_format),
    - NULL, 0, NULL,
    - G_TYPE_NONE, 0);
    - signals[CHANGED] = g_signal_new("changed",
    - G_TYPE_FROM_CLASS(gobject_class),
    - G_SIGNAL_RUN_FIRST,
    - G_STRUCT_OFFSET(PidginWebViewClass, changed),
    - NULL, NULL, NULL,
    - G_TYPE_NONE, 0);
    - signals[HTML_APPENDED] = g_signal_new("html-appended",
    - G_TYPE_FROM_CLASS(gobject_class),
    - G_SIGNAL_RUN_FIRST,
    - G_STRUCT_OFFSET(PidginWebViewClass, html_appended),
    - NULL, NULL, NULL,
    - G_TYPE_NONE, 1, WEBKIT_TYPE_DOM_RANGE,
    - NULL);
    - signals[INSERT_IMAGE] = g_signal_new("insert-image",
    - G_TYPE_FROM_CLASS(gobject_class), G_SIGNAL_RUN_LAST,
    - G_STRUCT_OFFSET(PidginWebViewClass, insert_image),
    - pidgin_webview_insert_image_accu, NULL, NULL,
    - G_TYPE_BOOLEAN, 1,
    - PURPLE_TYPE_IMAGE);
    -
    - /* Class Methods */
    -
    - klass->toggle_format = webview_toggle_format;
    - klass->clear_format = webview_clear_formatting;
    -
    - gobject_class->finalize = pidgin_webview_finalize;
    -
    - /* Key Bindings */
    -
    - binding_set = gtk_binding_set_by_class(pidgin_webview_parent_class);
    - gtk_binding_entry_add_signal(binding_set, GDK_KEY_b, GDK_CONTROL_MASK,
    - "format-toggled", 1, G_TYPE_INT,
    - PIDGIN_WEBVIEW_BOLD);
    - gtk_binding_entry_add_signal(binding_set, GDK_KEY_i, GDK_CONTROL_MASK,
    - "format-toggled", 1, G_TYPE_INT,
    - PIDGIN_WEBVIEW_ITALIC);
    - gtk_binding_entry_add_signal(binding_set, GDK_KEY_u, GDK_CONTROL_MASK,
    - "format-toggled", 1, G_TYPE_INT,
    - PIDGIN_WEBVIEW_UNDERLINE);
    - gtk_binding_entry_add_signal(binding_set, GDK_KEY_plus, GDK_CONTROL_MASK,
    - "format-toggled", 1, G_TYPE_INT,
    - PIDGIN_WEBVIEW_GROW);
    - gtk_binding_entry_add_signal(binding_set, GDK_KEY_equal, GDK_CONTROL_MASK,
    - "format-toggled", 1, G_TYPE_INT,
    - PIDGIN_WEBVIEW_GROW);
    - gtk_binding_entry_add_signal(binding_set, GDK_KEY_minus, GDK_CONTROL_MASK,
    - "format-toggled", 1, G_TYPE_INT,
    - PIDGIN_WEBVIEW_SHRINK);
    -
    - binding_set = gtk_binding_set_by_class(klass);
    - gtk_binding_entry_add_signal(binding_set, GDK_KEY_r, GDK_CONTROL_MASK,
    - "format-cleared", 0);
    -
    - /* properties */
    -
    - G_OBJECT_CLASS(klass)->set_property = pidgin_webview_set_property;
    - G_OBJECT_CLASS(klass)->get_property = pidgin_webview_get_property;
    -
    - if (!g_object_class_find_property(G_OBJECT_CLASS(klass), "expand")) {
    - /* webkitgtk for gtk2 doesn't seems to have this */
    - g_object_class_install_property(G_OBJECT_CLASS(klass),
    - PROP_EXPAND, g_param_spec_boolean("expand", "Expand Both",
    - "Whether widget wants to expand in both directions",
    - FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
    - }
    -
    - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/webview");
    - purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/webview/inspector_enabled", FALSE);
    -
    - g_return_if_fail(smileys_re == NULL);
    - g_return_if_fail(empty_html_re == NULL);
    - smileys_re = g_regex_new("<img[^>]* class=\"emoticon "
    - "[^\"^>]*\"[^>]*alt=\"([^\"^>]+)\"[^>]*>",
    - G_REGEX_DOTALL | G_REGEX_OPTIMIZE, 0, NULL);
    - empty_html_re = g_regex_new("<(?!img)[^>]*>",
    - G_REGEX_DOTALL | G_REGEX_OPTIMIZE, 0, NULL);
    -
    -#ifdef USE_ENCHANT
    - fill_spellcheck_dicts();
    -#endif
    -}
    -
    -static void
    -pidgin_webview_init(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv =
    - pidgin_webview_get_instance_private(webview);
    - WebKitWebInspector *inspector;
    -
    - priv->load_queue = g_queue_new();
    -
    - g_signal_connect(G_OBJECT(webview), "button-press-event",
    - G_CALLBACK(webview_button_pressed), NULL);
    -
    - g_signal_connect(G_OBJECT(webview), "popup-menu",
    - G_CALLBACK(webview_popup_menu), NULL);
    -
    - g_signal_connect(G_OBJECT(webview), "navigation-policy-decision-requested",
    - G_CALLBACK(webview_navigation_decision), NULL);
    -
    - g_signal_connect(G_OBJECT(webview), "load-started",
    - G_CALLBACK(webview_load_started), NULL);
    -
    - g_signal_connect(G_OBJECT(webview), "load-finished",
    - G_CALLBACK(webview_load_finished), NULL);
    -
    - g_signal_connect(G_OBJECT(webview), "resource-request-starting",
    - G_CALLBACK(webview_resource_loading), NULL);
    -
    - g_signal_connect(G_OBJECT(webview), "resource-load-finished",
    - G_CALLBACK(webview_resource_loaded), NULL);
    -
    - inspector = webkit_web_view_get_inspector(WEBKIT_WEB_VIEW(webview));
    - g_signal_connect(G_OBJECT(inspector), "inspect-web-view",
    - G_CALLBACK(webview_inspector_create), NULL);
    - g_signal_connect(G_OBJECT(inspector), "show-window",
    - G_CALLBACK(webview_inspector_show), webview);
    -
    - if (globally_loaded_images_refcnt++ == 0) {
    - g_assert(globally_loaded_images == NULL);
    - globally_loaded_images = g_hash_table_new_full(g_str_hash,
    - g_str_equal, g_free, g_object_unref);
    - }
    -}
    -
    -/*****************************************************************************
    - * Public API functions
    - *****************************************************************************/
    -
    -char *
    -pidgin_webview_quote_js_string(const char *text)
    -{
    - GString *str = g_string_new("\"");
    - const char *cur = text;
    -
    - while (cur && *cur) {
    - switch (*cur) {
    - case '\\':
    - g_string_append(str, "\\\\");
    - break;
    - case '\"':
    - g_string_append(str, "\\\"");
    - break;
    - case '\r':
    - g_string_append(str, "<br/>");
    - break;
    - case '\n':
    - break;
    - default:
    - g_string_append_c(str, *cur);
    - }
    - cur++;
    - }
    -
    - g_string_append_c(str, '"');
    -
    - return g_string_free(str, FALSE);
    -}
    -
    -void
    -pidgin_webview_safe_execute_script(PidginWebView *webview, const char *script)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - g_queue_push_tail(priv->load_queue, GINT_TO_POINTER(LOAD_JS));
    - g_queue_push_tail(priv->load_queue, g_strdup(script));
    - if (!priv->is_loading && priv->loader == 0)
    - priv->loader = g_idle_add((GSourceFunc)process_load_queue, webview);
    -}
    -
    -void
    -pidgin_webview_load_html_string(PidginWebView *webview, const char *html)
    -{
    - g_return_if_fail(webview != NULL);
    -
    - webkit_web_view_load_string(WEBKIT_WEB_VIEW(webview), html, NULL, NULL,
    - "file:///");
    -}
    -
    -void
    -pidgin_webview_load_html_string_with_selection(PidginWebView *webview, const char *html)
    -{
    - g_return_if_fail(webview != NULL);
    -
    - pidgin_webview_load_html_string(webview, html);
    - pidgin_webview_safe_execute_script(webview,
    - "var s = window.getSelection();"
    - "var r = document.createRange();"
    - "var n = document.getElementById('caret');"
    - "r.selectNodeContents(n);"
    - "var f = r.extractContents();"
    - "r.selectNode(n);"
    - "r.insertNode(f);"
    - "n.parentNode.removeChild(n);"
    - "s.removeAllRanges();"
    - "s.addRange(r);");
    -}
    -
    -void
    -pidgin_webview_append_html(PidginWebView *webview, const char *html)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - g_queue_push_tail(priv->load_queue, GINT_TO_POINTER(LOAD_HTML));
    - g_queue_push_tail(priv->load_queue, g_strdup(html));
    - if (!priv->is_loading && priv->loader == 0)
    - priv->loader = g_idle_add((GSourceFunc)process_load_queue, webview);
    -}
    -
    -void
    -pidgin_webview_set_vadjustment(PidginWebView *webview, GtkAdjustment *vadj)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - priv->vadj = vadj;
    -}
    -
    -void
    -pidgin_webview_scroll_to_end(PidginWebView *webview, gboolean smooth)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - if (priv->scroll_time)
    - g_timer_destroy(priv->scroll_time);
    - if (priv->scroll_src)
    - g_source_remove(priv->scroll_src);
    - if (smooth) {
    - priv->scroll_time = g_timer_new();
    - priv->scroll_src = g_timeout_add_full(G_PRIORITY_LOW, SCROLL_DELAY, smooth_scroll_cb, priv, NULL);
    - } else {
    - priv->scroll_time = NULL;
    - priv->scroll_src = g_idle_add_full(G_PRIORITY_LOW, scroll_idle_cb, priv, NULL);
    - }
    -}
    -
    -void
    -pidgin_webview_set_autoscroll(PidginWebView *webview, gboolean scroll)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - priv->autoscroll = scroll;
    -}
    -
    -gboolean
    -pidgin_webview_get_autoscroll(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_val_if_fail(webview != NULL, FALSE);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - return priv->autoscroll;
    -}
    -
    -void
    -pidgin_webview_page_up(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    - GtkAdjustment *vadj;
    - gdouble scroll_val;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - vadj = priv->vadj;
    - scroll_val = gtk_adjustment_get_value(vadj) - gtk_adjustment_get_page_size(vadj);
    - scroll_val = MAX(scroll_val, gtk_adjustment_get_lower(vadj));
    -
    - gtk_adjustment_set_value(vadj, scroll_val);
    -}
    -
    -void
    -pidgin_webview_page_down(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    - GtkAdjustment *vadj;
    - gdouble scroll_val;
    - gdouble page_size;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - vadj = priv->vadj;
    - page_size = gtk_adjustment_get_page_size(vadj);
    - scroll_val = gtk_adjustment_get_value(vadj) + page_size;
    - scroll_val = MIN(scroll_val, gtk_adjustment_get_upper(vadj) - page_size);
    -
    - gtk_adjustment_set_value(vadj, scroll_val);
    -}
    -
    -void
    -pidgin_webview_setup_entry(PidginWebView *webview, PurpleConnectionFlags flags)
    -{
    - PidginWebViewButtons buttons;
    -
    - g_return_if_fail(webview != NULL);
    -
    - if (flags & PURPLE_CONNECTION_FLAG_HTML) {
    - gboolean bold, italic, underline, strike;
    -
    - buttons = PIDGIN_WEBVIEW_ALL;
    -
    - if (flags & PURPLE_CONNECTION_FLAG_NO_BGCOLOR)
    - buttons &= ~PIDGIN_WEBVIEW_BACKCOLOR;
    - if (flags & PURPLE_CONNECTION_FLAG_NO_FONTSIZE)
    - {
    - buttons &= ~PIDGIN_WEBVIEW_GROW;
    - buttons &= ~PIDGIN_WEBVIEW_SHRINK;
    - }
    - if (flags & PURPLE_CONNECTION_FLAG_NO_URLDESC)
    - buttons &= ~PIDGIN_WEBVIEW_LINKDESC;
    -
    - pidgin_webview_get_current_format(webview, &bold, &italic, &underline, &strike);
    -
    - pidgin_webview_set_format_functions(webview, PIDGIN_WEBVIEW_ALL);
    - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_bold") != bold)
    - pidgin_webview_toggle_bold(webview);
    -
    - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_italic") != italic)
    - pidgin_webview_toggle_italic(webview);
    -
    - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_underline") != underline)
    - pidgin_webview_toggle_underline(webview);
    -
    - if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/send_strike") != strike)
    - pidgin_webview_toggle_strike(webview);
    -
    - pidgin_webview_toggle_fontface(webview,
    - purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/font_face"));
    -
    - if (!(flags & PURPLE_CONNECTION_FLAG_NO_FONTSIZE))
    - {
    - int size = purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/font_size");
    -
    - /* 3 is the default. */
    - if (size != 3)
    - pidgin_webview_font_set_size(webview, size);
    - }
    -
    - pidgin_webview_toggle_forecolor(webview,
    - purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/fgcolor"));
    -
    - if (!(flags & PURPLE_CONNECTION_FLAG_NO_BGCOLOR)) {
    - pidgin_webview_toggle_backcolor(webview,
    - purple_prefs_get_string(PIDGIN_PREFS_ROOT "/conversations/bgcolor"));
    - } else {
    - pidgin_webview_toggle_backcolor(webview, "");
    - }
    -
    - if (flags & PURPLE_CONNECTION_FLAG_FORMATTING_WBFO)
    - pidgin_webview_set_whole_buffer_formatting_only(webview, TRUE);
    - else
    - pidgin_webview_set_whole_buffer_formatting_only(webview, FALSE);
    - } else {
    - buttons = PIDGIN_WEBVIEW_SMILEY | PIDGIN_WEBVIEW_IMAGE;
    - webview_clear_formatting(webview);
    - }
    -
    - if (flags & PURPLE_CONNECTION_FLAG_NO_IMAGES)
    - buttons &= ~PIDGIN_WEBVIEW_IMAGE;
    -
    - if (flags & PURPLE_CONNECTION_FLAG_ALLOW_CUSTOM_SMILEY)
    - buttons |= PIDGIN_WEBVIEW_CUSTOM_SMILEY;
    - else
    - buttons &= ~PIDGIN_WEBVIEW_CUSTOM_SMILEY;
    -
    - pidgin_webview_set_format_functions(webview, buttons);
    -}
    -
    -void
    -pidgin_webview_set_spellcheck(PidginWebView *webview, gboolean enable)
    -{
    - WebKitWebSettings *settings;
    -
    - g_return_if_fail(webview != NULL);
    -
    - settings = webkit_web_view_get_settings(WEBKIT_WEB_VIEW(webview));
    - g_object_set(G_OBJECT(settings), "enable-spell-checking", enable, NULL);
    - webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webview), settings);
    -}
    -
    -void
    -pidgin_webview_set_whole_buffer_formatting_only(PidginWebView *webview, gboolean wbfo)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - priv->edit.wbfo = wbfo;
    -}
    -
    -void
    -pidgin_webview_set_format_functions(PidginWebView *webview, PidginWebViewButtons buttons)
    -{
    - PidginWebViewPrivate *priv;
    - GObject *object;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - object = g_object_ref(G_OBJECT(webview));
    - priv->format_functions = buttons;
    - g_signal_emit(object, signals[BUTTONS_UPDATE], 0, buttons);
    - g_object_unref(object);
    -}
    -
    -void
    -pidgin_webview_activate_anchor(WebKitDOMHTMLAnchorElement *link)
    -{
    - WebKitDOMDocument *doc;
    - WebKitDOMEvent *event;
    -
    - doc = webkit_dom_node_get_owner_document(WEBKIT_DOM_NODE(link));
    - event = webkit_dom_document_create_event(doc, "MouseEvent", NULL);
    - webkit_dom_event_init_event(event, "click", TRUE, TRUE);
    - webkit_dom_node_dispatch_event(WEBKIT_DOM_NODE(link), event, NULL);
    -}
    -
    -gboolean
    -pidgin_webview_class_register_protocol(const char *name,
    - gboolean (*activate)(PidginWebView *webview, const char *uri),
    - gboolean (*context_menu)(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu))
    -{
    - PidginWebViewClass *klass;
    - PidginWebViewProtocol *proto;
    -
    - g_return_val_if_fail(name, FALSE);
    -
    - klass = g_type_class_ref(PIDGIN_TYPE_WEBVIEW);
    - g_return_val_if_fail(klass, FALSE);
    -
    - if ((proto = webview_find_protocol(name, TRUE))) {
    - if (activate) {
    - return FALSE;
    - }
    - klass->protocols = g_list_remove(klass->protocols, proto);
    - g_free(proto->name);
    - g_free(proto);
    - return TRUE;
    - } else if (!activate) {
    - return FALSE;
    - }
    -
    - proto = g_new0(PidginWebViewProtocol, 1);
    - proto->name = g_strdup(name);
    - proto->length = strlen(name);
    - proto->activate = activate;
    - proto->context_menu = context_menu;
    - klass->protocols = g_list_prepend(klass->protocols, proto);
    -
    - return TRUE;
    -}
    -
    -gchar *
    -pidgin_webview_get_head_html(PidginWebView *webview)
    -{
    - WebKitDOMDocument *doc;
    - WebKitDOMHTMLHeadElement *head;
    - gchar *html;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - head = webkit_dom_document_get_head(doc);
    - html = webkit_dom_html_element_get_inner_html(WEBKIT_DOM_HTML_ELEMENT(head));
    -
    - return html;
    -}
    -
    -static gchar *
    -pidgin_webview_strip_smileys(const gchar *text)
    -{
    - return g_regex_replace(smileys_re, text, -1, 0, "\\1", 0, NULL);
    -}
    -
    -gchar *
    -pidgin_webview_get_body_html(PidginWebView *webview)
    -{
    - WebKitDOMDocument *doc;
    - WebKitDOMHTMLElement *body;
    - gchar *html, *stripped;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - body = webkit_dom_document_get_body(doc);
    - html = webkit_dom_html_element_get_inner_html(body);
    - stripped = pidgin_webview_strip_smileys(html);
    - g_free(html);
    -
    - return stripped;
    -}
    -
    -gchar *
    -pidgin_webview_get_body_text(PidginWebView *webview)
    -{
    - WebKitDOMDocument *doc;
    - WebKitDOMHTMLElement *body;
    - gchar *text;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - doc = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - body = webkit_dom_document_get_body(doc);
    - text = webkit_dom_html_element_get_inner_text(body);
    -
    - return text;
    -}
    -
    -gchar *
    -pidgin_webview_get_selected_text(PidginWebView *webview)
    -{
    - WebKitDOMDocument *dom;
    - WebKitDOMDOMWindow *win;
    - WebKitDOMDOMSelection *sel;
    - WebKitDOMRange *range = NULL;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - win = webkit_dom_document_get_default_view(dom);
    - sel = webkit_dom_dom_window_get_selection(win);
    - if (webkit_dom_dom_selection_get_range_count(sel))
    - range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL);
    -
    - if (range)
    - return webkit_dom_range_get_text(range);
    - else
    - return NULL;
    -}
    -
    -static gchar *
    -pidgin_webview_strip_empty_html(const gchar *text)
    -{
    - return g_regex_replace(empty_html_re, text, -1, 0, "", 0, NULL);
    -}
    -
    -gboolean
    -pidgin_webview_is_empty(PidginWebView *webview)
    -{
    - gchar *html, *tmp;
    - gboolean is_empty;
    -
    - g_return_val_if_fail(webview != NULL, TRUE);
    -
    - html = pidgin_webview_get_body_html(webview);
    - tmp = purple_strreplace(html, "&nbsp;", " ");
    - g_free(html);
    - html = tmp;
    -
    - tmp = pidgin_webview_strip_empty_html(html);
    - g_free(html);
    - html = tmp;
    -
    - g_strstrip(html);
    - is_empty = (html[0] == '\0');
    - g_free(html);
    -
    - return is_empty;
    -}
    -
    -void
    -pidgin_webview_get_caret(PidginWebView *webview, WebKitDOMNode **container_ret,
    - glong *pos_ret)
    -{
    - WebKitDOMDocument *dom;
    - WebKitDOMDOMWindow *win;
    - WebKitDOMDOMSelection *sel;
    - WebKitDOMRange *range = NULL;
    - WebKitDOMNode *start_container, *end_container;
    - glong start, end;
    -
    - g_return_if_fail(webview && container_ret && pos_ret);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - win = webkit_dom_document_get_default_view(dom);
    - sel = webkit_dom_dom_window_get_selection(win);
    - if (webkit_dom_dom_selection_get_range_count(sel))
    - range = webkit_dom_dom_selection_get_range_at(sel, 0, NULL);
    -
    - if (range) {
    - start_container = webkit_dom_range_get_start_container(range, NULL);
    - start = webkit_dom_range_get_start_offset(range, NULL);
    - end_container = webkit_dom_range_get_end_container(range, NULL);
    - end = webkit_dom_range_get_end_offset(range, NULL);
    -
    - if (start == end &&
    - webkit_dom_node_is_same_node(start_container, end_container)) {
    -
    - *container_ret = start_container;
    - *pos_ret = start;
    - return;
    - }
    - }
    -
    - *container_ret = NULL;
    - *pos_ret = -1;
    -}
    -
    -void
    -pidgin_webview_set_caret(PidginWebView *webview, WebKitDOMNode *container, glong pos)
    -{
    - WebKitDOMDocument *dom;
    - WebKitDOMDOMWindow *win;
    - WebKitDOMDOMSelection *sel;
    -
    - g_return_if_fail(webview && container && pos >= 0);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - win = webkit_dom_document_get_default_view(dom);
    - sel = webkit_dom_dom_window_get_selection(win);
    -
    - webkit_dom_dom_selection_set_position(sel, container, pos, NULL);
    -}
    -
    -PidginWebViewButtons
    -pidgin_webview_get_format_functions(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_val_if_fail(webview != NULL, 0);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - return priv->format_functions;
    -}
    -
    -void
    -pidgin_webview_get_current_format(PidginWebView *webview, gboolean *bold,
    - gboolean *italic, gboolean *underline,
    - gboolean *strike)
    -{
    - WebKitDOMDocument *dom;
    -
    - g_return_if_fail(webview != NULL);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    -
    - if (bold)
    - *bold = webkit_dom_document_query_command_state(dom, "bold");
    - if (italic)
    - *italic = webkit_dom_document_query_command_state(dom, "italic");
    - if (underline)
    - *underline = webkit_dom_document_query_command_state(dom, "underline");
    - if (strike)
    - *strike = webkit_dom_document_query_command_state(dom, "strikethrough");
    -}
    -
    -char *
    -pidgin_webview_get_current_fontface(PidginWebView *webview)
    -{
    - WebKitDOMDocument *dom;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - return webkit_dom_document_query_command_value(dom, "fontName");
    -}
    -
    -char *
    -pidgin_webview_get_current_forecolor(PidginWebView *webview)
    -{
    - WebKitDOMDocument *dom;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - return webkit_dom_document_query_command_value(dom, "foreColor");
    -}
    -
    -char *
    -pidgin_webview_get_current_backcolor(PidginWebView *webview)
    -{
    - WebKitDOMDocument *dom;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - return webkit_dom_document_query_command_value(dom, "backColor");
    -}
    -
    -gint
    -pidgin_webview_get_current_fontsize(PidginWebView *webview)
    -{
    - WebKitDOMDocument *dom;
    - gchar *text;
    - gint size;
    -
    - g_return_val_if_fail(webview != NULL, 0);
    -
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - text = webkit_dom_document_query_command_value(dom, "fontSize");
    - size = atoi(text);
    - g_free(text);
    -
    - return size;
    -}
    -
    -void
    -pidgin_webview_clear_formatting(PidginWebView *webview)
    -{
    - GObject *object;
    -
    - g_return_if_fail(webview != NULL);
    -
    - object = g_object_ref(G_OBJECT(webview));
    - g_signal_emit(object, signals[CLEAR_FORMAT], 0);
    - g_object_unref(object);
    -}
    -
    -void
    -pidgin_webview_toggle_bold(PidginWebView *webview)
    -{
    - g_return_if_fail(webview != NULL);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_BOLD);
    -}
    -
    -void
    -pidgin_webview_toggle_italic(PidginWebView *webview)
    -{
    - g_return_if_fail(webview != NULL);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_ITALIC);
    -}
    -
    -void
    -pidgin_webview_toggle_underline(PidginWebView *webview)
    -{
    - g_return_if_fail(webview != NULL);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_UNDERLINE);
    -}
    -
    -void
    -pidgin_webview_toggle_strike(PidginWebView *webview)
    -{
    - g_return_if_fail(webview != NULL);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_STRIKE);
    -}
    -
    -gboolean
    -pidgin_webview_toggle_forecolor(PidginWebView *webview, const char *color)
    -{
    - g_return_val_if_fail(webview != NULL, FALSE);
    -
    - do_formatting(webview, "foreColor", color);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_FORECOLOR);
    -
    - return FALSE;
    -}
    -
    -gboolean
    -pidgin_webview_toggle_backcolor(PidginWebView *webview, const char *color)
    -{
    - g_return_val_if_fail(webview != NULL, FALSE);
    -
    - do_formatting(webview, "backColor", color);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_BACKCOLOR);
    -
    - return FALSE;
    -}
    -
    -gboolean
    -pidgin_webview_toggle_fontface(PidginWebView *webview, const char *face)
    -{
    - g_return_val_if_fail(webview != NULL, FALSE);
    -
    - do_formatting(webview, "fontName", face);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_FACE);
    -
    - return FALSE;
    -}
    -
    -void
    -pidgin_webview_font_set_size(PidginWebView *webview, gint size)
    -{
    - char *tmp;
    -
    - g_return_if_fail(webview != NULL);
    -
    - tmp = g_strdup_printf("%d", size);
    - do_formatting(webview, "fontSize", tmp);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_SHRINK|PIDGIN_WEBVIEW_GROW);
    - g_free(tmp);
    -}
    -
    -void
    -pidgin_webview_font_shrink(PidginWebView *webview)
    -{
    - g_return_if_fail(webview != NULL);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_SHRINK);
    -}
    -
    -void
    -pidgin_webview_font_grow(PidginWebView *webview)
    -{
    - g_return_if_fail(webview != NULL);
    - emit_format_signal(webview, PIDGIN_WEBVIEW_GROW);
    -}
    -
    -void
    -pidgin_webview_insert_hr(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    - WebKitDOMDocument *dom;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    -
    - priv->edit.block_changed = TRUE;
    - webkit_dom_document_exec_command(dom, "insertHorizontalRule", FALSE, "");
    - priv->edit.block_changed = FALSE;
    -}
    -
    -void
    -pidgin_webview_insert_link(PidginWebView *webview, const char *url, const char *desc)
    -{
    - PidginWebViewPrivate *priv;
    - WebKitDOMDocument *dom;
    - char *link;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - link = g_strdup_printf("<a href='%s'>%s</a>", url, desc ? desc : url);
    -
    - priv->edit.block_changed = TRUE;
    - webkit_dom_document_exec_command(dom, "insertHTML", FALSE, link);
    - priv->edit.block_changed = FALSE;
    - g_free(link);
    -}
    -
    -void
    -pidgin_webview_insert_image(PidginWebView *webview, PurpleImage *image)
    -{
    - PidginWebViewPrivate *priv;
    - WebKitDOMDocument *dom;
    - char *img;
    - guint id;
    - gboolean cancel;
    -
    - g_return_if_fail(webview != NULL);
    -
    - g_signal_emit(webview, signals[INSERT_IMAGE], 0, image, &cancel);
    - if (cancel)
    - return;
    -
    - id = purple_image_store_add(image);
    - priv = pidgin_webview_get_instance_private(webview);
    - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - img = g_strdup_printf("<img src='" PURPLE_IMAGE_STORE_PROTOCOL
    - "%u'/>", id);
    -
    - priv->edit.block_changed = TRUE;
    - webkit_dom_document_exec_command(dom, "insertHTML", FALSE, img);
    - priv->edit.block_changed = FALSE;
    - g_free(img);
    -}
    -
    -static WebKitDOMCSSStyleDeclaration*
    -pidgin_webview_get_DOM_CSS_style(PidginWebView *webview)
    -{
    - WebKitDOMDocument *document;
    - WebKitDOMElement *dom_element;
    - WebKitDOMDOMWindow *dom_window;
    -
    - document = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(webview));
    - dom_window = webkit_dom_document_get_default_view(document);
    -
    - dom_element = webkit_dom_document_get_document_element(document);
    - return webkit_dom_dom_window_get_computed_style(dom_window, dom_element, 0);
    -}
    -
    -gint
    -pidgin_webview_get_DOM_height(PidginWebView *webview)
    -{
    - gchar *value;
    - WebKitDOMCSSStyleDeclaration *style;
    -
    - style = pidgin_webview_get_DOM_CSS_style(webview);
    - value = webkit_dom_css_style_declaration_get_property_value(style, "height");
    -
    - return g_ascii_strtoll(value, NULL, 0);
    -}
    -
    -gint
    -pidgin_webview_get_font_size(PidginWebView *webview)
    -{
    - gchar *value;
    - WebKitDOMCSSStyleDeclaration *style;
    -
    - style = pidgin_webview_get_DOM_CSS_style(webview);
    - value = webkit_dom_css_style_declaration_get_property_value(style, "font-size");
    -
    - return g_ascii_strtoll(value, NULL, 0);
    -}
    -
    -void
    -pidgin_webview_set_toolbar(PidginWebView *webview, GtkWidget *toolbar)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - priv->toolbar = PIDGIN_WEBVIEWTOOLBAR(toolbar);
    -}
    -
    -GtkWidget *
    -pidgin_webview_get_toolbar(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_val_if_fail(webview != NULL, NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - return GTK_WIDGET(priv->toolbar);
    -}
    -
    -void
    -pidgin_webview_show_toolbar(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - g_return_if_fail(priv->toolbar != NULL);
    -
    - gtk_widget_show(GTK_WIDGET(priv->toolbar));
    -}
    -
    -void
    -pidgin_webview_hide_toolbar(PidginWebView *webview)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - g_return_if_fail(priv->toolbar != NULL);
    -
    - gtk_widget_hide(GTK_WIDGET(priv->toolbar));
    -}
    -
    -void
    -pidgin_webview_activate_toolbar(PidginWebView *webview, PidginWebViewAction action)
    -{
    - PidginWebViewPrivate *priv;
    -
    - g_return_if_fail(webview != NULL);
    -
    - priv = pidgin_webview_get_instance_private(webview);
    - g_return_if_fail(priv->toolbar != NULL);
    -
    - pidgin_webviewtoolbar_activate(priv->toolbar, action);
    -}
    -
    -void
    -pidgin_webview_switch_active_conversation(PidginWebView *webview,
    - PurpleConversation *conv)
    -{
    - PidginWebViewPrivate *priv =
    - pidgin_webview_get_instance_private(webview);
    -
    - g_return_if_fail(priv != NULL);
    - if (priv->toolbar == NULL)
    - return;
    -
    - pidgin_webviewtoolbar_switch_active_conversation(priv->toolbar, conv);
    -}
    --- a/pidgin/gtkwebview.h Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,676 +0,0 @@
    -/* pidgin
    - *
    - * Pidgin is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef _PIDGIN_WEBVIEW_H_
    -#define _PIDGIN_WEBVIEW_H_
    -/**
    - * SECTION:gtkwebview
    - * @section_id: pidgin-gtkwebview
    - * @short_description: <filename>gtkwebview.h</filename>
    - * @title: WebKitWebView Wrapper
    - *
    - * Wrapper over the Gtk WebKitWebView component.
    - */
    -
    -#include <glib.h>
    -#include <gtk/gtk.h>
    -#include <webkit/webkit.h>
    -
    -#define PIDGIN_TYPE_WEBVIEW (pidgin_webview_get_type())
    -#define PIDGIN_WEBVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_WEBVIEW, PidginWebView))
    -#define PIDGIN_WEBVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_WEBVIEW, PidginWebViewClass))
    -#define PIDGIN_IS_WEBVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_WEBVIEW))
    -#define PIDGIN_IS_WEBVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_WEBVIEW))
    -#define PIDGIN_WEBVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_WEBVIEW, PidginWebViewClass))
    -
    -
    -/*salinasv: this are partially magic numbers, we need to find how to get them from the DOM */
    -#define WEBVIEW_DOM_FONT_PADDING 3
    -#define WEBVIEW_DOM_TEXT_PADDING 16
    -
    -typedef enum {
    - PIDGIN_WEBVIEW_BOLD = 1 << 0,
    - PIDGIN_WEBVIEW_ITALIC = 1 << 1,
    - PIDGIN_WEBVIEW_UNDERLINE = 1 << 2,
    - PIDGIN_WEBVIEW_GROW = 1 << 3,
    - PIDGIN_WEBVIEW_SHRINK = 1 << 4,
    - PIDGIN_WEBVIEW_FACE = 1 << 5,
    - PIDGIN_WEBVIEW_FORECOLOR = 1 << 6,
    - PIDGIN_WEBVIEW_BACKCOLOR = 1 << 7,
    - PIDGIN_WEBVIEW_LINK = 1 << 8,
    - PIDGIN_WEBVIEW_IMAGE = 1 << 9,
    - PIDGIN_WEBVIEW_SMILEY = 1 << 10,
    - PIDGIN_WEBVIEW_LINKDESC = 1 << 11,
    - PIDGIN_WEBVIEW_STRIKE = 1 << 12,
    - /* Show custom smileys when appropriate. */
    - PIDGIN_WEBVIEW_CUSTOM_SMILEY = 1 << 13,
    - PIDGIN_WEBVIEW_ALL = -1
    -} PidginWebViewButtons;
    -
    -typedef enum {
    - PIDGIN_WEBVIEW_ACTION_BOLD,
    - PIDGIN_WEBVIEW_ACTION_ITALIC,
    - PIDGIN_WEBVIEW_ACTION_UNDERLINE,
    - PIDGIN_WEBVIEW_ACTION_STRIKE,
    - PIDGIN_WEBVIEW_ACTION_LARGER,
    -#if 0
    - PIDGIN_WEBVIEW_ACTION_NORMAL,
    -#endif
    - PIDGIN_WEBVIEW_ACTION_SMALLER,
    - PIDGIN_WEBVIEW_ACTION_FONTFACE,
    - PIDGIN_WEBVIEW_ACTION_FGCOLOR,
    - PIDGIN_WEBVIEW_ACTION_BGCOLOR,
    - PIDGIN_WEBVIEW_ACTION_CLEAR,
    - PIDGIN_WEBVIEW_ACTION_IMAGE,
    - PIDGIN_WEBVIEW_ACTION_LINK,
    - PIDGIN_WEBVIEW_ACTION_HR,
    - PIDGIN_WEBVIEW_ACTION_SMILEY,
    - PIDGIN_WEBVIEW_ACTION_ATTENTION
    -} PidginWebViewAction;
    -
    -typedef struct _PidginWebView PidginWebView;
    -typedef struct _PidginWebViewClass PidginWebViewClass;
    -
    -struct _PidginWebView
    -{
    - WebKitWebView parent;
    -};
    -
    -struct _PidginWebViewClass
    -{
    - WebKitWebViewClass parent;
    -
    - GList *protocols;
    -
    - void (*buttons_update)(PidginWebView *webview, PidginWebViewButtons buttons);
    - void (*toggle_format)(PidginWebView *webview, PidginWebViewButtons buttons);
    - void (*clear_format)(PidginWebView *webview);
    - void (*update_format)(PidginWebView *webview);
    - void (*changed)(PidginWebView *webview);
    - void (*html_appended)(PidginWebView *webview, WebKitDOMRange *range);
    - gboolean (*insert_image)(PidginWebView *webview, PurpleImage *image);
    -};
    -
    -G_BEGIN_DECLS
    -
    -/**
    - * pidgin_webview_get_type:
    - *
    - * Returns: The #GType for #PidginWebView widget
    - */
    -GType pidgin_webview_get_type(void);
    -
    -/**
    - * pidgin_webview_new:
    - * @editable: Whether this PidginWebView will be user-editable
    - *
    - * Create a new PidginWebView object
    - *
    - * Returns: A GtkWidget corresponding to the PidginWebView object
    - */
    -GtkWidget *pidgin_webview_new(gboolean editable);
    -
    -/**
    - * pidgin_webview_append_html:
    - * @webview: The PidginWebView object
    - * @markup: The html markup to append
    - *
    - * A very basic routine to append html, which can be considered
    - * equivalent to a "document.write" using JavaScript.
    - */
    -void pidgin_webview_append_html(PidginWebView *webview, const char *markup);
    -
    -/**
    - * pidgin_webview_load_html_string:
    - * @webview: The PidginWebView object
    - * @html: The HTML content to load
    - *
    - * Requests loading of the given content.
    - */
    -void pidgin_webview_load_html_string(PidginWebView *webview, const char *html);
    -
    -/**
    - * pidgin_webview_load_html_string_with_selection:
    - * @webview: The PidginWebView object
    - * @html: The HTML content to load
    - *
    - * Requests loading of the given content and sets the selection. You must
    - * include an anchor tag with id='caret' in the HTML string, which will be
    - * used to set the selection. This tag is then removed so that querying the
    - * WebView's HTML contents will no longer return it.
    - */
    -void pidgin_webview_load_html_string_with_selection(PidginWebView *webview, const char *html);
    -
    -/**
    - * pidgin_webview_safe_execute_script:
    - * @webview: The PidginWebView object
    - * @script: The script to execute
    - *
    - * Execute the JavaScript only after the webkit_webview_load_string
    - * loads completely. We also guarantee that the scripts are executed
    - * in the order they are called here. This is useful to avoid race
    - * conditions when calling JS functions immediately after opening the
    - * page.
    - */
    -void pidgin_webview_safe_execute_script(PidginWebView *webview, const char *script);
    -
    -/**
    - * pidgin_webview_quote_js_string:
    - * @str: The string to escape and quote
    - *
    - * A convenience routine to quote a string for use as a JavaScript
    - * string. For instance, "hello 'world'" becomes "'hello \\'world\\''"
    - *
    - * Returns: The quoted string
    - */
    -char *pidgin_webview_quote_js_string(const char *str);
    -
    -/**
    - * pidgin_webview_set_vadjustment:
    - * @webview: The PidginWebView object
    - * @vadj: The GtkAdjustment that control the webview
    - *
    - * Set the vertical adjustment for the PidginWebView.
    - */
    -void pidgin_webview_set_vadjustment(PidginWebView *webview, GtkAdjustment *vadj);
    -
    -/**
    - * pidgin_webview_scroll_to_end:
    - * @webview: The PidginWebView object
    - * @smooth: A boolean indicating if smooth scrolling should be used
    - *
    - * Scrolls the Webview to the end of its contents.
    - */
    -void pidgin_webview_scroll_to_end(PidginWebView *webview, gboolean smooth);
    -
    -/**
    - * pidgin_webview_set_autoscroll:
    - * @webview: The PidginWebView object
    - * @scroll: Whether to automatically scroll
    - *
    - * Set whether the PidginWebView stays at its end when HTML content is appended. If
    - * not already at the end before appending, then scrolling will not occur.
    - */
    -void pidgin_webview_set_autoscroll(PidginWebView *webview, gboolean scroll);
    -
    -/**
    - * pidgin_webview_get_autoscroll:
    - * @webview: The PidginWebView object
    - *
    - * Set whether the PidginWebView stays at its end when HTML content is appended. If
    - * not already at the end before appending, then scrolling will not occur.
    - *
    - * Returns: Whether to automatically scroll
    - */
    -gboolean pidgin_webview_get_autoscroll(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_page_up:
    - * @webview: The PidginWebView.
    - *
    - * Scrolls a PidginWebView up by one page.
    - */
    -void pidgin_webview_page_up(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_page_down:
    - * @webview: The PidginWebView.
    - *
    - * Scrolls a PidginWebView down by one page.
    - */
    -void pidgin_webview_page_down(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_setup_entry:
    - * @webview: The PidginWebView.
    - * @flags: The connection flags describing the allowed formatting.
    - *
    - * Setup formatting for a PidginWebView depending on the flags specified.
    - */
    -void pidgin_webview_setup_entry(PidginWebView *webview, PurpleConnectionFlags flags);
    -
    -/**
    - * pidgin_webview_set_spellcheck:
    - * @webview: The PidginWebView.
    - * @enable: Whether to enable or disable spell-checking.
    - *
    - * Setup spell-checking on a PidginWebView.
    - */
    -void pidgin_webview_set_spellcheck(PidginWebView *webview, gboolean enable);
    -
    -/**
    - * pidgin_webview_set_whole_buffer_formatting_only:
    - * @webview: The PidginWebView
    - * @wbfo: %TRUE to enable the mode, or %FALSE otherwise.
    - *
    - * Enables or disables whole buffer formatting only (wbfo) in a PidginWebView.
    - * In this mode formatting options to the buffer take effect for the entire
    - * buffer instead of specific text.
    - */
    -void pidgin_webview_set_whole_buffer_formatting_only(PidginWebView *webview,
    - gboolean wbfo);
    -
    -/**
    - * pidgin_webview_set_format_functions:
    - * @webview: The PidginWebView
    - * @buttons: A PidginWebViewButtons bitmask indicating which functions to use
    - *
    - * Indicates which formatting functions to enable and disable in a PidginWebView.
    - */
    -void pidgin_webview_set_format_functions(PidginWebView *webview,
    - PidginWebViewButtons buttons);
    -
    -/**
    - * pidgin_webview_activate_anchor:
    - * @link: The WebKitDOMHTMLAnchorElement object
    - *
    - * Activates a WebKitDOMHTMLAnchorElement object. This triggers the navigation
    - * signals, and marks the link as visited (when possible).
    - */
    -void pidgin_webview_activate_anchor(WebKitDOMHTMLAnchorElement *link);
    -
    -/**
    - * pidgin_webview_class_register_protocol:
    - * @name: The name of the protocol (e.g. http://)
    - * @activate: The callback to trigger when the protocol text is clicked.
    - * Removes any current protocol definition if %NULL. The
    - * callback should return %TRUE if the link was activated
    - * properly, %FALSE otherwise.
    - * @context_menu: The callback to trigger when the context menu is popped
    - * up on the protocol text. The callback should return
    - * %TRUE if the request for context menu was processed
    - * successfully, %FALSE otherwise.
    - *
    - * Register a protocol with the PidginWebView widget. Registering a protocol would
    - * allow certain text to be clickable.
    - *
    - * Returns: %TRUE if the protocol was successfully registered
    - * (or unregistered, when \a activate is %NULL)
    - */
    -gboolean pidgin_webview_class_register_protocol(const char *name,
    - gboolean (*activate)(PidginWebView *webview, const char *uri),
    - gboolean (*context_menu)(PidginWebView *webview, WebKitDOMHTMLAnchorElement *link, GtkWidget *menu));
    -
    -/**
    - * pidgin_webview_get_format_functions:
    - * @webview: The PidginWebView
    - *
    - * Returns which formatting functions are enabled in a PidginWebView.
    - *
    - * Returns: A PidginWebViewButtons bitmask indicating which functions to are enabled
    - */
    -PidginWebViewButtons pidgin_webview_get_format_functions(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_current_format:
    - * @webview: The PidginWebView
    - * @bold: The boolean to set for bold or %NULL.
    - * @italic: The boolean to set for italic or %NULL.
    - * @underline: The boolean to set for underline or %NULL.
    - * @strikethrough: The boolean to set for strikethrough or %NULL.
    - *
    - * Sets each boolean to %TRUE or %FALSE to indicate if that formatting
    - * option is enabled at the current position in a PidginWebView.
    - */
    -void pidgin_webview_get_current_format(PidginWebView *webview, gboolean *bold,
    - gboolean *italic, gboolean *underline,
    - gboolean *strikethrough);
    -
    -/**
    - * pidgin_webview_get_current_fontface:
    - * @webview: The PidginWebView
    - *
    - * Returns a string containing the selected font face at the current position
    - * in a PidginWebView.
    - *
    - * Returns: A string containing the font face or %NULL if none is set.
    - */
    -char *pidgin_webview_get_current_fontface(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_current_forecolor:
    - * @webview: The PidginWebView
    - *
    - * Returns a string containing the selected foreground color at the current
    - * position in a PidginWebView.
    - *
    - * Returns: A string containing the foreground color or %NULL if none is set.
    - */
    -char *pidgin_webview_get_current_forecolor(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_current_backcolor:
    - * @webview: The PidginWebView
    - *
    - * Returns a string containing the selected font background color at the current
    - * position in a PidginWebView.
    - *
    - * Returns: A string containing the background color or %NULL if none is set.
    - */
    -char *pidgin_webview_get_current_backcolor(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_current_fontsize:
    - * @webview: The PidginWebView
    - *
    - * Returns a integer containing the selected HTML font size at the current
    - * position in a PidginWebView.
    - *
    - * Returns: The HTML font size.
    - */
    -gint pidgin_webview_get_current_fontsize(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_head_html:
    - * @webview: The PidginWebView
    - *
    - * Gets the content of the head element of a PidginWebView as HTML.
    - *
    - * Returns: The HTML from the head element.
    - */
    -gchar *pidgin_webview_get_head_html(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_body_html:
    - * @webview: The PidginWebView
    - *
    - * Gets the HTML content of a PidginWebView.
    - *
    - * Returns: The HTML that is currently displayed.
    - */
    -gchar *pidgin_webview_get_body_html(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_body_text:
    - * @webview: The PidginWebView
    - *
    - * Gets the text content of a PidginWebView.
    - *
    - * Returns: The HTML-free text that is currently displayed.
    - */
    -gchar *pidgin_webview_get_body_text(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_selected_text:
    - * @webview: The PidginWebView
    - *
    - * Gets the selected text of a PidginWebView.
    - *
    - * Returns: The HTML-free text that is currently selected, or NULL if nothing is
    - * currently selected.
    - */
    -gchar *pidgin_webview_get_selected_text(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_is_empty:
    - * @webview: the PidginWebView.
    - *
    - * Checks, if the @webview is empty.
    - *
    - * Returns %TRUE, if the @webview is empty, %FALSE otherwise.
    - */
    -gboolean
    -pidgin_webview_is_empty(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_caret:
    - * @webview: The PidginWebView
    - * @container_ret: A pointer to a pointer to a WebKitDOMNode. This pointer
    - * will be set to the container the caret is in. Set to
    - * %NULL if a range is selected.
    - * @pos_ret: A pointer to a glong. This value will be set to the
    - * position of the caret in the container. Set to -1 if a
    - * range is selected.
    - *
    - * Gets the container of the caret, along with its position in the container
    - * from a PidginWebView.
    - */
    -void pidgin_webview_get_caret(PidginWebView *webview, WebKitDOMNode **container_ret,
    - glong *pos_ret);
    -
    -/**
    - * pidgin_webview_set_caret:
    - * @webview: The PidginWebView
    - * @container: The WebKitDOMNode to set the caret in
    - * @pos: The position of the caret in the container
    - *
    - * Sets the caret position in container, in a PidginWebView.
    - */
    -void pidgin_webview_set_caret(PidginWebView *webview, WebKitDOMNode *container,
    - glong pos);
    -
    -/**
    - * pidgin_webview_clear_formatting:
    - * @webview: The PidginWebView
    - *
    - * Clear all the formatting on a PidginWebView.
    - */
    -void pidgin_webview_clear_formatting(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_toggle_bold:
    - * @webview: The PidginWebView
    - *
    - * Toggles bold at the cursor location or selection in a PidginWebView.
    - */
    -void pidgin_webview_toggle_bold(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_toggle_italic:
    - * @webview: The PidginWebView
    - *
    - * Toggles italic at the cursor location or selection in a PidginWebView.
    - */
    -void pidgin_webview_toggle_italic(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_toggle_underline:
    - * @webview: The PidginWebView
    - *
    - * Toggles underline at the cursor location or selection in a PidginWebView.
    - */
    -void pidgin_webview_toggle_underline(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_toggle_strike:
    - * @webview: The PidginWebView
    - *
    - * Toggles strikethrough at the cursor location or selection in a PidginWebView.
    - */
    -void pidgin_webview_toggle_strike(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_toggle_forecolor:
    - * @webview: The PidginWebView
    - * @color: The HTML-style color, or %NULL or "" to clear the color.
    - *
    - * Toggles a foreground color at the current location or selection in a
    - * PidginWebView.
    - *
    - * Returns: %TRUE if a color was set, or %FALSE if it was cleared.
    - */
    -gboolean pidgin_webview_toggle_forecolor(PidginWebView *webview, const char *color);
    -
    -/**
    - * pidgin_webview_toggle_backcolor:
    - * @webview: The PidginWebView
    - * @color: The HTML-style color, or %NULL or "" to clear the color.
    - *
    - * Toggles a background color at the current location or selection in a
    - * PidginWebView.
    - *
    - * Returns: %TRUE if a color was set, or %FALSE if it was cleared.
    - */
    -gboolean pidgin_webview_toggle_backcolor(PidginWebView *webview, const char *color);
    -
    -/**
    - * pidgin_webview_toggle_fontface:
    - * @webview: The PidginWebView
    - * @face: The font face name, or %NULL or "" to clear the font.
    - *
    - * Toggles a font face at the current location or selection in a PidginWebView.
    - *
    - * Returns: %TRUE if a font name was set, or %FALSE if it was cleared.
    - */
    -gboolean pidgin_webview_toggle_fontface(PidginWebView *webview, const char *face);
    -
    -/**
    - * pidgin_webview_font_set_size:
    - * @webview: The PidginWebView
    - * @size: The HTML font size to use.
    - *
    - * Sets the font size at the current location or selection in a PidginWebView.
    - */
    -void pidgin_webview_font_set_size(PidginWebView *webview, gint size);
    -
    -/**
    - * pidgin_webview_font_shrink:
    - * @webview: The PidginWebView
    - *
    - * Decreases the font size by 1 at the current location or selection in a
    - * PidginWebView.
    - */
    -void pidgin_webview_font_shrink(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_font_grow:
    - * @webview: The PidginWebView
    - *
    - * Increases the font size by 1 at the current location or selection in a
    - * PidginWebView.
    - */
    -void pidgin_webview_font_grow(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_insert_hr:
    - * @webview: The PidginWebView
    - *
    - * Inserts a horizontal rule at the current location or selection in a
    - * PidginWebView.
    - */
    -void pidgin_webview_insert_hr(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_insert_link:
    - * @webview: The PidginWebView
    - * @url: The URL of the link
    - * @desc: The text description of the link. If not supplied, the URL is
    - * used instead.
    - *
    - * Inserts a link at the current location or selection in a PidginWebView.
    - */
    -void pidgin_webview_insert_link(PidginWebView *webview, const char *url, const char *desc);
    -
    -/**
    - * pidgin_webview_insert_image:
    - * @webview: the PidginWebView.
    - * @image: the PurpleImage.
    - *
    - * Inserts an image at the current location or selection in a PidginWebView.
    - */
    -void
    -pidgin_webview_insert_image(PidginWebView *webview, PurpleImage *image);
    -
    -/**
    - * pidgin_webview_get_DOM_height:
    - * @webview: the PidginWebView.
    - *
    - * Look for the calculated height for the DOM on the webview.
    - *
    - * Returns the total height of the DOM in the webview.
    - */
    -gint
    -pidgin_webview_get_DOM_height(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_font_size:
    - * @webview: the PidginWebView.
    - *
    - * Look for the font size used on the current webview
    - *
    - * Returns the font size for the webview.
    - */
    -gint
    -pidgin_webview_get_font_size(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_get_protocol_name:
    - * @webview: The PidginWebView
    - *
    - * Gets the protocol name associated with this PidginWebView.
    - */
    -const char *pidgin_webview_get_protocol_name(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_show_toolbar:
    - * @webview: The PidginWebView.
    - *
    - * Makes the toolbar associated with a PidginWebView visible.
    - */
    -void pidgin_webview_show_toolbar(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_hide_toolbar:
    - * @webview: The PidginWebView.
    - *
    - * Makes the toolbar associated with a PidginWebView invisible.
    - */
    -void pidgin_webview_hide_toolbar(PidginWebView *webview);
    -
    -/**
    - * pidgin_webview_activate_toolbar:
    - * @webview: The PidginWebView
    - * @action: The PidginWebViewAction
    - *
    - * Activate an action on the toolbar associated with a PidginWebView.
    - */
    -void pidgin_webview_activate_toolbar(PidginWebView *webview, PidginWebViewAction action);
    -
    -/**
    - * pidgin_webview_switch_active_conversation:
    - * @webview: The PidginWebView
    - * @conv: The conversation.
    - *
    - * Updates the webview for a new active #PurpleConversation.
    - */
    -void
    -pidgin_webview_switch_active_conversation(PidginWebView *webview,
    - PurpleConversation *conv);
    -
    -/* Do not use. TODO: rename to _pidgin and move to gtkinternal.h */
    -void
    -pidgin_webview_set_toolbar(PidginWebView *webview, GtkWidget *toolbar);
    -
    -/**
    - * pidgin_webview_get_toolbar:
    - * @webview: The PidginWebView
    - *
    - * Returns the toolbar associated with the webview.
    - *
    - * Returns: (transfer none): the toolbar.
    - */
    -GtkWidget *
    -pidgin_webview_get_toolbar(PidginWebView *webview);
    -
    -G_END_DECLS
    -
    -#endif /* _PIDGIN_WEBVIEW_H_ */
    --- a/pidgin/gtkwebviewtoolbar.c Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1780 +0,0 @@
    -/* pidgin
    - *
    - * Pidgin is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -#include "internal.h"
    -#include "pidgin.h"
    -
    -#include "image-store.h"
    -#include "notify.h"
    -#include "prefs.h"
    -#include "request.h"
    -#include "pidginstock.h"
    -#include "smiley-custom.h"
    -#include "smiley-list.h"
    -#include "util.h"
    -#include "debug.h"
    -
    -#include "gtkdialogs.h"
    -#include "gtkwebviewtoolbar.h"
    -#include "gtksmiley-manager.h"
    -#include "gtksmiley-theme.h"
    -#include "gtkutils.h"
    -
    -#include <gdk/gdkkeysyms.h>
    -
    -#include "gtk3compat.h"
    -
    -#define PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FONT "sans-serif"
    -#define PIDGIN_WEBVIEWTOOLBAR_DEFAULT_BGCOLOR "inherit"
    -#define PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FGCOLOR "#000000"
    -
    -/******************************************************************************
    - * Structs
    - *****************************************************************************/
    -
    -typedef struct _PidginWebViewToolbarPrivate {
    - PurpleConversation *active_conv;
    -
    - GtkWidget *wide_view;
    - GtkWidget *lean_view;
    -
    - GtkWidget *font_label;
    - GtkWidget *font_menu;
    -
    - GtkAction *bold;
    - GtkAction *italic;
    - GtkAction *underline;
    - GtkAction *strike;
    -
    - GtkAction *larger_size;
    -#if 0
    - GtkAction *normal_size;
    -#endif
    - GtkAction *smaller_size;
    -
    - GtkAction *font;
    - GtkAction *fgcolor;
    - GtkAction *bgcolor;
    -
    - GtkAction *clear;
    -
    - GtkWidget *insert_menu;
    - GtkAction *image;
    - GtkAction *link;
    - GtkAction *hr;
    -
    - GtkAction *smiley;
    - GtkAction *attention;
    -
    - GtkWidget *font_dialog;
    - GtkWidget *fgcolor_dialog;
    - GtkWidget *bgcolor_dialog;
    - GtkWidget *link_dialog;
    - GtkWidget *smiley_dialog;
    - GtkWidget *image_dialog;
    -
    - gboolean allow_smileys;
    -} PidginWebViewToolbarPrivate;
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -
    -G_DEFINE_TYPE_WITH_PRIVATE(PidginWebViewToolbar, pidgin_webviewtoolbar,
    - GTK_TYPE_BOX);
    -
    -/******************************************************************************
    - * Prototypes
    - *****************************************************************************/
    -
    -static void
    -toggle_action_set_active_block(GtkToggleAction *action, gboolean is_active,
    - PidginWebViewToolbar *toolbar);
    -
    -/******************************************************************************
    - * Helpers
    - *****************************************************************************/
    -
    -static gboolean
    -pidgin_color_parse(const gchar *str, GdkRGBA *color)
    -{
    - GdkRGBA dummy_color;
    -
    - if (str == NULL)
    - return FALSE;
    -
    - while (isspace(str[0]))
    - str++;
    -
    - if (str[0] == '\0')
    - return FALSE;
    -
    - if (color == NULL)
    - color = &dummy_color;
    -
    - if (strcmp(str, "inherit") == 0) {
    - return FALSE;
    - }
    -
    - if (!gdk_rgba_parse(color, str)) {
    - return FALSE;
    - }
    -
    - /* FALSE for fully transparent color (same behavior as with "inherit") */
    - return color->alpha > 0;
    -}
    -
    -static gchar*
    -pidgin_color_to_str(GdkRGBA *color)
    -{
    - return g_strdup_printf("#%02X%02X%02X",
    - (unsigned int)(color->red * 255),
    - (unsigned int)(color->green * 255),
    - (unsigned int)(color->blue * 255));
    -}
    -
    -static void
    -do_bold(GtkAction *bold, PidginWebViewToolbar *toolbar)
    -{
    - g_return_if_fail(toolbar != NULL);
    - pidgin_webview_toggle_bold(PIDGIN_WEBVIEW(toolbar->webview));
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -do_italic(GtkAction *italic, PidginWebViewToolbar *toolbar)
    -{
    - g_return_if_fail(toolbar != NULL);
    - pidgin_webview_toggle_italic(PIDGIN_WEBVIEW(toolbar->webview));
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -do_underline(GtkAction *underline, PidginWebViewToolbar *toolbar)
    -{
    - g_return_if_fail(toolbar != NULL);
    - pidgin_webview_toggle_underline(PIDGIN_WEBVIEW(toolbar->webview));
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -do_strikethrough(GtkAction *strikethrough, PidginWebViewToolbar *toolbar)
    -{
    - g_return_if_fail(toolbar != NULL);
    - pidgin_webview_toggle_strike(PIDGIN_WEBVIEW(toolbar->webview));
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -do_small(GtkAction *small, PidginWebViewToolbar *toolbar)
    -{
    - g_return_if_fail(toolbar != NULL);
    - pidgin_webview_font_shrink(PIDGIN_WEBVIEW(toolbar->webview));
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -do_big(GtkAction *large, PidginWebViewToolbar *toolbar)
    -{
    - g_return_if_fail(toolbar);
    - pidgin_webview_font_grow(PIDGIN_WEBVIEW(toolbar->webview));
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -destroy_toolbar_font(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - if (priv->font_dialog != NULL)
    - {
    - gtk_widget_destroy(priv->font_dialog);
    - priv->font_dialog = NULL;
    - }
    -}
    -
    -static void
    -apply_font(GtkDialog *dialog, gint response, PidginWebViewToolbar *toolbar)
    -{
    - /* this could be expanded to include font size, weight, etc.
    - but for now only works with font face */
    - gchar *fontname = NULL;
    -
    - if (response == GTK_RESPONSE_OK)
    - fontname = gtk_font_chooser_get_font(GTK_FONT_CHOOSER(dialog));
    -
    - if (fontname) {
    - PangoFontDescription *desc;
    - const gchar *family_name;
    -
    - desc = pango_font_description_from_string(fontname);
    - family_name = pango_font_description_get_family(desc);
    -
    - if (family_name) {
    - pidgin_webview_toggle_fontface(PIDGIN_WEBVIEW(toolbar->webview),
    - family_name);
    - }
    -
    - pango_font_description_free(desc);
    - g_free(fontname);
    - } else {
    - pidgin_webview_toggle_fontface(PIDGIN_WEBVIEW(toolbar->webview),
    - PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FONT);
    - }
    -
    - destroy_toolbar_font(toolbar);
    -}
    -
    -static void
    -toggle_font(GtkAction *font, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(font))) {
    - char *fontname = pidgin_webview_get_current_fontface(PIDGIN_WEBVIEW(toolbar->webview));
    -
    - if (!priv->font_dialog) {
    - GtkWindow *window;
    - window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar)));
    - priv->font_dialog = gtk_font_chooser_dialog_new(_("Select Font"), window);
    -
    - if (fontname) {
    - char *fonttif = g_strdup_printf("%s 12", fontname);
    - gtk_font_chooser_set_font(GTK_FONT_CHOOSER(priv->font_dialog),
    - fonttif);
    - g_free(fonttif);
    - } else {
    - gtk_font_chooser_set_font(GTK_FONT_CHOOSER(priv->font_dialog),
    - PIDGIN_DEFAULT_FONT_FACE);
    - }
    -
    - g_signal_connect(G_OBJECT(priv->font_dialog), "response",
    - G_CALLBACK(apply_font), toolbar);
    - }
    -
    - gtk_window_present(GTK_WINDOW(priv->font_dialog));
    -
    - g_free(fontname);
    - } else {
    - pidgin_webview_toggle_fontface(PIDGIN_WEBVIEW(toolbar->webview),
    - PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FONT);
    - destroy_toolbar_font(toolbar);
    - }
    -
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static gboolean
    -destroy_toolbar_fgcolor(GtkWidget *widget, GdkEvent *event,
    - PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - if (widget != NULL) {
    - pidgin_webview_toggle_forecolor(
    - PIDGIN_WEBVIEW(toolbar->webview),
    - PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FGCOLOR);
    - }
    -
    - if (priv->fgcolor_dialog != NULL) {
    - gtk_widget_destroy(priv->fgcolor_dialog);
    - priv->fgcolor_dialog = NULL;
    - }
    -
    - return FALSE;
    -}
    -
    -static void
    -do_fgcolor(GtkDialog *dialog, gint response, gpointer _toolbar)
    -{
    - PidginWebViewToolbar *toolbar = _toolbar;
    - GdkRGBA text_color;
    - gchar *open_tag;
    -
    - if (response != GTK_RESPONSE_OK) {
    - destroy_toolbar_fgcolor(GTK_WIDGET(toolbar), NULL, toolbar);
    - return;
    - }
    -
    - gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &text_color);
    - open_tag = pidgin_color_to_str(&text_color);
    - pidgin_webview_toggle_forecolor(PIDGIN_WEBVIEW(toolbar->webview),
    - open_tag);
    - g_free(open_tag);
    -
    - destroy_toolbar_fgcolor(NULL, NULL, toolbar);
    -}
    -
    -static void
    -toggle_fg_color(GtkAction *color, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(color))) {
    - GdkRGBA fgcolor;
    - gchar *color = pidgin_webview_get_current_forecolor(
    - PIDGIN_WEBVIEW(toolbar->webview));
    -
    - if (!priv->fgcolor_dialog) {
    - priv->fgcolor_dialog = gtk_color_chooser_dialog_new(
    - _("Select Text Color"), GTK_WINDOW(
    - gtk_widget_get_ancestor(toolbar->webview,
    - GTK_TYPE_WINDOW)));
    - gtk_color_chooser_set_use_alpha(
    - GTK_COLOR_CHOOSER(priv->fgcolor_dialog), FALSE);
    -
    - if (pidgin_color_parse(color, &fgcolor)) {
    - gtk_color_chooser_set_rgba(
    - GTK_COLOR_CHOOSER(priv->fgcolor_dialog),
    - &fgcolor);
    - }
    -
    - g_signal_connect(G_OBJECT(priv->fgcolor_dialog),
    - "delete_event",
    - G_CALLBACK(destroy_toolbar_fgcolor), toolbar);
    -
    - g_signal_connect(G_OBJECT(priv->fgcolor_dialog),
    - "response", G_CALLBACK(do_fgcolor), toolbar);
    - }
    -
    - g_free(color);
    -
    - gtk_window_present(GTK_WINDOW(priv->fgcolor_dialog));
    - } else {
    - destroy_toolbar_fgcolor(GTK_WIDGET(toolbar), NULL, toolbar);
    - }
    -
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static gboolean
    -destroy_toolbar_bgcolor(GtkWidget *widget, GdkEvent *event,
    - PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - if (widget != NULL) {
    - pidgin_webview_toggle_backcolor(
    - PIDGIN_WEBVIEW(toolbar->webview),
    - PIDGIN_WEBVIEWTOOLBAR_DEFAULT_BGCOLOR);
    - }
    -
    - if (priv->bgcolor_dialog != NULL) {
    - gtk_widget_destroy(priv->bgcolor_dialog);
    - priv->bgcolor_dialog = NULL;
    - }
    -
    - return FALSE;
    -}
    -
    -static void
    -do_bgcolor(GtkDialog *dialog, gint response, gpointer _toolbar)
    -{
    - PidginWebViewToolbar *toolbar = _toolbar;
    - GdkRGBA text_color;
    - gchar *open_tag;
    -
    - if (response != GTK_RESPONSE_OK) {
    - destroy_toolbar_bgcolor(GTK_WIDGET(toolbar), NULL, toolbar);
    - return;
    - }
    -
    - gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &text_color);
    - open_tag = pidgin_color_to_str(&text_color);
    - pidgin_webview_toggle_backcolor(PIDGIN_WEBVIEW(toolbar->webview),
    - open_tag);
    - g_free(open_tag);
    -
    - destroy_toolbar_bgcolor(NULL, NULL, toolbar);
    -}
    -
    -static void
    -toggle_bg_color(GtkAction *color, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(color))) {
    - GdkRGBA bgcolor;
    - gchar *color = pidgin_webview_get_current_backcolor(
    - PIDGIN_WEBVIEW(toolbar->webview));
    -
    - if (!priv->bgcolor_dialog) {
    - priv->bgcolor_dialog = gtk_color_chooser_dialog_new(
    - _("Select Background Color"), GTK_WINDOW(
    - gtk_widget_get_ancestor(toolbar->webview,
    - GTK_TYPE_WINDOW)));
    - gtk_color_chooser_set_use_alpha(
    - GTK_COLOR_CHOOSER(priv->bgcolor_dialog), FALSE);
    -
    - if (pidgin_color_parse(color, &bgcolor)) {
    - gtk_color_chooser_set_rgba(
    - GTK_COLOR_CHOOSER(priv->bgcolor_dialog),
    - &bgcolor);
    - }
    -
    - g_signal_connect(G_OBJECT(priv->bgcolor_dialog),
    - "delete_event",
    - G_CALLBACK(destroy_toolbar_bgcolor), toolbar);
    - g_signal_connect(G_OBJECT(priv->bgcolor_dialog),
    - "response", G_CALLBACK(do_bgcolor), toolbar);
    - }
    - g_free(color);
    -
    - gtk_window_present(GTK_WINDOW(priv->bgcolor_dialog));
    - } else {
    - destroy_toolbar_bgcolor(GTK_WIDGET(toolbar), NULL, toolbar);
    - }
    -
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -clear_formatting_cb(GtkAction *clear, PidginWebViewToolbar *toolbar)
    -{
    - pidgin_webview_clear_formatting(PIDGIN_WEBVIEW(toolbar->webview));
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -cancel_link_cb(PidginWebViewToolbar *toolbar, PurpleRequestFields *fields)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(priv->link), FALSE);
    -
    - priv->link_dialog = NULL;
    -}
    -
    -static void
    -close_link_dialog(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - if (priv->link_dialog != NULL)
    - {
    - purple_request_close(PURPLE_REQUEST_FIELDS, priv->link_dialog);
    - priv->link_dialog = NULL;
    - }
    -}
    -
    -static void
    -do_insert_link_cb(PidginWebViewToolbar *toolbar, PurpleRequestFields *fields)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - const char *url, *description;
    -
    - url = purple_request_fields_get_string(fields, "url");
    - if (pidgin_webview_get_format_functions(PIDGIN_WEBVIEW(toolbar->webview)) & PIDGIN_WEBVIEW_LINKDESC)
    - description = purple_request_fields_get_string(fields, "description");
    - else
    - description = NULL;
    -
    - pidgin_webview_insert_link(PIDGIN_WEBVIEW(toolbar->webview), url, description);
    -
    - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(priv->link), FALSE);
    -
    - priv->link_dialog = NULL;
    -}
    -
    -static void
    -insert_link_cb(GtkAction *action, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(priv->link))) {
    - PurpleRequestFields *fields;
    - PurpleRequestFieldGroup *group;
    - PurpleRequestField *field;
    - char *msg;
    - char *desc = NULL;
    -
    - fields = purple_request_fields_new();
    -
    - group = purple_request_field_group_new(NULL);
    - purple_request_fields_add_group(fields, group);
    -
    - field = purple_request_field_string_new("url", _("_URL"), NULL, FALSE);
    - purple_request_field_set_required(field, TRUE);
    - purple_request_field_group_add_field(group, field);
    -
    - if (pidgin_webview_get_format_functions(PIDGIN_WEBVIEW(toolbar->webview)) & PIDGIN_WEBVIEW_LINKDESC) {
    - desc = pidgin_webview_get_selected_text(PIDGIN_WEBVIEW(toolbar->webview));
    - field = purple_request_field_string_new("description", _("_Description"),
    - desc, FALSE);
    - purple_request_field_group_add_field(group, field);
    - msg = g_strdup(_("Please enter the URL and description of the "
    - "link that you want to insert. The description "
    - "is optional."));
    - } else {
    - msg = g_strdup(_("Please enter the URL of the "
    - "link that you want to insert."));
    - }
    -
    - priv->link_dialog =
    - purple_request_fields(toolbar, _("Insert Link"), NULL,
    - msg, fields, _("_Insert"),
    - G_CALLBACK(do_insert_link_cb), _("Cancel"),
    - G_CALLBACK(cancel_link_cb), NULL, toolbar);
    - g_free(msg);
    - g_free(desc);
    - } else {
    - close_link_dialog(toolbar);
    - }
    -
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -insert_hr_cb(GtkAction *action, PidginWebViewToolbar *toolbar)
    -{
    - pidgin_webview_insert_hr(PIDGIN_WEBVIEW(toolbar->webview));
    -}
    -
    -static void
    -do_insert_image_cb(GtkWidget *widget, int response, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - gchar *filename = NULL;
    - PurpleImage *img;
    -
    - if (response == GTK_RESPONSE_ACCEPT)
    - filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
    -
    - /* The following triggers a callback that closes the widget */
    - gtk_action_activate(priv->image);
    -
    - if (filename == NULL)
    - return;
    -
    - img = purple_image_new_from_file(filename, NULL);
    -
    - if (!img) {
    - gchar *buf = g_strdup_printf(_("Failed to store image: %s"),
    - filename);
    -
    - purple_notify_error(NULL, NULL, buf, NULL, NULL);
    -
    - g_free(buf);
    - g_free(filename);
    -
    - return;
    - }
    -
    - g_free(filename);
    -
    - pidgin_webview_insert_image(PIDGIN_WEBVIEW(toolbar->webview), img);
    - /* TODO: do it after passing an image to protocol, not before
    - * g_object_unref(img);
    - */
    -}
    -
    -static void
    -insert_image_cb(GtkAction *action, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - GtkWidget *window;
    -
    - if (!priv->image_dialog) {
    - window = gtk_file_chooser_dialog_new(_("Insert Image"), NULL,
    - GTK_FILE_CHOOSER_ACTION_OPEN,
    - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
    - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
    - NULL);
    - gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_ACCEPT);
    - g_signal_connect(G_OBJECT(window), "response",
    - G_CALLBACK(do_insert_image_cb), toolbar);
    -
    - gtk_widget_show(window);
    - priv->image_dialog = window;
    - } else {
    - gtk_widget_destroy(priv->image_dialog);
    - priv->image_dialog = NULL;
    - }
    -
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -destroy_smiley_dialog(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - if (priv->smiley_dialog != NULL)
    - {
    - gtk_widget_destroy(priv->smiley_dialog);
    - priv->smiley_dialog = NULL;
    - }
    -}
    -
    -static gboolean
    -close_smiley_dialog(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(priv->smiley), FALSE);
    - return FALSE;
    -}
    -
    -static void
    -insert_smiley_text(GtkWidget *widget, PidginWebViewToolbar *toolbar)
    -{
    - PurpleSmiley *smiley;
    - guint image_id;
    - gchar *escaped_smiley, *smiley_html;
    - const gchar *smiley_class;
    -
    - smiley = g_object_get_data(G_OBJECT(widget), "smiley");
    - smiley_class = g_object_get_data(G_OBJECT(widget), "smiley-class");
    - image_id = purple_image_store_add(PURPLE_IMAGE(smiley));
    -
    - escaped_smiley = g_markup_escape_text(
    - purple_smiley_get_shortcut(smiley), -1);
    - smiley_html = g_strdup_printf("<img src=\"" PURPLE_IMAGE_STORE_PROTOCOL
    - "%u\" class=\"emoticon %s-emoticon\" alt=\"%s\" title=\"%s\">",
    - image_id, smiley_class, escaped_smiley, escaped_smiley);
    -
    - g_free(escaped_smiley);
    -
    - pidgin_webview_append_html(PIDGIN_WEBVIEW(toolbar->webview),
    - smiley_html);
    -
    - g_free(smiley_html);
    -
    - close_smiley_dialog(toolbar);
    -}
    -
    -static gboolean
    -smiley_dialog_input_cb(GtkWidget *dialog, GdkEvent *event,
    - PidginWebViewToolbar *toolbar)
    -{
    - if ((event->type == GDK_KEY_PRESS && event->key.keyval == GDK_KEY_Escape) ||
    - (event->type == GDK_BUTTON_PRESS && event->button.button == GDK_BUTTON_PRIMARY))
    - {
    - close_smiley_dialog(toolbar);
    - return TRUE;
    - }
    -
    - return FALSE;
    -}
    -
    -/* returns: total width */
    -static gulong
    -smileys_load_button_thumbs(GList *smileys)
    -{
    - GList *it;
    - gulong total_width = 0;
    -
    - for (it = smileys; it; it = g_list_next(it)) {
    - PurpleSmiley *smiley = it->data;
    - GdkPixbuf *pixbuf;
    - guint width;
    -
    - width = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(smiley),
    - "pidgin-webviewtoolbar-button-width"));
    -
    - /* smiley is already loaded */
    - if (width > 0) {
    - total_width += width;
    - continue;
    - }
    -
    - pixbuf = pidgin_pixbuf_from_image(PURPLE_IMAGE(smiley));
    - pixbuf = pidgin_pixbuf_scale_down(pixbuf,
    - 24, 24, GDK_INTERP_BILINEAR, TRUE);
    -
    - if (pixbuf)
    - width = gdk_pixbuf_get_width(pixbuf);
    - if (width == 0)
    - width = 1;
    -
    - /* XXX: a padding for the button */
    - width += 12;
    -
    - g_object_set_data(G_OBJECT(smiley),
    - "pidgin-webviewtoolbar-button-width",
    - GINT_TO_POINTER(width));
    - g_object_set_data_full(G_OBJECT(smiley),
    - "pidgin-webviewtoolbar-button-image",
    - pixbuf, g_object_unref);
    -
    - total_width += width;
    - }
    -
    - return total_width;
    -}
    -
    -static void
    -add_smiley_list(PidginWebViewToolbar *toolbar, GtkWidget *container,
    - GList *smileys, int max_width, PurpleSmileyList *shadow_smileys,
    - const gchar *smiley_class)
    -{
    - GList *it;
    - GtkWidget *line;
    - int line_width = 0;
    -
    - if (!smileys)
    - return;
    -
    - /* TODO: sort smileys by their position in theme */
    -
    - line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    - gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0);
    - for (it = smileys; it; it = g_list_next(it)) {
    - PurpleSmiley *smiley = it->data;
    - GtkWidget *button;
    - GdkPixbuf *pixbuf;
    - GtkImage *image;
    - guint width;
    - const gchar *smiley_shortcut;
    -
    - smiley_shortcut = purple_smiley_get_shortcut(smiley);
    -
    - width = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(smiley),
    - "pidgin-webviewtoolbar-button-width"));
    - pixbuf = g_object_get_data(G_OBJECT(smiley),
    - "pidgin-webviewtoolbar-button-image");
    - if (!pixbuf) {
    - purple_debug_warning("gtkwebviewtoolbar", "Smiley does "
    - "not exists: %s", smiley_shortcut);
    - continue;
    - }
    -
    - image = GTK_IMAGE(gtk_image_new_from_pixbuf(pixbuf));
    -
    - button = gtk_button_new();
    - gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(image));
    -
    - g_object_ref(smiley);
    - g_object_set_data_full(G_OBJECT(button), "smiley",
    - smiley, g_object_unref);
    - g_object_set_data(G_OBJECT(button),
    - "smiley-class", (gpointer)smiley_class);
    -
    - g_signal_connect(G_OBJECT(button), "clicked",
    - G_CALLBACK(insert_smiley_text), toolbar);
    - gtk_widget_set_tooltip_text(button, smiley_shortcut);
    - gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
    -
    - /* Disable theme smileys shadowed by custom smileys.
    - * There is a case, when a theme smiley have another,
    - * non-shadowed shortcut. But we won't handle it.
    - */
    - if (shadow_smileys && purple_smiley_list_get_by_shortcut(
    - shadow_smileys, smiley_shortcut))
    - {
    - gchar tip[1000];
    - g_snprintf(tip, sizeof(tip), _("This smiley is "
    - "disabled because a custom smiley exists for "
    - "this shortcut:\n %s"), smiley_shortcut);
    - gtk_widget_set_tooltip_text(button, tip);
    - gtk_widget_set_sensitive(button, FALSE);
    - }
    -
    - gtk_box_pack_start(GTK_BOX(line), button, FALSE, FALSE, 0);
    - gtk_widget_show(button);
    -
    - line_width += width;
    - if (line_width >= max_width && g_list_next(it)) {
    - line_width = 0;
    - line = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    - gtk_box_pack_start(GTK_BOX(container), line, FALSE, FALSE, 0);
    - }
    - }
    -
    -}
    -
    -static void
    -insert_smiley_manage_cb(GtkButton *button, gpointer _dialog)
    -{
    - GtkWidget *dialog = _dialog;
    -
    - gtk_widget_destroy(dialog);
    - pidgin_smiley_manager_show();
    -}
    -
    -static void
    -insert_smiley_cb(GtkAction *smiley, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - PurpleSmileyList *smileys_from_theme, *smileys_from_custom = NULL;
    - GList *theme_smileys = NULL, *custom_smileys = NULL, *it, *it_next;
    - PidginWebViewButtons webview_format;
    -
    - GtkWidget *dialog, *vbox;
    - GtkWidget *smiley_table = NULL;
    - gboolean supports_custom = FALSE;
    - GtkRequisition req;
    - GtkWidget *scrolled, *viewport;
    -
    - if (!gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(smiley))) {
    - destroy_smiley_dialog(toolbar);
    - gtk_widget_grab_focus(toolbar->webview);
    - return;
    - }
    -
    - webview_format = pidgin_webview_get_format_functions(
    - PIDGIN_WEBVIEW(toolbar->webview));
    -
    - smileys_from_theme = pidgin_smiley_theme_for_conv(priv->active_conv);
    - if (smileys_from_theme) {
    - theme_smileys = purple_smiley_list_get_unique(
    - smileys_from_theme);
    - }
    -
    - /* remove hidden theme smileys */
    - for (it = theme_smileys; it; it = it_next) {
    - PurpleSmiley *smiley = it->data;
    - it_next = g_list_next(it);
    -
    - if (!g_object_get_data(G_OBJECT(smiley),
    - "pidgin-smiley-hidden"))
    - {
    - continue;
    - }
    -
    - theme_smileys = g_list_delete_link(theme_smileys, it);
    - }
    -
    - supports_custom = (webview_format & PIDGIN_WEBVIEW_CUSTOM_SMILEY);
    - if (supports_custom) {
    - smileys_from_custom = purple_smiley_custom_get_list();
    - custom_smileys = purple_smiley_list_get_all(
    - smileys_from_custom);
    - }
    -
    - dialog = pidgin_create_dialog(_("Smile!"), 0, "smiley_dialog", FALSE);
    - gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
    - vbox = pidgin_dialog_get_vbox_with_properties(
    - GTK_DIALOG(dialog), FALSE, 0);
    -
    - if (theme_smileys != NULL || custom_smileys != NULL) {
    - guint max_line_width, num_lines, button_width = 0;
    -
    - /* Fill the cache (images and their widths). */
    - max_line_width = smileys_load_button_thumbs(theme_smileys);
    - max_line_width += smileys_load_button_thumbs(custom_smileys);
    - num_lines = sqrt(g_list_length(theme_smileys) +
    - g_list_length(custom_smileys));
    - max_line_width /= num_lines;
    -
    - /* We use hboxes packed in a vbox. */
    - smiley_table = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    -
    - /* Custom smileys manager button. */
    - if (supports_custom) {
    - GtkWidget *manage = gtk_button_new_with_mnemonic(
    - _("_Manage custom smileys"));
    - GtkRequisition req;
    - g_signal_connect(G_OBJECT(manage), "clicked",
    - G_CALLBACK(insert_smiley_manage_cb), dialog);
    - gtk_box_pack_end(GTK_BOX(vbox), manage, FALSE, TRUE, 0);
    - gtk_widget_get_preferred_size(manage, NULL, &req);
    - button_width = req.width;
    - max_line_width = MAX(button_width, max_line_width);
    - } else {
    - max_line_width = MAX(max_line_width, 100);
    - }
    -
    - /* Add buttons for smileys. */
    - if (theme_smileys) {
    - add_smiley_list(toolbar, smiley_table, theme_smileys,
    - max_line_width, smileys_from_custom, "theme");
    - }
    - if (theme_smileys && custom_smileys) {
    - gtk_box_pack_start(GTK_BOX(smiley_table),
    - gtk_separator_new(GTK_ORIENTATION_HORIZONTAL),
    - TRUE, FALSE, 0);
    - }
    - if (custom_smileys) {
    - add_smiley_list(toolbar, smiley_table, custom_smileys,
    - max_line_width, NULL, "custom");
    - }
    -
    - gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK);
    - } else {
    - smiley_table = gtk_label_new(_("This theme has no available smileys."));
    - gtk_widget_add_events(dialog, GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK);
    - g_signal_connect(G_OBJECT(dialog), "button-press-event", (GCallback)smiley_dialog_input_cb, toolbar);
    - }
    -
    - g_list_free(theme_smileys);
    - g_list_free(custom_smileys);
    -
    - scrolled = pidgin_make_scrollable(smiley_table, GTK_POLICY_NEVER, GTK_POLICY_NEVER, GTK_SHADOW_NONE, -1, -1);
    - gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
    - gtk_widget_show(smiley_table);
    -
    - viewport = gtk_widget_get_parent(smiley_table);
    - gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
    -
    - /* connect signals */
    - g_signal_connect_swapped(G_OBJECT(dialog), "destroy", G_CALLBACK(close_smiley_dialog), toolbar);
    - g_signal_connect(G_OBJECT(dialog), "key-press-event", G_CALLBACK(smiley_dialog_input_cb), toolbar);
    -
    - gtk_window_set_transient_for(GTK_WINDOW(dialog),
    - GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(toolbar))));
    -
    - /* show everything */
    - gtk_widget_show_all(dialog);
    -
    - gtk_widget_get_preferred_size(viewport, NULL, &req);
    - gtk_widget_set_size_request(scrolled, MIN(300, req.width), MIN(290, req.height));
    -
    - /* The window has to be made resizable, and the scrollbars in the scrolled window
    - * enabled only after setting the desired size of the window. If we do either of
    - * these tasks before now, GTK+ miscalculates the required size, and erronously
    - * makes one or both scrollbars visible (sometimes).
    - * I too think this hack is gross. But I couldn't find a better way -- sadrul */
    - gtk_window_set_resizable(GTK_WINDOW(dialog), TRUE);
    - g_object_set(G_OBJECT(scrolled),
    - "hscrollbar-policy", GTK_POLICY_AUTOMATIC,
    - "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
    - NULL);
    -
    -#ifdef _WIN32
    - winpidgin_ensure_onscreen(dialog);
    -#endif
    -
    - priv->smiley_dialog = dialog;
    -
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -update_smiley_button(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - PurpleSmileyList *sl;
    - gboolean any_smileys;
    - PidginWebViewButtons webview_format = 0;
    -
    - g_return_if_fail(priv != NULL);
    -
    - if (toolbar->webview) {
    - webview_format = pidgin_webview_get_format_functions(
    - PIDGIN_WEBVIEW(toolbar->webview));
    - }
    -
    - if (!priv->allow_smileys) {
    - gtk_action_set_sensitive(priv->smiley, FALSE);
    - return;
    - }
    -
    - sl = pidgin_smiley_theme_for_conv(priv->active_conv);
    - /* it's possible, that all theme smileys are hidden,
    - * but we won't handle it */
    - any_smileys = (sl ? !purple_smiley_list_is_empty(sl) : FALSE);
    -
    - if (!any_smileys && (webview_format & PIDGIN_WEBVIEW_CUSTOM_SMILEY)) {
    - sl = purple_smiley_custom_get_list();
    - any_smileys = (sl ? !purple_smiley_list_is_empty(sl) : FALSE);
    - }
    -
    - gtk_action_set_sensitive(priv->smiley, any_smileys);
    -}
    -
    -static void
    -send_attention_cb(GtkAction *attention, PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - PurpleConversation *conv = priv->active_conv;
    - const gchar *who = purple_conversation_get_name(conv);
    - PurpleConnection *gc = purple_conversation_get_connection(conv);
    -
    - purple_protocol_send_attention(gc, who, 0);
    - gtk_widget_grab_focus(toolbar->webview);
    -}
    -
    -static void
    -update_buttons_cb(PidginWebView *webview, PidginWebViewButtons buttons,
    - PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - gtk_action_set_sensitive(priv->bold, buttons & PIDGIN_WEBVIEW_BOLD);
    - gtk_action_set_sensitive(priv->italic, buttons & PIDGIN_WEBVIEW_ITALIC);
    - gtk_action_set_sensitive(priv->underline, buttons & PIDGIN_WEBVIEW_UNDERLINE);
    - gtk_action_set_sensitive(priv->strike, buttons & PIDGIN_WEBVIEW_STRIKE);
    -
    - gtk_action_set_sensitive(priv->larger_size, buttons & PIDGIN_WEBVIEW_GROW);
    - gtk_action_set_sensitive(priv->smaller_size, buttons & PIDGIN_WEBVIEW_SHRINK);
    -
    - gtk_action_set_sensitive(priv->font, buttons & PIDGIN_WEBVIEW_FACE);
    - gtk_action_set_sensitive(priv->fgcolor, buttons & PIDGIN_WEBVIEW_FORECOLOR);
    - gtk_action_set_sensitive(priv->bgcolor, buttons & PIDGIN_WEBVIEW_BACKCOLOR);
    -
    - gtk_action_set_sensitive(priv->clear,
    - (buttons & PIDGIN_WEBVIEW_BOLD ||
    - buttons & PIDGIN_WEBVIEW_ITALIC ||
    - buttons & PIDGIN_WEBVIEW_UNDERLINE ||
    - buttons & PIDGIN_WEBVIEW_STRIKE ||
    - buttons & PIDGIN_WEBVIEW_GROW ||
    - buttons & PIDGIN_WEBVIEW_SHRINK ||
    - buttons & PIDGIN_WEBVIEW_FACE ||
    - buttons & PIDGIN_WEBVIEW_FORECOLOR ||
    - buttons & PIDGIN_WEBVIEW_BACKCOLOR));
    -
    - gtk_action_set_sensitive(priv->image, buttons & PIDGIN_WEBVIEW_IMAGE);
    - gtk_action_set_sensitive(priv->link, buttons & PIDGIN_WEBVIEW_LINK);
    -
    - priv->allow_smileys = !!(buttons & PIDGIN_WEBVIEW_SMILEY);
    - update_smiley_button(toolbar);
    -}
    -
    -/* we call this when we want to _set_active the toggle button, it'll
    - * block the callback that's connected to the button so we don't have to
    - * do the double toggling hack
    - */
    -static void
    -toggle_action_set_active_block(GtkToggleAction *action, gboolean is_active,
    - PidginWebViewToolbar *toolbar)
    -{
    - GObject *object;
    - g_return_if_fail(toolbar);
    -
    - object = g_object_ref(G_OBJECT(action));
    - g_signal_handlers_block_matched(object, G_SIGNAL_MATCH_DATA,
    - 0, 0, NULL, NULL, toolbar);
    -
    - gtk_toggle_action_set_active(action, is_active);
    - g_signal_handlers_unblock_matched(object, G_SIGNAL_MATCH_DATA,
    - 0, 0, NULL, NULL, toolbar);
    - g_object_unref(object);
    -}
    -
    -static void
    -update_buttons(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - gboolean bold, italic, underline, strike;
    - char *tmp, *color_str;
    - char *label;
    - GdkRGBA color;
    -
    - label = g_strdup(_("_Font"));
    -
    - pidgin_webview_get_current_format(PIDGIN_WEBVIEW(toolbar->webview),
    - &bold, &italic, &underline, &strike);
    -
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(priv->bold)) != bold)
    - toggle_action_set_active_block(GTK_TOGGLE_ACTION(priv->bold), bold,
    - toolbar);
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(priv->italic)) != italic)
    - toggle_action_set_active_block(GTK_TOGGLE_ACTION(priv->italic), italic,
    - toolbar);
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(priv->underline)) != underline)
    - toggle_action_set_active_block(GTK_TOGGLE_ACTION(priv->underline),
    - underline, toolbar);
    - if (gtk_toggle_action_get_active(GTK_TOGGLE_ACTION(priv->strike)) != strike)
    - toggle_action_set_active_block(GTK_TOGGLE_ACTION(priv->strike), strike,
    - toolbar);
    -
    - if (bold) {
    - gchar *markup = g_strdup_printf("<b>%s</b>", label);
    - g_free(label);
    - label = markup;
    - }
    - if (italic) {
    - gchar *markup = g_strdup_printf("<i>%s</i>", label);
    - g_free(label);
    - label = markup;
    - }
    - if (underline) {
    - gchar *markup = g_strdup_printf("<u>%s</u>", label);
    - g_free(label);
    - label = markup;
    - }
    - if (strike) {
    - gchar *markup = g_strdup_printf("<s>%s</s>", label);
    - g_free(label);
    - label = markup;
    - }
    -
    - tmp = pidgin_webview_get_current_fontface(PIDGIN_WEBVIEW(toolbar->webview));
    - if (tmp && tmp[0] == '\0')
    - tmp = NULL;
    - if (g_strcmp0(tmp, PIDGIN_WEBVIEWTOOLBAR_DEFAULT_FONT) == 0)
    - tmp = NULL;
    - toggle_action_set_active_block(GTK_TOGGLE_ACTION(priv->font),
    - (tmp && *tmp), toolbar);
    - if (tmp && *tmp) {
    - gchar *markup = g_strdup_printf("<span face=\"%s\">%s</span>",
    - tmp, label);
    - g_free(label);
    - label = markup;
    - }
    - g_free(tmp);
    -
    - tmp = pidgin_webview_get_current_forecolor(
    - PIDGIN_WEBVIEW(toolbar->webview));
    - color_str = NULL;
    - if (pidgin_color_parse(tmp, &color) &&
    - (color.red > 0 || color.green > 0 || color.blue > 0))
    - {
    - color_str = pidgin_color_to_str(&color);
    - }
    - g_free(tmp);
    -
    - toggle_action_set_active_block(GTK_TOGGLE_ACTION(priv->fgcolor),
    - color_str != NULL, toolbar);
    - if (color_str) {
    - gchar *markup = g_strdup_printf(
    - "<span foreground=\"%s\">%s</span>", color_str, label);
    - g_free(label);
    - label = markup;
    - }
    - g_free(color_str);
    -
    - tmp = pidgin_webview_get_current_backcolor(
    - PIDGIN_WEBVIEW(toolbar->webview));
    - color_str = NULL;
    - if (pidgin_color_parse(tmp, &color))
    - {
    - color_str = pidgin_color_to_str(&color);
    - }
    - g_free(tmp);
    -
    - toggle_action_set_active_block(GTK_TOGGLE_ACTION(priv->bgcolor),
    - color_str != NULL, toolbar);
    - if (color_str) {
    - gchar *markup = g_strdup_printf(
    - "<span background=\"%s\">%s</span>", color_str, label);
    - g_free(label);
    - label = markup;
    - }
    - g_free(color_str);
    -
    - gtk_label_set_markup_with_mnemonic(GTK_LABEL(priv->font_label), label);
    -}
    -
    -static void
    -toggle_button_cb(PidginWebView *webview, PidginWebViewButtons buttons,
    - PidginWebViewToolbar *toolbar)
    -{
    - update_buttons(toolbar);
    -}
    -
    -static void
    -update_format_cb(PidginWebView *webview, PidginWebViewToolbar *toolbar)
    -{
    - update_buttons(toolbar);
    -}
    -
    -static void
    -mark_set_cb(PidginWebView *webview, PidginWebViewToolbar *toolbar)
    -{
    - update_buttons(toolbar);
    -}
    -
    -#if GTK_CHECK_VERSION(3,22,0)
    -
    -static void
    -pidgin_menu_clicked(GtkWidget *button, GtkMenu *menu)
    -{
    - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(button))) {
    - gtk_widget_show_all(GTK_WIDGET(menu));
    - gtk_menu_popup_at_widget(menu, button, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
    - }
    -}
    -
    -#else /* GTK+ 3.22.0 */
    -
    -/* This comes from gtkmenutoolbutton.c from gtk+
    - * Copyright (C) 2003 Ricardo Fernandez Pascual
    - * Copyright (C) 2004 Paolo Borelli
    - */
    -static void
    -menu_position_func(GtkMenu *menu,
    - int *x,
    - int *y,
    - gboolean *push_in,
    - gpointer data)
    -{
    - GtkWidget *widget = GTK_WIDGET(data);
    - GtkAllocation allocation;
    - int savy;
    -
    - gtk_widget_get_allocation(widget, &allocation);
    - gdk_window_get_origin(gtk_widget_get_window(widget), x, y);
    - *x += allocation.x;
    - *y += allocation.y + allocation.height;
    - savy = *y;
    -
    - pidgin_menu_position_func_helper(menu, x, y, push_in, data);
    -
    - if (savy > *y + 1)
    - *y -= allocation.height;
    -}
    -
    -static void
    -pidgin_menu_clicked(GtkWidget *button, GtkMenu *menu)
    -{
    - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(button))) {
    - gtk_widget_show_all(GTK_WIDGET(menu));
    - gtk_menu_popup(menu, NULL, NULL, menu_position_func, button, 0, gtk_get_current_event_time());
    - }
    -}
    -
    -#endif /* GTK+ 3.22.0 */
    -
    -static void
    -pidgin_menu_deactivate(GtkWidget *menu, GtkToggleButton *button)
    -{
    - gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(button), FALSE);
    -}
    -
    -static void
    -switch_toolbar_view(GtkWidget *item, PidginWebViewToolbar *toolbar)
    -{
    - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
    - !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide"));
    -}
    -
    -static gboolean
    -pidgin_webviewtoolbar_popup_menu(GtkWidget *widget, GdkEventButton *event,
    - PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - GtkWidget *menu;
    - GtkWidget *item;
    - gboolean wide;
    -
    - if (!gdk_event_triggers_context_menu((GdkEvent *)event))
    - return FALSE;
    -
    - wide = gtk_widget_get_visible(priv->wide_view);
    -
    - menu = gtk_menu_new();
    - item = gtk_menu_item_new_with_mnemonic(wide ? _("Group Items") : _("Ungroup Items"));
    - g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(switch_toolbar_view), toolbar);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    - gtk_widget_show(item);
    -
    - gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
    -
    - return TRUE;
    -}
    -
    -static void
    -enable_markup(GtkWidget *widget, gpointer null)
    -{
    - GtkWidget *label;
    - label = gtk_bin_get_child(GTK_BIN(widget));
    - if (GTK_IS_LABEL(label))
    - g_object_set(G_OBJECT(label), "use-markup", TRUE, NULL);
    -}
    -
    -static void
    -webviewtoolbar_view_pref_changed(const char *name, PurplePrefType type,
    - gconstpointer value, gpointer toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - if (value) {
    - gtk_widget_hide(priv->lean_view);
    - gtk_widget_show_all(priv->wide_view);
    - } else {
    - gtk_widget_hide(priv->wide_view);
    - gtk_widget_show_all(priv->lean_view);
    - }
    -}
    -
    -/******************************************************************************
    - * GObject stuff
    - *****************************************************************************/
    -
    -static void
    -pidgin_webviewtoolbar_finalize(GObject *object)
    -{
    - PidginWebViewToolbar *toolbar = PIDGIN_WEBVIEWTOOLBAR(object);
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - if (priv->image_dialog != NULL)
    - {
    - gtk_widget_destroy(priv->image_dialog);
    - priv->image_dialog = NULL;
    - }
    -
    - destroy_toolbar_font(toolbar);
    - if (priv->smiley_dialog != NULL) {
    - g_signal_handlers_disconnect_by_func(G_OBJECT(priv->smiley_dialog), close_smiley_dialog, toolbar);
    - destroy_smiley_dialog(toolbar);
    - }
    - destroy_toolbar_bgcolor(NULL, NULL, toolbar);
    - destroy_toolbar_fgcolor(NULL, NULL, toolbar);
    - close_link_dialog(toolbar);
    - if (toolbar->webview) {
    - g_signal_handlers_disconnect_matched(toolbar->webview,
    - G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
    - toolbar);
    -#if 0
    - g_signal_handlers_disconnect_matched(PIDGIN_WEBVIEW(toolbar->webview)->text_buffer,
    - G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
    - toolbar);
    -#endif
    - }
    -
    - if (priv->font_menu)
    - gtk_widget_destroy(priv->font_menu);
    - if (priv->insert_menu)
    - gtk_widget_destroy(priv->insert_menu);
    -
    - purple_prefs_disconnect_by_handle(object);
    -
    - G_OBJECT_CLASS(pidgin_webviewtoolbar_parent_class)->finalize(object);
    -}
    -
    -static void
    -pidgin_webviewtoolbar_class_init(PidginWebViewToolbarClass *klass)
    -{
    - G_OBJECT_CLASS(klass)->finalize = pidgin_webviewtoolbar_finalize;
    -
    - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/conversations/toolbar");
    - purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/conversations/toolbar/wide", FALSE);
    -}
    -
    -static void
    -pidgin_webviewtoolbar_create_actions(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - GtkActionGroup *action_group;
    - gsize i;
    - struct {
    - GtkAction **action;
    - char *name;
    - char *stock;
    - char *label;
    - char *tooltip;
    - void (*cb)();
    - gboolean toggle;
    - } actions[] = {
    - {&priv->bold, "ToggleBold", GTK_STOCK_BOLD, N_("<b>_Bold</b>"), N_("Bold"), do_bold, TRUE},
    - {&priv->italic, "ToggleItalic", GTK_STOCK_ITALIC, N_("<i>_Italic</i>"), N_("Italic"), do_italic, TRUE},
    - {&priv->underline, "ToggleUnderline", GTK_STOCK_UNDERLINE, N_("<u>_Underline</u>"), N_("Underline"), do_underline, TRUE},
    - {&priv->strike, "ToggleStrike", GTK_STOCK_STRIKETHROUGH, N_("<span strikethrough='true'>Strikethrough</span>"), N_("Strikethrough"), do_strikethrough, TRUE},
    - {&priv->larger_size, "ToggleLarger", PIDGIN_STOCK_TOOLBAR_TEXT_LARGER, N_("<span size='larger'>Larger</span>"), N_("Increase Font Size"), do_big, FALSE},
    -#if 0
    - {&priv->normal_size, "ToggleNormal", NULL, N_("Normal"), N_("Normal Font Size"), NULL, FALSE},
    -#endif
    - {&priv->smaller_size, "ToggleSmaller", PIDGIN_STOCK_TOOLBAR_TEXT_SMALLER, N_("<span size='smaller'>Smaller</span>"), N_("Decrease Font Size"), do_small, FALSE},
    - {&priv->font, "ToggleFontFace", PIDGIN_STOCK_TOOLBAR_FONT_FACE, N_("_Font face"), N_("Font Face"), toggle_font, TRUE},
    - {&priv->fgcolor, "ToggleFG", PIDGIN_STOCK_TOOLBAR_FGCOLOR, N_("Foreground _color"), N_("Foreground Color"), toggle_fg_color, TRUE},
    - {&priv->bgcolor, "ToggleBG", PIDGIN_STOCK_TOOLBAR_BGCOLOR, N_("Bac_kground color"), N_("Background Color"), toggle_bg_color, TRUE},
    - {&priv->clear, "ResetFormat", PIDGIN_STOCK_CLEAR, N_("_Reset formatting"), N_("Reset Formatting"), clear_formatting_cb, FALSE},
    - {&priv->image, "InsertImage", PIDGIN_STOCK_TOOLBAR_INSERT_IMAGE, N_("_Image"), N_("Insert IM Image"), insert_image_cb, FALSE},
    - {&priv->link, "InsertLink", PIDGIN_STOCK_TOOLBAR_INSERT_LINK, N_("_Link"), N_("Insert Link"), insert_link_cb, TRUE},
    - {&priv->hr, "InsertHR", NULL, N_("_Horizontal rule"), N_("Insert Horizontal rule"), insert_hr_cb, FALSE},
    - {&priv->smiley, "InsertSmiley", PIDGIN_STOCK_TOOLBAR_SMILEY, N_("_Smile!"), N_("Insert Smiley"), insert_smiley_cb, TRUE},
    - {&priv->attention, "SendAttention", PIDGIN_STOCK_TOOLBAR_SEND_ATTENTION, N_("_Attention!"), N_("Get Attention"), send_attention_cb, FALSE},
    - };
    -
    - action_group = gtk_action_group_new("PidginWebViewToolbar");
    - gtk_action_group_set_translation_domain(action_group, PACKAGE);
    -
    - for (i = 0; i < G_N_ELEMENTS(actions); i++) {
    - GtkAction *action;
    - if (actions[i].toggle) {
    - action = GTK_ACTION(gtk_toggle_action_new(
    - actions[i].name, _(actions[i].label),
    - _(actions[i].tooltip), actions[i].stock));
    - } else {
    - action = gtk_action_new(actions[i].name,
    - _(actions[i].label), _(actions[i].tooltip),
    - actions[i].stock);
    - }
    - gtk_action_set_is_important(action, TRUE);
    - gtk_action_group_add_action(action_group, action);
    - g_signal_connect(G_OBJECT(action), "activate", actions[i].cb, toolbar);
    - *(actions[i].action) = action;
    - }
    -}
    -
    -static void
    -pidgin_webviewtoolbar_create_wide_view(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - GtkAction *layout[] = {
    - priv->bold,
    - priv->italic,
    - priv->underline,
    - priv->strike,
    - NULL,
    - priv->larger_size,
    -#if 0
    - priv->normal_size,
    -#endif
    - priv->smaller_size,
    - NULL,
    - priv->font,
    - priv->fgcolor,
    - priv->bgcolor,
    - NULL,
    - priv->clear,
    - NULL,
    - priv->image,
    - priv->link,
    - NULL,
    - priv->smiley,
    - priv->attention
    - };
    - gsize i;
    - GtkToolItem *item;
    -
    - priv->wide_view = gtk_toolbar_new();
    - gtk_toolbar_set_icon_size(GTK_TOOLBAR(priv->wide_view),
    - gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
    - gtk_toolbar_set_style(GTK_TOOLBAR(priv->wide_view), GTK_TOOLBAR_ICONS);
    -
    - for (i = 0; i < G_N_ELEMENTS(layout); i++) {
    - if (layout[i]) {
    - item = GTK_TOOL_ITEM(gtk_action_create_tool_item(layout[i]));
    - g_object_set_data(G_OBJECT(item), "action", layout[i]);
    - } else
    - item = gtk_separator_tool_item_new();
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->wide_view), item, -1);
    - }
    -}
    -
    -static inline void
    -lean_view_add_menu_item(GtkWidget *menu, GtkAction *action)
    -{
    - GtkWidget *menuitem;
    -
    - menuitem = gtk_action_create_menu_item(action);
    - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
    - g_object_set_data(G_OBJECT(menuitem), "action", action);
    -}
    -
    -static void
    -pidgin_webviewtoolbar_create_lean_view(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - GtkWidget *label;
    - GtkToolItem *sep;
    - GtkToolItem *font_button;
    - GtkWidget *font_menu;
    - GtkToolItem *insert_button;
    - GtkWidget *insert_menu;
    - GtkWidget *smiley_button;
    - GtkWidget *attention_button;
    -
    - priv->lean_view = gtk_toolbar_new();
    - gtk_toolbar_set_icon_size(GTK_TOOLBAR(priv->lean_view),
    - gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL));
    - gtk_toolbar_set_style(GTK_TOOLBAR(priv->lean_view), GTK_TOOLBAR_BOTH_HORIZ);
    -
    - /* Fonts */
    - font_button = gtk_toggle_tool_button_new();
    - g_object_set_data_full(G_OBJECT(font_button), "menu-name",
    - g_strdup("font"), g_free);
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->lean_view), font_button, -1);
    - gtk_tool_item_set_is_important(font_button, TRUE);
    - gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(font_button), GTK_STOCK_BOLD);
    - priv->font_label = label = gtk_label_new_with_mnemonic(_("_Font"));
    - gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
    - gtk_tool_button_set_label_widget(GTK_TOOL_BUTTON(font_button), label);
    -
    - priv->font_menu = font_menu = gtk_menu_new();
    - g_object_set_data(G_OBJECT(font_button), "menu", font_menu);
    -
    - lean_view_add_menu_item(font_menu, priv->bold);
    - lean_view_add_menu_item(font_menu, priv->italic);
    - lean_view_add_menu_item(font_menu, priv->underline);
    - lean_view_add_menu_item(font_menu, priv->strike);
    - lean_view_add_menu_item(font_menu, priv->larger_size);
    -#if 0
    - lean_view_add_menu_item(font_menu, priv->normal_size);
    -#endif
    - lean_view_add_menu_item(font_menu, priv->smaller_size);
    - lean_view_add_menu_item(font_menu, priv->font);
    - lean_view_add_menu_item(font_menu, priv->fgcolor);
    - lean_view_add_menu_item(font_menu, priv->bgcolor);
    - lean_view_add_menu_item(font_menu, priv->clear);
    -
    - g_signal_connect(G_OBJECT(font_button), "toggled",
    - G_CALLBACK(pidgin_menu_clicked), font_menu);
    - g_signal_connect_object(G_OBJECT(font_menu), "deactivate",
    - G_CALLBACK(pidgin_menu_deactivate), font_button, 0);
    -
    - gtk_container_foreach(GTK_CONTAINER(font_menu), enable_markup, NULL);
    -
    - /* Sep */
    - sep = gtk_separator_tool_item_new();
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->lean_view), sep, -1);
    -
    - /* Insert */
    - insert_button = gtk_toggle_tool_button_new();
    - g_object_set_data_full(G_OBJECT(insert_button), "menu-name",
    - g_strdup("insert"), g_free);
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->lean_view), insert_button, -1);
    - gtk_tool_item_set_is_important(insert_button, TRUE);
    - gtk_tool_button_set_stock_id(GTK_TOOL_BUTTON(insert_button),
    - PIDGIN_STOCK_TOOLBAR_INSERT);
    - label = gtk_label_new_with_mnemonic(_("_Insert"));
    - gtk_tool_button_set_label_widget(GTK_TOOL_BUTTON(insert_button), label);
    -
    - priv->insert_menu = insert_menu = gtk_menu_new();
    - g_object_set_data(G_OBJECT(insert_button), "menu", insert_menu);
    -
    - lean_view_add_menu_item(insert_menu, priv->image);
    - lean_view_add_menu_item(insert_menu, priv->link);
    - lean_view_add_menu_item(insert_menu, priv->hr);
    -
    - g_signal_connect(G_OBJECT(insert_button), "toggled",
    - G_CALLBACK(pidgin_menu_clicked), insert_menu);
    - g_signal_connect_object(G_OBJECT(insert_menu), "deactivate",
    - G_CALLBACK(pidgin_menu_deactivate), insert_button, 0);
    -
    - /* Sep */
    - sep = gtk_separator_tool_item_new();
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->lean_view), sep, -1);
    -
    - /* Smiley */
    - smiley_button = gtk_action_create_tool_item(priv->smiley);
    - g_object_set_data(G_OBJECT(smiley_button), "action", priv->smiley);
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->lean_view),
    - GTK_TOOL_ITEM(smiley_button), -1);
    -
    - /* Sep */
    - sep = gtk_separator_tool_item_new();
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->lean_view), sep, -1);
    -
    - /* Attention */
    - attention_button = gtk_action_create_tool_item(priv->attention);
    - g_object_set_data(G_OBJECT(attention_button), "action", priv->attention);
    - gtk_toolbar_insert(GTK_TOOLBAR(priv->lean_view),
    - GTK_TOOL_ITEM(attention_button), -1);
    -}
    -
    -static void
    -pidgin_webviewtoolbar_init(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - GtkWidget *hbox = GTK_WIDGET(toolbar);
    -
    - pidgin_webviewtoolbar_create_actions(toolbar);
    - pidgin_webviewtoolbar_create_wide_view(toolbar);
    - pidgin_webviewtoolbar_create_lean_view(toolbar);
    -
    - gtk_box_pack_start(GTK_BOX(hbox), priv->wide_view, TRUE, TRUE, 0);
    - gtk_box_pack_start(GTK_BOX(hbox), priv->lean_view, TRUE, TRUE, 0);
    -
    - /* set attention button to be greyed out until we get a conversation */
    - gtk_action_set_sensitive(priv->attention, FALSE);
    -
    - priv->allow_smileys = TRUE;
    - update_smiley_button(toolbar);
    -
    - purple_prefs_connect_callback(toolbar,
    - PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
    - webviewtoolbar_view_pref_changed, toolbar);
    - g_signal_connect_data(G_OBJECT(toolbar), "realize",
    - G_CALLBACK(purple_prefs_trigger_callback),
    - PIDGIN_PREFS_ROOT "/conversations/toolbar/wide",
    - NULL, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
    -
    - g_signal_connect(G_OBJECT(hbox), "button-press-event",
    - G_CALLBACK(pidgin_webviewtoolbar_popup_menu), toolbar);
    -}
    -
    -/******************************************************************************
    - * Public API
    - *****************************************************************************/
    -
    -GtkWidget *
    -pidgin_webviewtoolbar_new(void)
    -{
    - return GTK_WIDGET(g_object_new(pidgin_webviewtoolbar_get_type(), NULL));
    -}
    -
    -void
    -pidgin_webviewtoolbar_attach(PidginWebViewToolbar *toolbar, GtkWidget *webview)
    -{
    - PidginWebViewButtons buttons;
    -
    - g_return_if_fail(toolbar != NULL);
    - g_return_if_fail(PIDGIN_IS_WEBVIEWTOOLBAR(toolbar));
    - g_return_if_fail(webview != NULL);
    - g_return_if_fail(PIDGIN_IS_WEBVIEW(webview));
    -
    - toolbar->webview = webview;
    - g_signal_connect(G_OBJECT(webview), "allowed-formats-updated",
    - G_CALLBACK(update_buttons_cb), toolbar);
    - g_signal_connect_after(G_OBJECT(webview), "format-toggled",
    - G_CALLBACK(toggle_button_cb), toolbar);
    - g_signal_connect_after(G_OBJECT(webview), "format-cleared",
    - G_CALLBACK(update_format_cb), toolbar);
    - g_signal_connect(G_OBJECT(webview), "format-updated",
    - G_CALLBACK(update_format_cb), toolbar);
    - g_signal_connect_after(G_OBJECT(webview), "selection-changed",
    - G_CALLBACK(mark_set_cb), toolbar);
    -
    - buttons = pidgin_webview_get_format_functions(PIDGIN_WEBVIEW(webview));
    - update_buttons_cb(PIDGIN_WEBVIEW(webview), buttons, toolbar);
    - update_buttons(toolbar);
    -}
    -
    -void
    -pidgin_webviewtoolbar_switch_active_conversation(PidginWebViewToolbar *toolbar,
    - PurpleConversation *conv)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    - PurpleConnection *gc = purple_conversation_get_connection(conv);
    - PurpleProtocol *protocol = purple_connection_get_protocol(gc);
    -
    - priv->active_conv = conv;
    -
    - /* gray out attention button on protocols that don't support it
    - for the time being it is always disabled for chats */
    - gtk_action_set_sensitive(priv->attention,
    - conv && protocol && PURPLE_IS_IM_CONVERSATION(conv) &&
    - PURPLE_IS_PROTOCOL_ATTENTION(protocol));
    -
    - update_smiley_button(toolbar);
    -}
    -
    -void
    -pidgin_webviewtoolbar_activate(PidginWebViewToolbar *toolbar,
    - PidginWebViewAction action)
    -{
    - PidginWebViewToolbarPrivate *priv;
    - GtkAction *act;
    -
    - g_return_if_fail(toolbar != NULL);
    -
    - priv = pidgin_webviewtoolbar_get_instance_private(toolbar);
    - switch (action) {
    - case PIDGIN_WEBVIEW_ACTION_BOLD:
    - act = priv->bold;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_ITALIC:
    - act = priv->italic;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_UNDERLINE:
    - act = priv->underline;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_STRIKE:
    - act = priv->strike;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_LARGER:
    - act = priv->larger_size;
    - break;
    -
    -#if 0
    - case PIDGIN_WEBVIEW_ACTION_NORMAL:
    - act = priv->normal_size;
    - break;
    -#endif
    -
    - case PIDGIN_WEBVIEW_ACTION_SMALLER:
    - act = priv->smaller_size;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_FONTFACE:
    - act = priv->font;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_FGCOLOR:
    - act = priv->fgcolor;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_BGCOLOR:
    - act = priv->bgcolor;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_CLEAR:
    - act = priv->clear;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_IMAGE:
    - act = priv->image;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_LINK:
    - act = priv->link;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_HR:
    - act = priv->hr;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_SMILEY:
    - act = priv->smiley;
    - break;
    -
    - case PIDGIN_WEBVIEW_ACTION_ATTENTION:
    - act = priv->attention;
    - break;
    -
    - default:
    - g_return_if_reached();
    - break;
    - }
    -
    - gtk_action_activate(act);
    -}
    -
    -GtkWidget *
    -pidgin_webviewtoolbar_get_wide_view(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - g_return_val_if_fail(toolbar != NULL, NULL);
    -
    - return priv->wide_view;
    -}
    -
    -GtkWidget *
    -pidgin_webviewtoolbar_get_lean_view(PidginWebViewToolbar *toolbar)
    -{
    - PidginWebViewToolbarPrivate *priv =
    - pidgin_webviewtoolbar_get_instance_private(toolbar);
    -
    - g_return_val_if_fail(toolbar != NULL, NULL);
    -
    - return priv->lean_view;
    -}
    --- a/pidgin/gtkwebviewtoolbar.h Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,131 +0,0 @@
    -/*
    - * PidginWebViewToolbar
    - *
    - * Pidgin is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#ifndef _PIDGINWEBVIEWTOOLBAR_H_
    -#define _PIDGINWEBVIEWTOOLBAR_H_
    -/**
    - * SECTION:gtkwebviewtoolbar
    - * @section_id: pidgin-gtkwebviewtoolbar
    - * @short_description: <filename>gtkwebviewtoolbar.h</filename>
    - * @title: WebView Toolbar
    - */
    -
    -#include <gtk/gtk.h>
    -#include "gtkwebview.h"
    -
    -#define PIDGIN_DEFAULT_FONT_FACE "Helvetica 12"
    -
    -#define PIDGIN_TYPE_WEBVIEWTOOLBAR (pidgin_webviewtoolbar_get_type())
    -#define PIDGIN_WEBVIEWTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PIDGIN_TYPE_WEBVIEWTOOLBAR, PidginWebViewToolbar))
    -#define PIDGIN_WEBVIEWTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PIDGIN_TYPE_WEBVIEWTOOLBAR, PidginWebViewToolbarClass))
    -#define PIDGIN_IS_WEBVIEWTOOLBAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PIDGIN_TYPE_WEBVIEWTOOLBAR))
    -#define PIDGIN_IS_WEBVIEWTOOLBAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PIDGIN_TYPE_WEBVIEWTOOLBAR))
    -#define PIDGIN_WEBVIEWTOOLBAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PIDGIN_TYPE_WEBVIEWTOOLBAR, PidginWebViewToolbarClass))
    -
    -typedef struct _PidginWebViewToolbar PidginWebViewToolbar;
    -typedef struct _PidginWebViewToolbarClass PidginWebViewToolbarClass;
    -
    -struct _PidginWebViewToolbar {
    - GtkBox box;
    -
    - GtkWidget *webview;
    -};
    -
    -struct _PidginWebViewToolbarClass {
    - GtkBoxClass parent_class;
    -};
    -
    -G_BEGIN_DECLS
    -
    -/**
    - * pidgin_webviewtoolbar_get_type:
    - *
    - * Returns the GType for a PidginWebViewToolbar widget
    - *
    - * Returns: The GType for PidginWebViewToolbar widget
    - */
    -GType pidgin_webviewtoolbar_get_type(void);
    -
    -/**
    - * pidgin_webviewtoolbar_new:
    - *
    - * Create a new PidginWebViewToolbar object
    - *
    - * Returns: A GtkWidget corresponding to the PidginWebViewToolbar object
    - */
    -GtkWidget *pidgin_webviewtoolbar_new(void);
    -
    -/**
    - * pidgin_webviewtoolbar_attach:
    - * @toolbar: The PidginWebViewToolbar object
    - * @webview: The PidginWebView object
    - *
    - * Attach a PidginWebViewToolbar object to a PidginWebView
    - */
    -void pidgin_webviewtoolbar_attach(PidginWebViewToolbar *toolbar, GtkWidget *webview);
    -
    -/**
    - * pidgin_webviewtoolbar_switch_active_conversation:
    - * @toolbar: The PidginWebViewToolbar object
    - * @conv: The new conversation
    - *
    - * Switch the active conversation for a PidginWebViewToolbar object
    - */
    -void pidgin_webviewtoolbar_switch_active_conversation(PidginWebViewToolbar *toolbar,
    - PurpleConversation *conv);
    -
    -/**
    - * pidgin_webviewtoolbar_activate:
    - * @toolbar: The PidginWebViewToolbar object
    - * @action: The PidginWebViewAction
    - *
    - * Activate a PidginWebViewToolbar action
    - */
    -void pidgin_webviewtoolbar_activate(PidginWebViewToolbar *toolbar,
    - PidginWebViewAction action);
    -
    -/**
    - * pidgin_webviewtoolbar_get_wide_view:
    - * @toolbar: The PidginWebViewToolbar object
    - *
    - * Returns the wide toolbar variant widget for the given @toolbar.
    - *
    - * Returns: (transfer none): the wide toolbar variant.
    - */
    -GtkWidget *
    -pidgin_webviewtoolbar_get_wide_view(PidginWebViewToolbar *toolbar);
    -
    -/**
    - * pidgin_webviewtoolbar_get_lean_view:
    - * @toolbar: The PidginWebViewToolbar object
    - *
    - * Returns the lean toolbar variant widget for the given @toolbar.
    - *
    - * Returns: (transfer none): the lean toolbar variant.
    - */
    -GtkWidget *
    -pidgin_webviewtoolbar_get_lean_view(PidginWebViewToolbar *toolbar);
    -
    -G_END_DECLS
    -
    -#endif /* _PIDGINWEBVIEWTOOLBAR_H_ */
    --- a/pidgin/gtkwhiteboard.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkwhiteboard.c Tue Oct 08 21:48:28 2019 -0500
    @@ -37,340 +37,75 @@
    PIDGIN_WHITEBOARD_BRUSH_MOTION
    } PidginWhiteboardBrushState;
    -typedef struct _PidginWhiteboard PidginWhiteboard;
    -typedef struct _PidginWhiteboardPrivate PidginWhiteboardPrivate;
    -
    -struct _PidginWhiteboardPrivate {
    - cairo_t *cr;
    - cairo_surface_t *surface;
    -};
    +#define PIDGIN_TYPE_WHITEBOARD (pidgin_whiteboard_get_type())
    +G_DECLARE_FINAL_TYPE(PidginWhiteboard, pidgin_whiteboard, PIDGIN, WHITEBOARD,
    + GtkWindow)
    /**
    * PidginWhiteboard:
    - * @priv: Internal data
    + * @cr: Cairo context for drawing
    + * @surface: Cairo surface for drawing
    * @wb: Backend data for this whiteboard
    - * @window: Window for the Doodle session
    * @drawing_area: Drawing area
    + * @color_button: A color chooser widget
    * @width: Canvas width
    * @height: Canvas height
    * @brush_color: Foreground color
    * @brush_size: Brush size
    + * @brush_state: The @PidginWhiteboardBrushState state of the brush
    *
    * A PidginWhiteboard
    */
    struct _PidginWhiteboard
    {
    - PidginWhiteboardPrivate *priv;
    + GtkWindow parent;
    +
    + cairo_t *cr;
    + cairo_surface_t *surface;
    PurpleWhiteboard *wb;
    - GtkWidget *window;
    GtkWidget *drawing_area;
    + GtkWidget *color_button;
    int width;
    int height;
    int brush_color;
    int brush_size;
    + PidginWhiteboardBrushState brush_state;
    +
    + /* Tracks last position of the mouse when drawing */
    + gint last_x;
    + gint last_y;
    + /* Tracks how many brush motions made */
    + gint motion_count;
    };
    +G_DEFINE_TYPE(PidginWhiteboard, pidgin_whiteboard, GTK_TYPE_WINDOW)
    +
    /******************************************************************************
    - * Prototypes
    + * Helpers
    *****************************************************************************/
    -static void pidgin_whiteboard_create(PurpleWhiteboard *wb);
    -
    -static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb);
    -static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb);
    -
    -/*static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data); */
    -
    -static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data);
    -static gboolean
    -pidgin_whiteboard_draw_event(GtkWidget *widget, cairo_t *cr,
    - gpointer _gtkwb);
    -
    -static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data);
    -static gboolean pidgin_whiteboard_brush_motion(GtkWidget *widget, GdkEventMotion *event, gpointer data);
    -static gboolean pidgin_whiteboard_brush_up(GtkWidget *widget, GdkEventButton *event, gpointer data);
    -
    -static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb,
    - int x, int y, int color, int size);
    -static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0,
    - int x1, int y1, int color, int size);
    -
    -static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height);
    -static void pidgin_whiteboard_set_brush(PurpleWhiteboard *wb, int size, int color);
    -static void pidgin_whiteboard_clear(PurpleWhiteboard *wb);
    -
    -static void pidgin_whiteboard_button_clear_press(GtkWidget *widget, gpointer data);
    -static void pidgin_whiteboard_button_save_press(GtkWidget *widget, gpointer data);
    -
    -static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb);
    -
    -static void pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color);
    -
    -static void color_selected(GtkColorButton *button, PidginWhiteboard *gtkwb);
    -
    -/******************************************************************************
    - * Globals
    - *****************************************************************************/
    -
    -static int LastX; /* Tracks last position of the mouse when drawing */
    -static int LastY;
    -static int MotionCount; /* Tracks how many brush motions made */
    -static PidginWhiteboardBrushState brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
    -
    -static PurpleWhiteboardUiOps ui_ops =
    +static void
    +pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color)
    {
    - pidgin_whiteboard_create,
    - pidgin_whiteboard_destroy,
    - pidgin_whiteboard_set_dimensions,
    - pidgin_whiteboard_set_brush,
    - pidgin_whiteboard_draw_brush_point,
    - pidgin_whiteboard_draw_brush_line,
    - pidgin_whiteboard_clear,
    - NULL,
    - NULL,
    - NULL,
    - NULL
    -};
    -
    -/******************************************************************************
    - * API
    - *****************************************************************************/
    -PurpleWhiteboardUiOps *pidgin_whiteboard_get_ui_ops(void)
    -{
    - return &ui_ops;
    + color->red = ((color_rgb >> 16) & 0xFF) / 255.0f;
    + color->green = ((color_rgb >> 8) & 0xFF) / 255.0f;
    + color->blue = (color_rgb & 0xFF) / 255.0f;
    + color->alpha = 1.0;
    }
    -static void pidgin_whiteboard_create(PurpleWhiteboard *wb)
    +static gboolean
    +whiteboard_close_cb(GtkWidget *widget, GdkEvent *event,
    + G_GNUC_UNUSED gpointer data)
    {
    - PurpleBuddy *buddy;
    - GtkWidget *window;
    - GtkWidget *drawing_area;
    - GtkWidget *vbox_controls;
    - GtkWidget *hbox_canvas_and_controls;
    -
    - /*
    - --------------------------
    - |[][][][palette[][][][][]|
    - |------------------------|
    - | canvas | con |
    - | | trol|
    - | | s |
    - | | |
    - | | |
    - --------------------------
    - */
    - GtkWidget *clear_button;
    - GtkWidget *save_button;
    - GtkWidget *color_button;
    - GdkRGBA color;
    -
    - PidginWhiteboard *gtkwb = g_new0(PidginWhiteboard, 1);
    - gtkwb->priv = g_new0(PidginWhiteboardPrivate, 1);
    -
    - gtkwb->wb = wb;
    - purple_whiteboard_set_ui_data(wb, gtkwb);
    -
    - /* Get dimensions (default?) for the whiteboard canvas */
    - if (!purple_whiteboard_get_dimensions(wb, &gtkwb->width, &gtkwb->height))
    - {
    - /* Give some initial board-size */
    - gtkwb->width = 300;
    - gtkwb->height = 250;
    - }
    -
    - if (!purple_whiteboard_get_brush(wb, &gtkwb->brush_size, &gtkwb->brush_color))
    - {
    - /* Give some initial brush-info */
    - gtkwb->brush_size = 2;
    - gtkwb->brush_color = 0xff0000;
    - }
    -
    - /* Try and set window title as the name of the buddy, else just use their
    - * username
    - */
    - buddy = purple_blist_find_buddy(purple_whiteboard_get_account(wb), purple_whiteboard_get_who(wb));
    -
    - window = pidgin_create_window(buddy != NULL ? purple_buddy_get_contact_alias(buddy) : purple_whiteboard_get_who(wb), 0, NULL, FALSE);
    - gtkwb->window = window;
    - gtk_widget_set_name(window, purple_whiteboard_get_who(wb));
    -
    - g_signal_connect(G_OBJECT(window), "delete_event",
    - G_CALLBACK(whiteboard_close_cb), gtkwb);
    -
    - hbox_canvas_and_controls = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    - gtk_widget_show(hbox_canvas_and_controls);
    -
    - gtk_container_add(GTK_CONTAINER(window), hbox_canvas_and_controls);
    - gtk_container_set_border_width(GTK_CONTAINER(window), PIDGIN_HIG_BORDER);
    -
    - /* Create the drawing area */
    - drawing_area = gtk_drawing_area_new();
    - gtkwb->drawing_area = drawing_area;
    - gtk_widget_set_size_request(GTK_WIDGET(drawing_area), gtkwb->width, gtkwb->height);
    - gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls), drawing_area, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE);
    -
    - gtk_widget_show(drawing_area);
    -
    - /* Signals used to handle backing pixmap */
    - g_signal_connect(G_OBJECT(drawing_area), "draw",
    - G_CALLBACK(pidgin_whiteboard_draw_event), gtkwb);
    -
    - g_signal_connect(G_OBJECT(drawing_area), "configure-event",
    - G_CALLBACK(pidgin_whiteboard_configure_event), gtkwb);
    -
    - /* Event signals */
    - g_signal_connect(G_OBJECT(drawing_area), "button_press_event",
    - G_CALLBACK(pidgin_whiteboard_brush_down), gtkwb);
    -
    - g_signal_connect(G_OBJECT(drawing_area), "motion_notify_event",
    - G_CALLBACK(pidgin_whiteboard_brush_motion), gtkwb);
    -
    - g_signal_connect(G_OBJECT(drawing_area), "button_release_event",
    - G_CALLBACK(pidgin_whiteboard_brush_up), gtkwb);
    + PidginWhiteboard *gtkwb = PIDGIN_WHITEBOARD(widget);
    - gtk_widget_set_events(drawing_area,
    - GDK_EXPOSURE_MASK |
    - GDK_LEAVE_NOTIFY_MASK |
    - GDK_BUTTON_PRESS_MASK |
    - GDK_POINTER_MOTION_MASK |
    - GDK_BUTTON_RELEASE_MASK |
    - GDK_POINTER_MOTION_HINT_MASK);
    -
    - /* Create vertical box to contain the controls */
    - vbox_controls = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    - gtk_box_pack_start(GTK_BOX(hbox_canvas_and_controls),
    - vbox_controls, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
    - gtk_widget_show(vbox_controls);
    -
    - /* Add a clear button */
    - clear_button = gtk_button_new_from_stock(GTK_STOCK_CLEAR);
    - gtk_box_pack_start(GTK_BOX(vbox_controls), clear_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
    - gtk_widget_show(clear_button);
    - g_signal_connect(G_OBJECT(clear_button), "clicked",
    - G_CALLBACK(pidgin_whiteboard_button_clear_press), gtkwb);
    -
    - /* Add a save button */
    - save_button = gtk_button_new_from_stock(GTK_STOCK_SAVE);
    - gtk_box_pack_start(GTK_BOX(vbox_controls), save_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
    - gtk_widget_show(save_button);
    -
    - g_signal_connect(G_OBJECT(save_button), "clicked",
    - G_CALLBACK(pidgin_whiteboard_button_save_press), gtkwb);
    -
    - /* Add a color selector */
    - color_button = gtk_color_button_new();
    - gtk_box_pack_start(GTK_BOX(vbox_controls), color_button, FALSE, FALSE, PIDGIN_HIG_BOX_SPACE);
    - gtk_widget_show(color_button);
    -
    - gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_button), FALSE);
    - pidgin_whiteboard_rgb24_to_rgba(gtkwb->brush_color, &color);
    - gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_button), &color);
    -
    - g_signal_connect(G_OBJECT(color_button), "color-set",
    - G_CALLBACK(color_selected), gtkwb);
    -
    - /* Make all this (window) visible */
    - gtk_widget_show(window);
    -
    - pidgin_whiteboard_set_canvas_as_icon(gtkwb);
    -
    - /* TODO Specific protocol/whiteboard assignment here? Needs a UI Op? */
    - /* Set default brush size and color */
    - /*
    - ds->brush_size = DOODLE_BRUSH_MEDIUM;
    - ds->brush_color = 0;
    - */
    -}
    -
    -static void pidgin_whiteboard_destroy(PurpleWhiteboard *wb)
    -{
    - PidginWhiteboard *gtkwb;
    - GtkWidget *colour_dialog;
    -
    - g_return_if_fail(wb != NULL);
    - gtkwb = purple_whiteboard_get_ui_data(wb);
    - g_return_if_fail(gtkwb != NULL);
    -
    - /* TODO Ask if user wants to save picture before the session is closed */
    -
    - /* Clear graphical memory */
    - if (gtkwb->priv->cr) {
    - cairo_destroy(gtkwb->priv->cr);
    - gtkwb->priv->cr = NULL;
    - }
    - if (gtkwb->priv->surface) {
    - cairo_surface_destroy(gtkwb->priv->surface);
    - gtkwb->priv->surface = NULL;
    - }
    -
    - colour_dialog = g_object_get_data(G_OBJECT(gtkwb->window), "colour-dialog");
    - if (colour_dialog) {
    - gtk_widget_destroy(colour_dialog);
    - g_object_set_data(G_OBJECT(gtkwb->window), "colour-dialog", NULL);
    - }
    -
    - if(gtkwb->window)
    - {
    - gtk_widget_destroy(gtkwb->window);
    - gtkwb->window = NULL;
    - }
    -
    - g_free(gtkwb->priv);
    - g_free(gtkwb);
    - purple_whiteboard_set_ui_data(wb, NULL);
    -}
    -
    -static gboolean whiteboard_close_cb(GtkWidget *widget, GdkEvent *event, PidginWhiteboard *gtkwb)
    -{
    - PurpleWhiteboard *wb;
    -
    - g_return_val_if_fail(gtkwb != NULL, FALSE);
    - wb = gtkwb->wb;
    - g_return_val_if_fail(wb != NULL, FALSE);
    -
    - g_object_unref(wb);
    + g_clear_object(&gtkwb->wb);
    return FALSE;
    }
    -/*
    - * Whiteboard start button on conversation window (move this code to gtkconv?
    - * and use new protocol member?)
    - */
    -#if 0
    -static void pidginwhiteboard_button_start_press(GtkButton *button, gpointer data)
    -{
    - PurpleConversation *conv = data;
    - PurpleAccount *account = purple_conversation_get_account(conv);
    - PurpleConnection *gc = purple_account_get_connection(account);
    - char *to = (char*)(purple_conversation_get_name(conv));
    -
    - /* Only handle this if local client requested Doodle session (else local
    - * client would have sent one)
    - */
    - PurpleWhiteboard *wb = purple_whiteboard_get(account, to);
    -
    - /* Write a local message to this conversation showing that a request for a
    - * Doodle session has been made
    - */
    - /* XXXX because otherwise gettext will see this string, even though it's
    - * in an #if 0 block. Remove the XXXX if you want to use this code.
    - * But, it really shouldn't be a Yahoo-specific string. ;) */
    - purple_im_conversation_write_message(PURPLE_CONV_IM(conv), "", XXXX_("Sent Doodle request."),
    - PURPLE_MESSAGE_NICK | PURPLE_MESSAGE_RECV, time(NULL));
    -
    - yahoo_doodle_command_send_request(gc, to);
    - yahoo_doodle_command_send_ready(gc, to);
    -
    - /* Insert this 'session' in the list. At this point, it's only a requested
    - * session.
    - */
    - wb = purple_whiteboard_new(account, to, DOODLE_STATE_REQUESTING);
    -}
    -#endif
    -
    static gboolean pidgin_whiteboard_configure_event(GtkWidget *widget, GdkEventConfigure *event, gpointer data)
    {
    PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
    @@ -378,16 +113,18 @@
    GtkAllocation allocation;
    GdkRGBA white = {1.0, 1.0, 1.0, 1.0};
    - if (gtkwb->priv->cr)
    - cairo_destroy(gtkwb->priv->cr);
    - if (gtkwb->priv->surface)
    - cairo_surface_destroy(gtkwb->priv->surface);
    + if (gtkwb->cr) {
    + cairo_destroy(gtkwb->cr);
    + }
    + if (gtkwb->surface) {
    + cairo_surface_destroy(gtkwb->surface);
    + }
    gtk_widget_get_allocation(widget, &allocation);
    - gtkwb->priv->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
    - allocation.width, allocation.height);
    - gtkwb->priv->cr = cr = cairo_create(gtkwb->priv->surface);
    + gtkwb->surface = cairo_image_surface_create(
    + CAIRO_FORMAT_RGB24, allocation.width, allocation.height);
    + gtkwb->cr = cr = cairo_create(gtkwb->surface);
    gdk_cairo_set_source_rgba(cr, &white);
    cairo_rectangle(cr, 0, 0, allocation.width, allocation.height);
    cairo_fill(cr);
    @@ -401,12 +138,120 @@
    {
    PidginWhiteboard *gtkwb = _gtkwb;
    - cairo_set_source_surface(cr, gtkwb->priv->surface, 0, 0);
    + cairo_set_source_surface(cr, gtkwb->surface, 0, 0);
    cairo_paint(cr);
    return FALSE;
    }
    +static void
    +pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb)
    +{
    + GdkPixbuf *pixbuf;
    +
    + /* Makes an icon from the whiteboard's canvas 'image' */
    + pixbuf = gdk_pixbuf_get_from_surface(gtkwb->surface, 0, 0, gtkwb->width,
    + gtkwb->height);
    + gtk_window_set_icon(GTK_WINDOW(gtkwb), pixbuf);
    + g_object_unref(pixbuf);
    +}
    +
    +static void
    +pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y,
    + int color, int size)
    +{
    + PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
    + GtkWidget *widget = gtkwb->drawing_area;
    + cairo_t *gfx_con = gtkwb->cr;
    + GdkRGBA rgba;
    +
    + /* Interpret and convert color */
    + pidgin_whiteboard_rgb24_to_rgba(color, &rgba);
    + gdk_cairo_set_source_rgba(gfx_con, &rgba);
    +
    + /* Draw a circle */
    + cairo_arc(gfx_con, x, y, size / 2.0, 0.0, 2.0 * M_PI);
    + cairo_fill(gfx_con);
    +
    + gtk_widget_queue_draw_area(widget, x - size / 2, y - size / 2, size,
    + size);
    +}
    +/* Uses Bresenham's algorithm (as provided by Wikipedia) */
    +static void
    +pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0, int x1,
    + int y1, int color, int size)
    +{
    + int temp;
    +
    + int xstep;
    + int ystep;
    +
    + int dx;
    + int dy;
    +
    + int error;
    + int derror;
    +
    + int x;
    + int y;
    +
    + gboolean steep = abs(y1 - y0) > abs(x1 - x0);
    +
    + if (steep) {
    + temp = x0;
    + x0 = y0;
    + y0 = temp;
    + temp = x1;
    + x1 = y1;
    + y1 = temp;
    + }
    +
    + dx = abs(x1 - x0);
    + dy = abs(y1 - y0);
    +
    + error = 0;
    + derror = dy;
    +
    + x = x0;
    + y = y0;
    +
    + if (x0 < x1) {
    + xstep = 1;
    + } else {
    + xstep = -1;
    + }
    +
    + if (y0 < y1) {
    + ystep = 1;
    + } else {
    + ystep = -1;
    + }
    +
    + if (steep) {
    + pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
    + } else {
    + pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
    + }
    +
    + while (x != x1) {
    + x += xstep;
    + error += derror;
    +
    + if ((error * 2) >= dx) {
    + y += ystep;
    + error -= dx;
    + }
    +
    + if (steep) {
    + pidgin_whiteboard_draw_brush_point(wb, y, x, color,
    + size);
    + } else {
    + pidgin_whiteboard_draw_brush_point(wb, x, y, color,
    + size);
    + }
    + }
    +}
    +
    static gboolean pidgin_whiteboard_brush_down(GtkWidget *widget, GdkEventButton *event, gpointer data)
    {
    PidginWhiteboard *gtkwb = (PidginWhiteboard*)data;
    @@ -414,17 +259,16 @@
    PurpleWhiteboard *wb = gtkwb->wb;
    GList *draw_list = purple_whiteboard_get_draw_list(wb);
    - if (brush_state != PIDGIN_WHITEBOARD_BRUSH_UP) {
    + if (gtkwb->brush_state != PIDGIN_WHITEBOARD_BRUSH_UP) {
    /* Potential double-click DOWN to DOWN? */
    - brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
    + gtkwb->brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
    /* return FALSE; */
    }
    - brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
    + gtkwb->brush_state = PIDGIN_WHITEBOARD_BRUSH_DOWN;
    - if(event->button == GDK_BUTTON_PRIMARY && gtkwb->priv->cr != NULL)
    - {
    + if (event->button == GDK_BUTTON_PRIMARY && gtkwb->cr != NULL) {
    /* Check if draw_list has contents; if so, clear it */
    if(draw_list)
    {
    @@ -433,13 +277,15 @@
    }
    /* Set tracking variables */
    - LastX = event->x;
    - LastY = event->y;
    + gtkwb->last_x = event->x;
    + gtkwb->last_y = event->y;
    +
    + gtkwb->motion_count = 0;
    - MotionCount = 0;
    -
    - draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastX));
    - draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastY));
    + draw_list = g_list_append(draw_list,
    + GINT_TO_POINTER(gtkwb->last_x));
    + draw_list = g_list_append(draw_list,
    + GINT_TO_POINTER(gtkwb->last_y));
    pidgin_whiteboard_draw_brush_point(gtkwb->wb,
    event->x, event->y,
    @@ -475,31 +321,29 @@
    state = event->state;
    }
    - if(state & GDK_BUTTON1_MASK && gtkwb->priv->cr != NULL)
    - {
    - if ((brush_state != PIDGIN_WHITEBOARD_BRUSH_DOWN) &&
    - (brush_state != PIDGIN_WHITEBOARD_BRUSH_MOTION))
    - {
    - purple_debug_error("gtkwhiteboard",
    - "***Bad brush state transition %d to MOTION\n",
    - brush_state);
    + if (state & GDK_BUTTON1_MASK && gtkwb->cr != NULL) {
    + if ((gtkwb->brush_state != PIDGIN_WHITEBOARD_BRUSH_DOWN) &&
    + (gtkwb->brush_state != PIDGIN_WHITEBOARD_BRUSH_MOTION)) {
    + purple_debug_error(
    + "gtkwhiteboard",
    + "***Bad brush state transition %d to MOTION\n",
    + gtkwb->brush_state);
    - brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
    + gtkwb->brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
    return FALSE;
    }
    - brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
    + gtkwb->brush_state = PIDGIN_WHITEBOARD_BRUSH_MOTION;
    - dx = x - LastX;
    - dy = y - LastY;
    + dx = x - gtkwb->last_x;
    + dy = y - gtkwb->last_y;
    - MotionCount++;
    + gtkwb->motion_count++;
    /* NOTE 100 is a temporary constant for how many deltas/motions in a
    * stroke (needs UI Ops?)
    */
    - if(MotionCount == 100)
    - {
    + if (gtkwb->motion_count == 100) {
    draw_list = g_list_append(draw_list, GINT_TO_POINTER(dx));
    draw_list = g_list_append(draw_list, GINT_TO_POINTER(dy));
    @@ -514,26 +358,27 @@
    }
    /* Reset motion tracking */
    - MotionCount = 0;
    + gtkwb->motion_count = 0;
    - draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastX));
    - draw_list = g_list_append(draw_list, GINT_TO_POINTER(LastY));
    + draw_list = g_list_append(
    + draw_list, GINT_TO_POINTER(gtkwb->last_x));
    + draw_list = g_list_append(
    + draw_list, GINT_TO_POINTER(gtkwb->last_y));
    - dx = x - LastX;
    - dy = y - LastY;
    + dx = x - gtkwb->last_x;
    + dy = y - gtkwb->last_y;
    }
    draw_list = g_list_append(draw_list, GINT_TO_POINTER(dx));
    draw_list = g_list_append(draw_list, GINT_TO_POINTER(dy));
    - pidgin_whiteboard_draw_brush_line(gtkwb->wb,
    - LastX, LastY,
    - x, y,
    - gtkwb->brush_color, gtkwb->brush_size);
    + pidgin_whiteboard_draw_brush_line(
    + gtkwb->wb, gtkwb->last_x, gtkwb->last_y, x, y,
    + gtkwb->brush_color, gtkwb->brush_size);
    /* Set tracking variables */
    - LastX = x;
    - LastY = y;
    + gtkwb->last_x = x;
    + gtkwb->last_y = y;
    }
    purple_whiteboard_set_draw_list(wb, draw_list);
    @@ -548,50 +393,43 @@
    PurpleWhiteboard *wb = gtkwb->wb;
    GList *draw_list = purple_whiteboard_get_draw_list(wb);
    - if ((brush_state != PIDGIN_WHITEBOARD_BRUSH_DOWN) &&
    - (brush_state != PIDGIN_WHITEBOARD_BRUSH_MOTION))
    - {
    + if ((gtkwb->brush_state != PIDGIN_WHITEBOARD_BRUSH_DOWN) &&
    + (gtkwb->brush_state != PIDGIN_WHITEBOARD_BRUSH_MOTION)) {
    purple_debug_error("gtkwhiteboard",
    - "***Bad brush state transition %d to UP\n",
    - brush_state);
    + "***Bad brush state transition %d to UP\n",
    + gtkwb->brush_state);
    - brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
    + gtkwb->brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
    return FALSE;
    }
    - brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
    + gtkwb->brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
    - if(event->button == GDK_BUTTON_PRIMARY && gtkwb->priv->cr != NULL)
    - {
    + if (event->button == GDK_BUTTON_PRIMARY && gtkwb->cr != NULL) {
    /* If the brush was never moved, express two sets of two deltas That's a
    * 'point,' but not for Yahoo!
    */
    - /* if((event->x == LastX) && (event->y == LastY)) */
    - if(MotionCount == 0)
    - {
    + if (gtkwb->motion_count == 0) {
    int index;
    /* For Yahoo!, a (0 0) indicates the end of drawing */
    /* FIXME: Yahoo Doodle specific! */
    - for(index = 0; index < 2; index++)
    - {
    + for (index = 0; index < 2; index++) {
    draw_list = g_list_append(draw_list, 0);
    draw_list = g_list_append(draw_list, 0);
    }
    }
    - /*
    - else
    - MotionCount = 0;
    - */
    /* Send draw list to protocol draw_list handler */
    purple_whiteboard_send_draw_list(gtkwb->wb, draw_list);
    pidgin_whiteboard_set_canvas_as_icon(gtkwb);
    - /* The brush stroke is finished, clear the list for another one */
    - if(draw_list)
    + /* The brush stroke is finished, clear the list for another one
    + */
    + if (draw_list) {
    purple_whiteboard_draw_list_destroy(draw_list);
    + }
    purple_whiteboard_set_draw_list(wb, NULL);
    }
    @@ -599,96 +437,6 @@
    return TRUE;
    }
    -static void pidgin_whiteboard_draw_brush_point(PurpleWhiteboard *wb, int x, int y, int color, int size)
    -{
    - PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
    - GtkWidget *widget = gtkwb->drawing_area;
    - cairo_t *gfx_con = gtkwb->priv->cr;
    - GdkRGBA rgba;
    -
    - /* Interpret and convert color */
    - pidgin_whiteboard_rgb24_to_rgba(color, &rgba);
    - gdk_cairo_set_source_rgba(gfx_con, &rgba);
    -
    - /* Draw a circle */
    - cairo_arc(gfx_con,
    - x, y,
    - size / 2.0,
    - 0.0, 2.0 * M_PI);
    - cairo_fill(gfx_con);
    -
    - gtk_widget_queue_draw_area(widget,
    - x - size / 2, y - size / 2,
    - size, size);
    -}
    -
    -/* Uses Bresenham's algorithm (as provided by Wikipedia) */
    -static void pidgin_whiteboard_draw_brush_line(PurpleWhiteboard *wb, int x0, int y0, int x1, int y1, int color, int size)
    -{
    - int temp;
    -
    - int xstep;
    - int ystep;
    -
    - int dx;
    - int dy;
    -
    - int error;
    - int derror;
    -
    - int x;
    - int y;
    -
    - gboolean steep = abs(y1 - y0) > abs(x1 - x0);
    -
    - if(steep)
    - {
    - temp = x0; x0 = y0; y0 = temp;
    - temp = x1; x1 = y1; y1 = temp;
    - }
    -
    - dx = abs(x1 - x0);
    - dy = abs(y1 - y0);
    -
    - error = 0;
    - derror = dy;
    -
    - x = x0;
    - y = y0;
    -
    - if(x0 < x1)
    - xstep = 1;
    - else
    - xstep = -1;
    -
    - if(y0 < y1)
    - ystep = 1;
    - else
    - ystep = -1;
    -
    - if(steep)
    - pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
    - else
    - pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
    -
    - while(x != x1)
    - {
    - x += xstep;
    - error += derror;
    -
    - if((error * 2) >= dx)
    - {
    - y += ystep;
    - error -= dx;
    - }
    -
    - if(steep)
    - pidgin_whiteboard_draw_brush_point(wb, y, x, color, size);
    - else
    - pidgin_whiteboard_draw_brush_point(wb, x, y, color, size);
    - }
    -}
    -
    static void pidgin_whiteboard_set_dimensions(PurpleWhiteboard *wb, int width, int height)
    {
    PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
    @@ -709,7 +457,7 @@
    {
    PidginWhiteboard *gtkwb = purple_whiteboard_get_ui_data(wb);
    GtkWidget *drawing_area = gtkwb->drawing_area;
    - cairo_t *cr = gtkwb->priv->cr;
    + cairo_t *cr = gtkwb->cr;
    GtkAllocation allocation;
    GdkRGBA white = {1.0, 1.0, 1.0, 1.0};
    @@ -728,11 +476,10 @@
    PidginWhiteboard *gtkwb = (PidginWhiteboard*)(data);
    /* Confirm whether the user really wants to clear */
    - GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(gtkwb->window),
    - GTK_DIALOG_DESTROY_WITH_PARENT,
    - GTK_MESSAGE_QUESTION,
    - GTK_BUTTONS_YES_NO,
    - _("Do you really want to clear?"));
    + GtkWidget *dialog = gtk_message_dialog_new(
    + GTK_WINDOW(gtkwb), GTK_DIALOG_DESTROY_WITH_PARENT,
    + GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s",
    + _("Do you really want to clear?"));
    gint response = gtk_dialog_run(GTK_DIALOG(dialog));
    gtk_widget_destroy(dialog);
    @@ -752,31 +499,26 @@
    {
    PidginWhiteboard *gtkwb = _gtkwb;
    GdkPixbuf *pixbuf;
    - GtkWidget *dialog;
    + GtkFileChooserNative *chooser;
    int result;
    - dialog = gtk_file_chooser_dialog_new(_("Save File"),
    - GTK_WINDOW(gtkwb->window), GTK_FILE_CHOOSER_ACTION_SAVE,
    - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE,
    - GTK_RESPONSE_ACCEPT, NULL);
    + chooser = gtk_file_chooser_native_new(_("Save File"), GTK_WINDOW(gtkwb),
    + GTK_FILE_CHOOSER_ACTION_SAVE,
    + _("_Save"), _("_Cancel"));
    - gtk_file_chooser_set_do_overwrite_confirmation(
    - GTK_FILE_CHOOSER(dialog), TRUE);
    + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(chooser),
    + TRUE);
    + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(chooser),
    + "whiteboard.png");
    - gtk_file_chooser_set_current_name(
    - GTK_FILE_CHOOSER(dialog), "whiteboard.png");
    -
    - result = gtk_dialog_run(GTK_DIALOG(dialog));
    -
    + result = gtk_native_dialog_run(GTK_NATIVE_DIALOG(chooser));
    if (result == GTK_RESPONSE_ACCEPT) {
    gboolean success;
    gchar *filename =
    - gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
    + gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
    - gtk_widget_destroy(dialog);
    -
    - pixbuf = gdk_pixbuf_get_from_surface(gtkwb->priv->surface, 0, 0,
    - gtkwb->width, gtkwb->height);
    + pixbuf = gdk_pixbuf_get_from_surface(
    + gtkwb->surface, 0, 0, gtkwb->width, gtkwb->height);
    success = gdk_pixbuf_save(pixbuf, filename, "png", NULL,
    "compression", "9", NULL);
    @@ -791,26 +533,9 @@
    "couldn't be saved to \"%s\"", filename);
    }
    g_free(filename);
    - } else if (result == GTK_RESPONSE_CANCEL)
    - gtk_widget_destroy(dialog);
    -}
    -
    -static void pidgin_whiteboard_set_canvas_as_icon(PidginWhiteboard *gtkwb)
    -{
    - GdkPixbuf *pixbuf;
    + }
    - /* Makes an icon from the whiteboard's canvas 'image' */
    - pixbuf = gdk_pixbuf_get_from_surface(gtkwb->priv->surface,
    - 0, 0, gtkwb->width, gtkwb->height);
    - gtk_window_set_icon(GTK_WINDOW(gtkwb->window), pixbuf);
    - g_object_unref(pixbuf);
    -}
    -
    -static void pidgin_whiteboard_rgb24_to_rgba(int color_rgb, GdkRGBA *color)
    -{
    - color->red = ((color_rgb >> 16) & 0xFF) / 255.0f;
    - color->green = ((color_rgb >> 8) & 0xFF) / 255.0f;
    - color->blue = (color_rgb & 0xFF) / 255.0f;
    + g_object_unref(chooser);
    }
    static void
    @@ -831,3 +556,160 @@
    purple_whiteboard_send_brush(wb, old_size, new_color);
    }
    +static void
    +pidgin_whiteboard_create(PurpleWhiteboard *wb)
    +{
    + PidginWhiteboard *gtkwb;
    + PurpleBuddy *buddy;
    + GdkRGBA color;
    +
    + gtkwb = PIDGIN_WHITEBOARD(g_object_new(PIDGIN_TYPE_WHITEBOARD, NULL));
    + gtkwb->wb = wb;
    + purple_whiteboard_set_ui_data(wb, gtkwb);
    +
    + /* Get dimensions (default?) for the whiteboard canvas */
    + if (!purple_whiteboard_get_dimensions(wb, &gtkwb->width,
    + &gtkwb->height)) {
    + /* Give some initial board-size */
    + gtkwb->width = 300;
    + gtkwb->height = 250;
    + }
    +
    + if (!purple_whiteboard_get_brush(wb, &gtkwb->brush_size,
    + &gtkwb->brush_color)) {
    + /* Give some initial brush-info */
    + gtkwb->brush_size = 2;
    + gtkwb->brush_color = 0xff0000;
    + }
    +
    + /* Try and set window title as the name of the buddy, else just use
    + * their username
    + */
    + buddy = purple_blist_find_buddy(purple_whiteboard_get_account(wb),
    + purple_whiteboard_get_who(wb));
    +
    + gtk_window_set_title(GTK_WINDOW(gtkwb),
    + buddy != NULL
    + ? purple_buddy_get_contact_alias(buddy)
    + : purple_whiteboard_get_who(wb));
    + gtk_widget_set_name(GTK_WIDGET(gtkwb), purple_whiteboard_get_who(wb));
    +
    + gtk_widget_set_size_request(GTK_WIDGET(gtkwb->drawing_area),
    + gtkwb->width, gtkwb->height);
    +
    + pidgin_whiteboard_rgb24_to_rgba(gtkwb->brush_color, &color);
    + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(gtkwb->color_button),
    + &color);
    +
    + /* Make all this (window) visible */
    + gtk_widget_show(GTK_WIDGET(gtkwb));
    +
    + pidgin_whiteboard_set_canvas_as_icon(gtkwb);
    +
    + /* TODO Specific protocol/whiteboard assignment here? Needs a UI Op? */
    + /* Set default brush size and color */
    + /*
    + ds->brush_size = DOODLE_BRUSH_MEDIUM;
    + ds->brush_color = 0;
    + */
    +}
    +
    +static void
    +pidgin_whiteboard_destroy(PurpleWhiteboard *wb)
    +{
    + PidginWhiteboard *gtkwb;
    +
    + g_return_if_fail(wb != NULL);
    + gtkwb = purple_whiteboard_get_ui_data(wb);
    + g_return_if_fail(gtkwb != NULL);
    +
    + /* TODO Ask if user wants to save picture before the session is closed
    + */
    +
    + gtkwb->wb = NULL;
    + g_object_unref(gtkwb);
    + purple_whiteboard_set_ui_data(wb, NULL);
    +}
    +
    +/******************************************************************************
    + * GObject implementation
    + *****************************************************************************/
    +static void
    +pidgin_whiteboard_init(PidginWhiteboard *self)
    +{
    + gtk_widget_init_template(GTK_WIDGET(self));
    +
    + self->brush_state = PIDGIN_WHITEBOARD_BRUSH_UP;
    +}
    +
    +static void
    +pidgin_whiteboard_finalize(GObject *obj)
    +{
    + PidginWhiteboard *gtkwb = PIDGIN_WHITEBOARD(obj);
    +
    + /* Clear graphical memory */
    + g_clear_pointer(&gtkwb->cr, cairo_destroy);
    + g_clear_pointer(&gtkwb->surface, cairo_surface_destroy);
    +
    + G_OBJECT_CLASS(pidgin_whiteboard_parent_class)->finalize(obj);
    +}
    +
    +static void
    +pidgin_whiteboard_class_init(PidginWhiteboardClass *klass)
    +{
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    +
    + obj_class->finalize = pidgin_whiteboard_finalize;
    +
    + gtk_widget_class_set_template_from_resource(
    + widget_class, "/im/pidgin/Pidgin/Whiteboard/whiteboard.ui");
    +
    + gtk_widget_class_bind_template_child(widget_class, PidginWhiteboard,
    + drawing_area);
    + gtk_widget_class_bind_template_child(widget_class, PidginWhiteboard,
    + color_button);
    +
    + gtk_widget_class_bind_template_callback(
    + widget_class, whiteboard_close_cb);
    + gtk_widget_class_bind_template_callback(
    + widget_class, pidgin_whiteboard_draw_event);
    + gtk_widget_class_bind_template_callback(
    + widget_class, pidgin_whiteboard_configure_event);
    + gtk_widget_class_bind_template_callback(
    + widget_class, pidgin_whiteboard_brush_down);
    + gtk_widget_class_bind_template_callback(
    + widget_class, pidgin_whiteboard_brush_motion);
    + gtk_widget_class_bind_template_callback(
    + widget_class, pidgin_whiteboard_brush_up);
    + gtk_widget_class_bind_template_callback(
    + widget_class, pidgin_whiteboard_button_clear_press);
    + gtk_widget_class_bind_template_callback(
    + widget_class, pidgin_whiteboard_button_save_press);
    + gtk_widget_class_bind_template_callback(
    + widget_class, color_selected);
    +}
    +
    +/******************************************************************************
    + * API
    + *****************************************************************************/
    +static PurpleWhiteboardUiOps ui_ops =
    +{
    + pidgin_whiteboard_create,
    + pidgin_whiteboard_destroy,
    + pidgin_whiteboard_set_dimensions,
    + pidgin_whiteboard_set_brush,
    + pidgin_whiteboard_draw_brush_point,
    + pidgin_whiteboard_draw_brush_line,
    + pidgin_whiteboard_clear,
    + NULL,
    + NULL,
    + NULL,
    + NULL
    +};
    +
    +PurpleWhiteboardUiOps *
    +pidgin_whiteboard_get_ui_ops(void)
    +{
    + return &ui_ops;
    +}
    --- a/pidgin/gtkxfer.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkxfer.c Tue Oct 08 21:48:28 2019 -0500
    @@ -43,23 +43,20 @@
    struct _PidginXferDialog
    {
    - gint box_count;
    + GtkDialog parent;
    - gboolean keep_open;
    - gboolean auto_clear;
    + GtkWidget *keep_open;
    + GtkWidget *auto_clear;
    gint num_transfers;
    PurpleXfer *selected_xfer;
    - GtkWidget *window;
    GtkWidget *tree;
    GtkListStore *model;
    GtkWidget *expander;
    - GtkWidget *grid;
    -
    GtkWidget *local_user_desc_label;
    GtkWidget *local_user_label;
    GtkWidget *remote_user_desc_label;
    @@ -81,6 +78,8 @@
    GtkWidget *close_button;
    };
    +G_DEFINE_TYPE(PidginXferDialog, pidgin_xfer_dialog, GTK_TYPE_DIALOG);
    +
    typedef struct
    {
    GtkTreeIter iter;
    @@ -100,11 +99,10 @@
    COLUMN_FILENAME,
    COLUMN_SIZE,
    COLUMN_REMAINING,
    - COLUMN_DATA,
    + COLUMN_XFER,
    NUM_COLUMNS
    };
    -
    /**************************************************************************
    * Utility Functions
    **************************************************************************/
    @@ -114,23 +112,24 @@
    {
    double kb_sent, kb_rem;
    double kbps = 0.0;
    - time_t elapsed, now;
    -
    - now = purple_xfer_get_end_time(xfer);
    - if (now == 0)
    - now = time(NULL);
    + gint64 now;
    + gint64 elapsed = 0;
    - kb_sent = purple_xfer_get_bytes_sent(xfer) / 1024.0;
    - kb_rem = purple_xfer_get_bytes_remaining(xfer) / 1024.0;
    elapsed = purple_xfer_get_start_time(xfer);
    - if (elapsed > 0)
    + if (elapsed > 0) {
    + now = purple_xfer_get_end_time(xfer);
    + if (now == 0) {
    + now = g_get_monotonic_time();
    + }
    elapsed = now - elapsed;
    - else
    - elapsed = 0;
    - kbps = (elapsed > 0 ? (kb_sent / elapsed) : 0);
    + }
    +
    + kb_sent = purple_xfer_get_bytes_sent(xfer) / 1000.0;
    + kb_rem = purple_xfer_get_bytes_remaining(xfer) / 1000.0;
    + kbps = (elapsed > 0 ? (kb_sent * G_USEC_PER_SEC) / elapsed : 0);
    if (kbsec != NULL) {
    - *kbsec = g_strdup_printf(_("%.2f KiB/s"), kbps);
    + *kbsec = g_strdup_printf(_("%.2f KB/s"), kbps);
    }
    if (time_elapsed != NULL)
    @@ -138,18 +137,15 @@
    int h, m, s;
    int secs_elapsed;
    - if (purple_xfer_get_start_time(xfer) > 0)
    - {
    - secs_elapsed = now - purple_xfer_get_start_time(xfer);
    + if (purple_xfer_get_start_time(xfer) > 0) {
    + secs_elapsed = elapsed / G_USEC_PER_SEC;
    h = secs_elapsed / 3600;
    m = (secs_elapsed % 3600) / 60;
    s = secs_elapsed % 60;
    *time_elapsed = g_strdup_printf("%d:%02d:%02d", h, m, s);
    - }
    - else
    - {
    + } else {
    *time_elapsed = g_strdup(_("Not started"));
    }
    }
    @@ -191,9 +187,6 @@
    guint64 total_bytes_xferred = 0;
    guint64 total_file_size = 0;
    - if (dialog->window == NULL)
    - return;
    -
    valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter);
    /* Find all active transfers */
    @@ -202,10 +195,10 @@
    PurpleXfer *xfer = NULL;
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model),
    - &iter, COLUMN_DATA, &val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    + COLUMN_XFER, &val);
    + xfer = g_value_get_object(&val);
    - xfer = g_value_get_pointer(&val);
    if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_STARTED) {
    num_active_xfers++;
    total_bytes_xferred += purple_xfer_get_bytes_sent(xfer);
    @@ -229,10 +222,10 @@
    "File Transfers - %d%% of %d files",
    num_active_xfers),
    total_pct, num_active_xfers);
    - gtk_window_set_title(GTK_WINDOW(dialog->window), title);
    + gtk_window_set_title(GTK_WINDOW(dialog), title);
    g_free(title);
    } else {
    - gtk_window_set_title(GTK_WINDOW(dialog->window), _("File Transfers"));
    + gtk_window_set_title(GTK_WINDOW(dialog), _("File Transfers"));
    }
    }
    @@ -398,19 +391,19 @@
    }
    static void
    -toggle_keep_open_cb(GtkWidget *w, PidginXferDialog *dialog)
    +toggle_keep_open_cb(GtkWidget *w, G_GNUC_UNUSED gpointer data)
    {
    - dialog->keep_open = !dialog->keep_open;
    - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open",
    - dialog->keep_open);
    + purple_prefs_set_bool(
    + PIDGIN_PREFS_ROOT "/filetransfer/keep_open",
    + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));
    }
    static void
    -toggle_clear_finished_cb(GtkWidget *w, PidginXferDialog *dialog)
    +toggle_clear_finished_cb(GtkWidget *w, G_GNUC_UNUSED gpointer data)
    {
    - dialog->auto_clear = !dialog->auto_clear;
    - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished",
    - dialog->auto_clear);
    + purple_prefs_set_bool(
    + PIDGIN_PREFS_ROOT "/filetransfer/clear_finished",
    + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)));
    }
    static void
    @@ -425,10 +418,9 @@
    gtk_widget_set_sensitive(dialog->expander, TRUE);
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model),
    - &iter, COLUMN_DATA, &val);
    -
    - xfer = g_value_get_pointer(&val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    + COLUMN_XFER, &val);
    + xfer = g_value_get_object(&val);
    update_detailed_info(dialog, xfer);
    @@ -551,266 +543,104 @@
    /**************************************************************************
    * Dialog Building Functions
    **************************************************************************/
    -static GtkWidget *
    -setup_tree(PidginXferDialog *dialog)
    -{
    - GtkWidget *tree;
    - GtkListStore *model;
    - GtkCellRenderer *renderer;
    - GtkTreeViewColumn *column;
    - GtkTreeSelection *selection;
    -
    - /* Build the tree model */
    - /* Transfer type, Progress Bar, Filename, Size, Remaining */
    - model = gtk_list_store_new(NUM_COLUMNS, G_TYPE_STRING, G_TYPE_INT,
    - G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
    - G_TYPE_POINTER);
    - dialog->model = model;
    -
    - /* Create the treeview */
    - dialog->tree = tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
    - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
    - /* gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); */
    -
    - gtk_widget_show(tree);
    -
    - g_signal_connect(G_OBJECT(selection), "changed",
    - G_CALLBACK(selection_changed_cb), dialog);
    -
    - g_object_unref(G_OBJECT(model));
    -
    -
    - /* Columns */
    -
    - /* Transfer Type column */
    - renderer = gtk_cell_renderer_pixbuf_new();
    - column = gtk_tree_view_column_new_with_attributes(NULL, renderer,
    - "icon-name", COLUMN_STATUS, NULL);
    - gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
    - GTK_TREE_VIEW_COLUMN_FIXED);
    - gtk_tree_view_column_set_fixed_width(GTK_TREE_VIEW_COLUMN(column), 25);
    - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
    -
    - /* Progress bar column */
    - renderer = gtk_cell_renderer_progress_new();
    - column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer,
    - "value", COLUMN_PROGRESS, NULL);
    - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
    -
    - /* Filename column */
    - renderer = gtk_cell_renderer_text_new();
    - column = gtk_tree_view_column_new_with_attributes(_("Filename"), renderer,
    - "text", COLUMN_FILENAME, NULL);
    - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
    -
    - /* File Size column */
    - renderer = gtk_cell_renderer_text_new();
    - column = gtk_tree_view_column_new_with_attributes(_("Size"), renderer,
    - "text", COLUMN_SIZE, NULL);
    - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
    -
    - /* Bytes Remaining column */
    - renderer = gtk_cell_renderer_text_new();
    - column = gtk_tree_view_column_new_with_attributes(_("Remaining"),
    - renderer, "text", COLUMN_REMAINING, NULL);
    - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
    -
    - gtk_tree_view_columns_autosize(GTK_TREE_VIEW(tree));
    -
    - gtk_widget_show(tree);
    -
    - return tree;
    -}
    -
    -static GtkWidget *
    -make_info_grid(PidginXferDialog *dialog)
    -{
    - GtkWidget *grid;
    - GtkWidget *label;
    - gsize i;
    -
    - struct
    - {
    - GtkWidget **desc_label;
    - GtkWidget **val_label;
    - const char *desc;
    -
    - } labels[] =
    - {
    - { &dialog->local_user_desc_label, &dialog->local_user_label, NULL },
    - { &dialog->remote_user_desc_label, &dialog->remote_user_label, NULL },
    - { &label, &dialog->protocol_label, _("Protocol:") },
    - { &label, &dialog->filename_label, _("Filename:") },
    - { &label, &dialog->localfile_label, _("Local File:") },
    - { &label, &dialog->status_label, _("Status:") },
    - { &label, &dialog->speed_label, _("Speed:") },
    - { &label, &dialog->time_elapsed_label, _("Time Elapsed:") },
    - { &label, &dialog->time_remaining_label, _("Time Remaining:") }
    - };
    -
    - /* Setup the initial grid */
    - dialog->grid = grid = gtk_grid_new();
    - gtk_grid_set_row_spacing(GTK_GRID(grid), PIDGIN_HIG_BOX_SPACE);
    - gtk_grid_set_column_spacing(GTK_GRID(grid), PIDGIN_HIG_BOX_SPACE);
    -
    - /* Setup the labels */
    - for (i = 0; i < G_N_ELEMENTS(labels); i++) {
    - GtkWidget *label;
    - char buf[256];
    -
    - g_snprintf(buf, sizeof(buf), "<b>%s</b>",
    - labels[i].desc != NULL ? labels[i].desc : "");
    -
    - *labels[i].desc_label = label = gtk_label_new(NULL);
    - gtk_label_set_markup(GTK_LABEL(label), buf);
    - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_RIGHT);
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_grid_attach(GTK_GRID(grid), label, 0, i, 1, 1);
    - gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
    -
    - gtk_widget_show(label);
    -
    - *labels[i].val_label = label = gtk_label_new(NULL);
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_grid_attach(GTK_GRID(grid), label, 1, i, 1, 1);
    - gtk_widget_set_hexpand(label, TRUE);
    - gtk_widget_set_valign(label, GTK_ALIGN_CENTER);
    -
    - gtk_widget_show(label);
    - }
    -
    - /* Setup the progress bar */
    - dialog->progress = gtk_progress_bar_new();
    - gtk_grid_attach(GTK_GRID(grid), dialog->progress,
    - 0, G_N_ELEMENTS(labels), 2, 1);
    -
    - gtk_widget_show(dialog->progress);
    -
    - return grid;
    -}
    -
    PidginXferDialog *
    pidgin_xfer_dialog_new(void)
    {
    - PidginXferDialog *dialog;
    - GtkWidget *window;
    - GtkWidget *vbox;
    - GtkWidget *expander;
    -#if !GTK_CHECK_VERSION(3,14,0)
    - GtkWidget *alignment;
    -#endif
    - GtkWidget *grid;
    - GtkWidget *checkbox;
    - GtkWidget *bbox;
    + return PIDGIN_XFER_DIALOG(g_object_new(PIDGIN_TYPE_XFER_DIALOG, NULL));
    +}
    +
    +static void
    +pidgin_xfer_dialog_class_init(PidginXferDialogClass *klass)
    +{
    + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    +
    + gtk_widget_class_set_template_from_resource(
    + widget_class, "/im/pidgin/Pidgin/Xfer/xfer.ui");
    +
    + gtk_widget_class_bind_template_callback(widget_class, delete_win_cb);
    +
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + tree);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + model);
    + gtk_widget_class_bind_template_callback(widget_class,
    + selection_changed_cb);
    +
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + keep_open);
    + gtk_widget_class_bind_template_callback(widget_class,
    + toggle_keep_open_cb);
    +
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + auto_clear);
    + gtk_widget_class_bind_template_callback(widget_class,
    + toggle_clear_finished_cb);
    +
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + expander);
    - dialog = g_new0(PidginXferDialog, 1);
    - dialog->keep_open =
    - purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/keep_open");
    - dialog->auto_clear =
    - purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/filetransfer/clear_finished");
    -
    - /* Create the window. */
    - dialog->window = window = pidgin_create_dialog(_("File Transfers"), 0, "file transfer", TRUE);
    - gtk_window_set_default_size(GTK_WINDOW(window), 450, 250);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + local_user_desc_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + local_user_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + remote_user_desc_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + remote_user_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + protocol_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + filename_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + localfile_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + status_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + speed_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + time_elapsed_label);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + time_remaining_label);
    - g_signal_connect(G_OBJECT(window), "delete_event",
    - G_CALLBACK(delete_win_cb), dialog);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + progress);
    - /* Create the main vbox for top half of the window. */
    - vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + open_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + remove_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + stop_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginXferDialog,
    + close_button);
    - /* Setup the listbox */
    - gtk_box_pack_start(GTK_BOX(vbox),
    - pidgin_make_scrollable(setup_tree(dialog), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, 140),
    - TRUE, TRUE, 0);
    + gtk_widget_class_bind_template_callback(widget_class, open_button_cb);
    + gtk_widget_class_bind_template_callback(widget_class, remove_button_cb);
    + gtk_widget_class_bind_template_callback(widget_class, stop_button_cb);
    + gtk_widget_class_bind_template_callback(widget_class, close_button_cb);
    +}
    +
    +static void
    +pidgin_xfer_dialog_init(PidginXferDialog *dialog)
    +{
    + gtk_widget_init_template(GTK_WIDGET(dialog));
    /* "Close this window when all transfers finish" */
    - checkbox = gtk_check_button_new_with_mnemonic(
    - _("Close this window when all transfers _finish"));
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
    - !dialog->keep_open);
    - g_signal_connect(G_OBJECT(checkbox), "toggled",
    - G_CALLBACK(toggle_keep_open_cb), dialog);
    - gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
    - gtk_widget_show(checkbox);
    + gtk_toggle_button_set_active(
    + GTK_TOGGLE_BUTTON(dialog->keep_open),
    + !purple_prefs_get_bool(PIDGIN_PREFS_ROOT
    + "/filetransfer/keep_open"));
    /* "Clear finished transfers" */
    - checkbox = gtk_check_button_new_with_mnemonic(
    - _("C_lear finished transfers"));
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
    - dialog->auto_clear);
    - g_signal_connect(G_OBJECT(checkbox), "toggled",
    - G_CALLBACK(toggle_clear_finished_cb), dialog);
    - gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
    - gtk_widget_show(checkbox);
    -
    - /* "Download Details" arrow */
    - expander = gtk_expander_new_with_mnemonic(_("File transfer _details"));
    - dialog->expander = expander;
    - gtk_box_pack_start(GTK_BOX(vbox), expander, FALSE, FALSE, 0);
    - gtk_widget_show(expander);
    -
    - gtk_widget_set_sensitive(expander, FALSE);
    -
    -#if GTK_CHECK_VERSION(3,14,0)
    - /* The grid of information. */
    - grid = make_info_grid(dialog);
    - gtk_container_add(GTK_CONTAINER(expander), grid);
    - gtk_widget_show(grid);
    -
    - /* Small indent make grid fall under GtkExpander's label */
    - gtk_widget_set_margin_start(grid, 20);
    -#else
    - /* Small indent make grid fall under GtkExpander's label */
    - alignment = gtk_alignment_new(1, 0, 1, 1);
    - gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 20, 0);
    - gtk_container_add(GTK_CONTAINER(expander), alignment);
    - gtk_widget_show(alignment);
    -
    - /* The grid of information. */
    - grid = make_info_grid(dialog);
    - gtk_container_add(GTK_CONTAINER(alignment), grid);
    - gtk_widget_show(grid);
    -#endif
    -
    - bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window));
    -
    -#define ADD_BUTTON(b, label, callback, callbackdata) do { \
    - GtkWidget *button = gtk_button_new_from_stock(label); \
    - gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0); \
    - g_signal_connect(G_OBJECT(button), "clicked", callback, callbackdata); \
    - gtk_widget_show(button); \
    - b = button; \
    - } while (0)
    -
    - /* Open button */
    - ADD_BUTTON(dialog->open_button, GTK_STOCK_OPEN, G_CALLBACK(open_button_cb), dialog);
    - gtk_widget_set_sensitive(dialog->open_button, FALSE);
    -
    - /* Remove button */
    - ADD_BUTTON(dialog->remove_button, GTK_STOCK_REMOVE, G_CALLBACK(remove_button_cb), dialog);
    - gtk_widget_hide(dialog->remove_button);
    -
    - /* Stop button */
    - ADD_BUTTON(dialog->stop_button, GTK_STOCK_STOP, G_CALLBACK(stop_button_cb), dialog);
    - gtk_widget_set_sensitive(dialog->stop_button, FALSE);
    -
    - /* Close button */
    - ADD_BUTTON(dialog->close_button, GTK_STOCK_CLOSE, G_CALLBACK(close_button_cb), dialog);
    -
    -#undef ADD_BUTTON
    + gtk_toggle_button_set_active(
    + GTK_TOGGLE_BUTTON(dialog->auto_clear),
    + purple_prefs_get_bool(PIDGIN_PREFS_ROOT
    + "/filetransfer/clear_finished"));
    #ifdef _WIN32
    - g_signal_connect(G_OBJECT(dialog->window), "show",
    - G_CALLBACK(winpidgin_ensure_onscreen), dialog->window);
    + g_signal_connect(G_OBJECT(dialog), "show",
    + G_CALLBACK(winpidgin_ensure_onscreen), NULL);
    #endif
    -
    - return dialog;
    }
    void
    @@ -820,9 +650,7 @@
    purple_notify_close_with_handle(dialog);
    - gtk_widget_destroy(dialog->window);
    -
    - g_free(dialog);
    + gtk_widget_destroy(GTK_WIDGET(dialog));
    }
    void
    @@ -838,9 +666,9 @@
    pidgin_set_xfer_dialog(tmp);
    }
    - gtk_widget_show(tmp->window);
    + gtk_widget_show(GTK_WIDGET(tmp));
    } else {
    - gtk_window_present(GTK_WINDOW(dialog->window));
    + gtk_window_present(GTK_WINDOW(dialog));
    }
    }
    @@ -851,7 +679,7 @@
    purple_notify_close_with_handle(dialog);
    - gtk_widget_hide(dialog->window);
    + gtk_widget_hide(GTK_WIDGET(dialog));
    }
    void
    @@ -877,8 +705,8 @@
    type = purple_xfer_get_xfer_type(xfer);
    - size_str = purple_str_size_to_units(purple_xfer_get_size(xfer));
    - remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer));
    + size_str = g_format_size(purple_xfer_get_size(xfer));
    + remaining_str = g_format_size(purple_xfer_get_bytes_remaining(xfer));
    icon_name = (type == PURPLE_XFER_TYPE_RECEIVE ? "go-down" : "go-up");
    @@ -887,16 +715,14 @@
    utf8 = g_filename_to_utf8(lfilename, -1, NULL, NULL, NULL);
    g_free(lfilename);
    lfilename = utf8;
    - gtk_list_store_set(dialog->model, &data->iter,
    - COLUMN_STATUS, icon_name,
    - COLUMN_PROGRESS, 0,
    - COLUMN_FILENAME, (type == PURPLE_XFER_TYPE_RECEIVE)
    - ? purple_xfer_get_filename(xfer)
    - : lfilename,
    - COLUMN_SIZE, size_str,
    - COLUMN_REMAINING, _("Waiting for transfer to begin"),
    - COLUMN_DATA, xfer,
    - -1);
    + gtk_list_store_set(dialog->model, &data->iter, COLUMN_STATUS, icon_name,
    + COLUMN_PROGRESS, 0, COLUMN_FILENAME,
    + (type == PURPLE_XFER_TYPE_RECEIVE)
    + ? purple_xfer_get_filename(xfer)
    + : lfilename,
    + COLUMN_SIZE, size_str, COLUMN_REMAINING,
    + _("Waiting for transfer to begin"), COLUMN_XFER,
    + xfer, -1);
    g_free(lfilename);
    gtk_tree_view_columns_autosize(GTK_TREE_VIEW(dialog->tree));
    @@ -957,7 +783,9 @@
    if (!data->in_list)
    return;
    - if ((purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) && (dialog->auto_clear)) {
    + if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL &&
    + gtk_toggle_button_get_active(
    + GTK_TOGGLE_BUTTON(dialog->auto_clear))) {
    pidgin_xfer_dialog_remove_xfer(dialog, xfer);
    return;
    }
    @@ -1008,8 +836,8 @@
    }
    data->last_updated_time = current_time;
    - size_str = purple_str_size_to_units(purple_xfer_get_size(xfer));
    - remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer));
    + size_str = g_format_size(purple_xfer_get_size(xfer));
    + remaining_str = g_format_size(purple_xfer_get_bytes_remaining(xfer));
    gtk_list_store_set(xfer_dialog->model, &data->iter,
    COLUMN_PROGRESS, (gint)(purple_xfer_get_progress(xfer) * 100),
    @@ -1032,17 +860,22 @@
    if (xfer == dialog->selected_xfer)
    update_detailed_info(xfer_dialog, xfer);
    - if (purple_xfer_is_completed(xfer) && dialog->auto_clear)
    + if (purple_xfer_is_completed(xfer) &&
    + gtk_toggle_button_get_active(
    + GTK_TOGGLE_BUTTON(dialog->auto_clear))) {
    pidgin_xfer_dialog_remove_xfer(dialog, xfer);
    - else
    + } else {
    update_buttons(dialog, xfer);
    + }
    /*
    * If all transfers are finished, and the pref is set, then
    * close the dialog. Otherwise just exit this function.
    */
    - if (dialog->keep_open)
    + if (!gtk_toggle_button_get_active(
    + GTK_TOGGLE_BUTTON(dialog->keep_open))) {
    return;
    + }
    valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(dialog->model), &iter);
    while (valid)
    @@ -1051,10 +884,10 @@
    PurpleXfer *next;
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model),
    - &iter, COLUMN_DATA, &val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    + COLUMN_XFER, &val);
    + next = g_value_get_object(&val);
    - next = g_value_get_pointer(&val);
    if (!purple_xfer_is_completed(next))
    return;
    @@ -1066,43 +899,6 @@
    }
    /**************************************************************************
    - * PidginXferDialog GBoxed code
    - **************************************************************************/
    -static PidginXferDialog *
    -pidgin_xfer_dialog_ref(PidginXferDialog *dialog)
    -{
    - g_return_val_if_fail(dialog != NULL, NULL);
    -
    - dialog->box_count++;
    -
    - return dialog;
    -}
    -
    -static void
    -pidgin_xfer_dialog_unref(PidginXferDialog *dialog)
    -{
    - g_return_if_fail(dialog != NULL);
    - g_return_if_fail(dialog->box_count >= 0);
    -
    - if (!dialog->box_count--)
    - pidgin_xfer_dialog_destroy(dialog);
    -}
    -
    -GType
    -pidgin_xfer_dialog_get_type(void)
    -{
    - static GType type = 0;
    -
    - if (type == 0) {
    - type = g_boxed_type_register_static("PidginXferDialog",
    - (GBoxedCopyFunc)pidgin_xfer_dialog_ref,
    - (GBoxedFreeFunc)pidgin_xfer_dialog_unref);
    - }
    -
    - return type;
    -}
    -
    -/**************************************************************************
    * File Transfer UI Ops
    **************************************************************************/
    static void
    --- a/pidgin/gtkxfer.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/gtkxfer.h Tue Oct 08 21:48:28 2019 -0500
    @@ -30,6 +30,8 @@
    #include "xfer.h"
    +G_BEGIN_DECLS
    +
    /**
    * PidginXferDialog:
    *
    @@ -38,24 +40,15 @@
    * The structure is opaque, as nobody should be touching anything inside of
    * it.
    */
    -typedef struct _PidginXferDialog PidginXferDialog;
    -
    #define PIDGIN_TYPE_XFER_DIALOG (pidgin_xfer_dialog_get_type())
    -
    -G_BEGIN_DECLS
    +G_DECLARE_FINAL_TYPE(PidginXferDialog, pidgin_xfer_dialog, PIDGIN, XFER_DIALOG,
    + GtkDialog)
    /**************************************************************************/
    /* GTK+ File Transfer Dialog API */
    /**************************************************************************/
    /**
    - * pidgin_xfer_dialog_get_type:
    - *
    - * Returns: The #GType for the #PidginXferDialog boxed structure.
    - */
    -GType pidgin_xfer_dialog_get_type(void);
    -
    -/**
    * pidgin_xfer_dialog_new:
    *
    * Creates a new file transfer dialog.
    --- a/pidgin/libpidgin.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/libpidgin.c Tue Oct 08 21:48:28 2019 -0500
    @@ -225,7 +225,7 @@
    /* Set the UI operation structures. */
    purple_accounts_set_ui_ops(pidgin_accounts_get_ui_ops());
    purple_xfers_set_ui_ops(pidgin_xfers_get_ui_ops());
    - purple_blist_set_ui_ops(pidgin_blist_get_ui_ops());
    + purple_blist_set_ui(PIDGIN_TYPE_BUDDY_LIST);
    purple_notify_set_ui_ops(pidgin_notify_get_ui_ops());
    purple_request_set_ui_ops(pidgin_request_get_ui_ops());
    purple_sound_set_ui_ops(pidgin_sound_get_ui_ops());
    @@ -246,7 +246,6 @@
    pidgin_log_init();
    pidgin_docklet_init();
    _pidgin_smiley_theme_init();
    - pidgin_utils_init();
    pidgin_medias_init();
    pidgin_notify_init();
    }
    @@ -259,7 +258,6 @@
    /* Uninit */
    PurpleDebugUi *ui;
    - pidgin_utils_uninit();
    pidgin_notify_uninit();
    _pidgin_smiley_theme_uninit();
    pidgin_conversations_uninit();
    @@ -584,7 +582,7 @@
    }
    }
    - search_path = g_build_filename(purple_user_dir(), "gtk-3.0.css", NULL);
    + search_path = g_build_filename(purple_config_dir(), "gtk-3.0.css", NULL);
    provider = gtk_css_provider_new();
    gui_check = gtk_css_provider_load_from_path(provider, search_path, &error);
    @@ -619,7 +617,7 @@
    }
    if (!g_getenv("PURPLE_PLUGINS_SKIP")) {
    - search_path = g_build_filename(purple_user_dir(),
    + search_path = g_build_filename(purple_data_dir(),
    "plugins", NULL);
    if (!g_stat(search_path, &st))
    g_mkdir(search_path, S_IRUSR | S_IWUSR | S_IXUSR);
    @@ -728,7 +726,7 @@
    pidgin_setup_error_handler();
    #endif
    - app = G_APPLICATION(gtk_application_new("im.pidgin.Pidgin",
    + app = G_APPLICATION(gtk_application_new("im.pidgin.Pidgin3",
    #if GLIB_CHECK_VERSION(2, 48, 0)
    G_APPLICATION_CAN_OVERRIDE_APP_ID |
    #endif
    @@ -761,8 +759,8 @@
    if (g_application_get_is_registered(app) &&
    g_application_get_is_remote(app)) {
    - g_printerr(_("Exiting because another libpurple client is "
    - "already running.\n"));
    + g_printerr("%s\n", _("Exiting because another libpurple client is "
    + "already running."));
    }
    /* Now that we're sure purple_core_quit() has been called,
    --- a/pidgin/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -7,8 +7,6 @@
    'gtkcellrendererexpander.c',
    'gtkconn.c',
    'gtkconv.c',
    - 'gtkconv-theme.c',
    - 'gtkconv-theme-loader.c',
    'gtkdialogs.c',
    'gtkdnd-hints.c',
    'gtkdocklet.c',
    @@ -34,19 +32,19 @@
    'gtkstatusbox.c',
    'gtkstyle.c',
    'gtkutils.c',
    - 'gtkwebview.c',
    - 'gtkwebviewtoolbar.c',
    'gtkwhiteboard.c',
    'gtkxfer.c',
    'libpidgin.c',
    'minidialog.c',
    'pidginabout.c',
    + 'pidginaccountchooser.c',
    'pidgincontactcompletion.c',
    'pidgindebug.c',
    'pidgindebugplugininfo.c',
    'pidgingdkpixbuf.c',
    'pidgininvitedialog.c',
    'pidginlog.c',
    + 'pidginmessage.c',
    'pidgintalkatu.c',
    'pidgintooltip.c',
    ]
    @@ -60,8 +58,6 @@
    'gtkconn.h',
    'gtkconv.h',
    'gtkconvwin.h',
    - 'gtkconv-theme.h',
    - 'gtkconv-theme-loader.h',
    'gtkdialogs.h',
    'gtkdnd-hints.h',
    'gtkdocklet.h',
    @@ -89,12 +85,11 @@
    'gtkstyle.h',
    'pidginstock.h',
    'gtkutils.h',
    - 'gtkwebview.h',
    - 'gtkwebviewtoolbar.h',
    'gtkwhiteboard.h',
    'gtkxfer.h',
    'minidialog.h',
    'pidginabout.h',
    + 'pidginaccountchooser.h',
    'pidgincontactcompletion.h',
    'pidgindebug.h',
    'pidgindebugplugininfo.h',
    @@ -102,6 +97,7 @@
    'pidginicon.h',
    'pidgininvitedialog.h',
    'pidginlog.h',
    + 'pidginmessage.h',
    'pidgintalkatu.h',
    'pidgintooltip.h',
    'pidgin.h',
    @@ -167,7 +163,6 @@
    include_directories : [toplevel_inc],
    version : PURPLE_LIB_VERSION,
    dependencies : [
    - enchant,
    glib,
    gstreamer_video,
    gtk,
    @@ -175,7 +170,6 @@
    json,
    math,
    talkatu_dep,
    - webkit,
    x11,
    libpurple_dep,
    ],
    @@ -183,7 +177,7 @@
    libpidgin_dep = declare_dependency(
    include_directories : [toplevel_inc, libpidgin_inc],
    link_with : libpidgin,
    - dependencies : [webkit, gtk, glib, math])
    + dependencies : [gtk, glib, math])
    pidgin = executable('pidgin3',
    pidgin_SOURCES,
    @@ -204,7 +198,7 @@
    filebase : 'pidgin-3',
    subdirs : 'pidgin-3',
    libraries : [libpidgin],
    - requires : ['gtk+-3.0', 'webkitgtk-3.0', 'purple-3'],
    + requires : ['gtk+-3.0', 'purple-3', 'talkatu'],
    variables : ['plugindir=${libdir}/pidgin-@0@'.format(purple_major_version)])
    if INSTALL_I18N
    @@ -234,7 +228,7 @@
    gnome.generate_gir(libpidgin,
    sources : introspection_sources,
    - includes : ['GObject-2.0', 'Gtk-3.0', libpurple_gir[0], 'WebKit-3.0', talkatu_gir],
    + includes : ['GObject-2.0', 'Gtk-3.0', libpurple_gir[0], talkatu_gir, gplugin_gir],
    namespace : 'Pidgin',
    symbol_prefix : 'pidgin',
    identifier_prefix : 'Pidgin',
    @@ -250,7 +244,7 @@
    endif
    subdir('data')
    + subdir('glade')
    subdir('pixmaps')
    subdir('plugins')
    - subdir('themes')
    endif # ENABLE_GTK
    --- a/pidgin/pidginabout.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/pidginabout.c Tue Oct 08 21:48:28 2019 -0500
    @@ -32,25 +32,9 @@
    #include "meson-config.h"
    #endif
    -typedef struct _PidginAboutDialogPrivate PidginAboutDialogPrivate;
    -
    struct _PidginAboutDialog {
    GtkDialog parent;
    - /*< private >*/
    - PidginAboutDialogPrivate *priv;
    -};
    -
    -struct _PidginAboutDialogClass {
    - GtkDialogClass parent;
    -
    - void (*_pidgin_reserved1)(void);
    - void (*_pidgin_reserved2)(void);
    - void (*_pidgin_reserved3)(void);
    - void (*_pidgin_reserved4)(void);
    -};
    -
    -struct _PidginAboutDialogPrivate {
    GtkWidget *close_button;
    GtkWidget *application_name;
    GtkWidget *stack;
    @@ -71,8 +55,6 @@
    GtkTreeStore *build_info_store;
    };
    -G_DEFINE_TYPE_WITH_PRIVATE(PidginAboutDialog, pidgin_about_dialog, GTK_TYPE_DIALOG);
    -
    /******************************************************************************
    * Helpers
    *****************************************************************************/
    @@ -84,14 +66,13 @@
    VERSION
    );
    - gtk_label_set_text(GTK_LABEL(about->priv->application_name), label);
    + gtk_label_set_text(GTK_LABEL(about->application_name), label);
    g_free(label);
    }
    static void
    _pidgin_about_dialog_load_main_page(PidginAboutDialog *about) {
    - PidginAboutDialogPrivate *priv = pidgin_about_dialog_get_instance_private(about);
    GtkTextIter start;
    GInputStream *istream = NULL;
    GString *str = NULL;
    @@ -113,10 +94,10 @@
    size += read;
    }
    - gtk_text_buffer_get_start_iter(priv->main_buffer, &start);
    + gtk_text_buffer_get_start_iter(about->main_buffer, &start);
    talkatu_markdown_buffer_insert_markdown(
    - TALKATU_MARKDOWN_BUFFER(priv->main_buffer),
    + TALKATU_MARKDOWN_BUFFER(about->main_buffer),
    &start,
    str->str,
    size
    @@ -208,12 +189,12 @@
    static void
    _pidgin_about_dialog_load_developers(PidginAboutDialog *about) {
    - _pidgin_about_dialog_load_json(about->priv->developers_store, "developers");
    + _pidgin_about_dialog_load_json(about->developers_store, "developers");
    }
    static void
    _pidgin_about_dialog_load_translators(PidginAboutDialog *about) {
    - _pidgin_about_dialog_load_json(about->priv->translators_store, "languages");
    + _pidgin_about_dialog_load_json(about->translators_store, "languages");
    }
    static void
    @@ -228,9 +209,9 @@
    gint idx = 0;
    markup = g_strdup_printf("<span font-weight=\"bold\">%s</span>", title);
    - gtk_tree_store_append(about->priv->build_info_store, &section, NULL);
    + gtk_tree_store_append(about->build_info_store, &section, NULL);
    gtk_tree_store_set(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    0, markup,
    -1
    @@ -246,14 +227,9 @@
    continue;
    }
    - gtk_tree_store_append(about->priv->build_info_store, &value, &section);
    - gtk_tree_store_set(
    - about->priv->build_info_store,
    - &value,
    - 0, value_split[0] ? value_split[0] : "",
    - 1, value_split[1] ? value_split[1] : "",
    - -1
    - );
    + gtk_tree_store_append(about->build_info_store, &value, &section);
    + gtk_tree_store_set(about->build_info_store, &value, 0, value_split[0],
    + 1, value_split[1] ? value_split[1] : "", -1);
    g_strfreev(value_split);
    }
    @@ -293,9 +269,9 @@
    "<span font-weight=\"bold\">%s</span>",
    _("Build Information")
    );
    - gtk_tree_store_append(about->priv->build_info_store, &section, NULL);
    + gtk_tree_store_append(about->build_info_store, &section, NULL);
    gtk_tree_store_set(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    0, markup,
    -1
    @@ -303,9 +279,9 @@
    g_free(markup);
    /* add the commit hash */
    - gtk_tree_store_append(about->priv->build_info_store, &item, &section);
    + gtk_tree_store_append(about->build_info_store, &item, &section);
    gtk_tree_store_set(
    - about->priv->build_info_store,
    + about->build_info_store,
    &item,
    0, "Commit Hash",
    1, REVISION,
    @@ -314,7 +290,7 @@
    /* add the purple version */
    _pidgin_about_dialog_build_info_add_version(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    _("Purple Version"),
    PURPLE_MAJOR_VERSION,
    @@ -324,7 +300,7 @@
    /* add the glib version */
    _pidgin_about_dialog_build_info_add_version(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    _("GLib Version"),
    GLIB_MAJOR_VERSION,
    @@ -334,7 +310,7 @@
    /* add the gtk version */
    _pidgin_about_dialog_build_info_add_version(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    _("GTK+ Version"),
    GTK_MAJOR_VERSION,
    @@ -353,9 +329,9 @@
    "<span font-weight=\"bold\">%s</span>",
    _("Runtime Information")
    );
    - gtk_tree_store_append(about->priv->build_info_store, &section, NULL);
    + gtk_tree_store_append(about->build_info_store, &section, NULL);
    gtk_tree_store_set(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    0, markup,
    -1
    @@ -364,7 +340,7 @@
    /* add the purple version */
    _pidgin_about_dialog_build_info_add_version(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    _("Purple Version"),
    purple_major_version,
    @@ -374,7 +350,7 @@
    /* add the glib version */
    _pidgin_about_dialog_build_info_add_version(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    _("GLib Version"),
    glib_major_version,
    @@ -384,7 +360,7 @@
    /* add the gtk version */
    _pidgin_about_dialog_build_info_add_version(
    - about->priv->build_info_store,
    + about->build_info_store,
    &section,
    _("GTK+ Version"),
    gtk_major_version,
    @@ -414,6 +390,8 @@
    /******************************************************************************
    * GObject Stuff
    *****************************************************************************/
    +G_DEFINE_TYPE(PidginAboutDialog, pidgin_about_dialog, GTK_TYPE_DIALOG);
    +
    static void
    pidgin_about_dialog_class_init(PidginAboutDialogClass *klass) {
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    @@ -423,35 +401,33 @@
    "/im/pidgin/Pidgin/About/about.ui"
    );
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, close_button);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, application_name);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, stack);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, close_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, application_name);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, stack);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, main_scrolled_window);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, main_buffer);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, main_scrolled_window);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, main_buffer);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, developers_page);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, developers_store);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, developers_treeview);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, developers_page);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, developers_store);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, developers_treeview);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, translators_page);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, translators_store);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, translators_treeview);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, translators_page);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, translators_store);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, translators_treeview);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, build_info_page);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, build_info_store);
    - gtk_widget_class_bind_template_child_private(widget_class, PidginAboutDialog, build_info_treeview);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, build_info_page);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, build_info_store);
    + gtk_widget_class_bind_template_child(widget_class, PidginAboutDialog, build_info_treeview);
    }
    static void
    pidgin_about_dialog_init(PidginAboutDialog *about) {
    - about->priv = pidgin_about_dialog_get_instance_private(about);
    -
    gtk_widget_init_template(GTK_WIDGET(about));
    /* wire up the close button */
    g_signal_connect(
    - about->priv->close_button,
    + about->close_button,
    "clicked",
    G_CALLBACK(_pidgin_about_dialog_close),
    about
    @@ -465,15 +441,15 @@
    /* setup the developers stuff */
    _pidgin_about_dialog_load_developers(about);
    - gtk_tree_view_expand_all(GTK_TREE_VIEW(about->priv->developers_treeview));
    + gtk_tree_view_expand_all(GTK_TREE_VIEW(about->developers_treeview));
    /* setup the translators stuff */
    _pidgin_about_dialog_load_translators(about);
    - gtk_tree_view_expand_all(GTK_TREE_VIEW(about->priv->translators_treeview));
    + gtk_tree_view_expand_all(GTK_TREE_VIEW(about->translators_treeview));
    /* setup the build info page */
    _pidgin_about_dialog_load_build_configuration(about);
    - gtk_tree_view_expand_all(GTK_TREE_VIEW(about->priv->build_info_treeview));
    + gtk_tree_view_expand_all(GTK_TREE_VIEW(about->build_info_treeview));
    }
    GtkWidget *
    --- a/pidgin/pidginabout.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/pidginabout.h Tue Oct 08 21:48:28 2019 -0500
    @@ -12,7 +12,7 @@
    G_BEGIN_DECLS
    -#define PIDGIN_TYPE_ABOUT_DIALOG pidgin_about_dialog_get_type()
    +#define PIDGIN_TYPE_ABOUT_DIALOG (pidgin_about_dialog_get_type())
    G_DECLARE_FINAL_TYPE(PidginAboutDialog, pidgin_about_dialog, PIDGIN,
    ABOUT_DIALOG, GtkDialog)
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/pidginaccountchooser.c Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,273 @@
    +/* pidgin
    + *
    + * Pidgin is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + */
    +#include "internal.h"
    +#include "pidgin.h"
    +
    +#include "gtkutils.h"
    +#include "pidginaccountchooser.h"
    +
    +/******************************************************************************
    + * Enums
    + *****************************************************************************/
    +
    +enum
    +{
    + AOP_ICON_COLUMN,
    + AOP_NAME_COLUMN,
    + AOP_DATA_COLUMN,
    + AOP_COLUMN_COUNT
    +};
    +
    +/******************************************************************************
    + * Structs
    + *****************************************************************************/
    +
    +struct _PidginAccountChooser {
    + GtkComboBox parent;
    +
    + GtkListStore *model;
    +
    + PurpleFilterAccountFunc filter_func;
    + gboolean show_all;
    +};
    +
    +/******************************************************************************
    + * Code
    + *****************************************************************************/
    +G_DEFINE_TYPE(PidginAccountChooser, pidgin_account_chooser, GTK_TYPE_COMBO_BOX)
    +
    +static gpointer
    +account_chooser_get_selected(PidginAccountChooser *chooser)
    +{
    + gpointer data = NULL;
    + GtkTreeIter iter;
    +
    + if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(chooser), &iter)) {
    + gtk_tree_model_get(
    + gtk_combo_box_get_model(GTK_COMBO_BOX(chooser)), &iter,
    + AOP_DATA_COLUMN, &data, -1);
    + }
    +
    + return data;
    +}
    +
    +static void
    +account_chooser_select_by_data(GtkWidget *chooser, gpointer data)
    +{
    + GtkTreeModel *model;
    + GtkTreeIter iter;
    + gpointer iter_data;
    + model = gtk_combo_box_get_model(GTK_COMBO_BOX(chooser));
    + if (gtk_tree_model_get_iter_first(model, &iter)) {
    + do {
    + gtk_tree_model_get(model, &iter, AOP_DATA_COLUMN,
    + &iter_data, -1);
    + if (iter_data == data) {
    + gtk_combo_box_set_active_iter(
    + GTK_COMBO_BOX(chooser), &iter);
    + return;
    + }
    + } while (gtk_tree_model_iter_next(model, &iter));
    + }
    +}
    +
    +static void
    +set_account_menu(PidginAccountChooser *chooser, PurpleAccount *default_account)
    +{
    + PurpleAccount *account;
    + GdkPixbuf *pixbuf = NULL;
    + GList *list;
    + GList *p;
    + GtkTreeIter iter;
    + gint default_item = 0;
    + gint i;
    + gchar buf[256];
    +
    + if (chooser->show_all) {
    + list = purple_accounts_get_all();
    + } else {
    + list = purple_connections_get_all();
    + }
    +
    + gtk_list_store_clear(chooser->model);
    + for (p = list, i = 0; p != NULL; p = p->next, i++) {
    + if (chooser->show_all) {
    + account = (PurpleAccount *)p->data;
    + } else {
    + PurpleConnection *gc = (PurpleConnection *)p->data;
    +
    + account = purple_connection_get_account(gc);
    + }
    +
    + if (chooser->filter_func && !chooser->filter_func(account)) {
    + i--;
    + continue;
    + }
    +
    + pixbuf = pidgin_create_protocol_icon(
    + account, PIDGIN_PROTOCOL_ICON_SMALL);
    +
    + if (pixbuf) {
    + if (purple_account_is_disconnected(account) &&
    + chooser->show_all && purple_connections_get_all()) {
    + gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf,
    + 0.0, FALSE);
    + }
    + }
    +
    + if (purple_account_get_private_alias(account)) {
    + g_snprintf(buf, sizeof(buf), "%s (%s) (%s)",
    + purple_account_get_username(account),
    + purple_account_get_private_alias(account),
    + purple_account_get_protocol_name(account));
    + } else {
    + g_snprintf(buf, sizeof(buf), "%s (%s)",
    + purple_account_get_username(account),
    + purple_account_get_protocol_name(account));
    + }
    +
    + gtk_list_store_append(chooser->model, &iter);
    + gtk_list_store_set(chooser->model, &iter, AOP_ICON_COLUMN,
    + pixbuf, AOP_NAME_COLUMN, buf,
    + AOP_DATA_COLUMN, account, -1);
    +
    + if (pixbuf) {
    + g_object_unref(pixbuf);
    + }
    +
    + if (default_account && account == default_account) {
    + default_item = i;
    + }
    + }
    +
    + gtk_combo_box_set_active(GTK_COMBO_BOX(chooser), default_item);
    +}
    +
    +static void
    +regenerate_account_menu(PidginAccountChooser *chooser)
    +{
    + PurpleAccount *account;
    +
    + account = (PurpleAccount *)account_chooser_get_selected(chooser);
    +
    + set_account_menu(chooser, account);
    +}
    +
    +static void
    +account_menu_sign_on_off_cb(PurpleConnection *gc, PidginAccountChooser *chooser)
    +{
    + regenerate_account_menu(chooser);
    +}
    +
    +static void
    +account_menu_added_removed_cb(PurpleAccount *account,
    + PidginAccountChooser *chooser)
    +{
    + regenerate_account_menu(chooser);
    +}
    +
    +static gboolean
    +account_menu_destroyed_cb(GtkWidget *chooser, GdkEvent *event, void *user_data)
    +{
    + purple_signals_disconnect_by_handle(chooser);
    +
    + return FALSE;
    +}
    +
    +/******************************************************************************
    + * GObject implementation
    + *****************************************************************************/
    +static void
    +pidgin_account_chooser_class_init(PidginAccountChooserClass *klass)
    +{
    + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    +
    + gtk_widget_class_set_template_from_resource(
    + widget_class, "/im/pidgin/Pidgin/Accounts/chooser.ui");
    +
    + gtk_widget_class_bind_template_child(widget_class, PidginAccountChooser,
    + model);
    +
    + gtk_widget_class_bind_template_callback(widget_class,
    + account_menu_destroyed_cb);
    +}
    +
    +static void
    +pidgin_account_chooser_init(PidginAccountChooser *chooser)
    +{
    + gtk_widget_init_template(GTK_WIDGET(chooser));
    +
    + /* Register the purple sign on/off event callbacks. */
    + purple_signal_connect(
    + purple_connections_get_handle(), "signed-on", chooser,
    + PURPLE_CALLBACK(account_menu_sign_on_off_cb), chooser);
    + purple_signal_connect(
    + purple_connections_get_handle(), "signed-off", chooser,
    + PURPLE_CALLBACK(account_menu_sign_on_off_cb), chooser);
    + purple_signal_connect(
    + purple_accounts_get_handle(), "account-added", chooser,
    + PURPLE_CALLBACK(account_menu_added_removed_cb), chooser);
    + purple_signal_connect(
    + purple_accounts_get_handle(), "account-removed", chooser,
    + PURPLE_CALLBACK(account_menu_added_removed_cb), chooser);
    +}
    +
    +/******************************************************************************
    + * Public API
    + *****************************************************************************/
    +GtkWidget *
    +pidgin_account_chooser_new(PurpleAccount *default_account, gboolean show_all)
    +{
    + PidginAccountChooser *chooser = NULL;
    +
    + chooser = g_object_new(PIDGIN_TYPE_ACCOUNT_CHOOSER, NULL);
    + chooser->show_all = show_all;
    + set_account_menu(PIDGIN_ACCOUNT_CHOOSER(chooser), default_account);
    +
    + return GTK_WIDGET(chooser);
    +}
    +
    +void
    +pidgin_account_chooser_set_filter_func(PidginAccountChooser *chooser,
    + PurpleFilterAccountFunc filter_func)
    +{
    + g_return_if_fail(PIDGIN_IS_ACCOUNT_CHOOSER(chooser));
    +
    + chooser->filter_func = filter_func;
    + regenerate_account_menu(chooser);
    +}
    +
    +PurpleAccount *
    +pidgin_account_chooser_get_selected(GtkWidget *chooser)
    +{
    + g_return_val_if_fail(PIDGIN_IS_ACCOUNT_CHOOSER(chooser), NULL);
    +
    + return (PurpleAccount *)account_chooser_get_selected(
    + PIDGIN_ACCOUNT_CHOOSER(chooser));
    +}
    +
    +void
    +pidgin_account_chooser_set_selected(GtkWidget *chooser, PurpleAccount *account)
    +{
    + g_return_if_fail(PIDGIN_IS_ACCOUNT_CHOOSER(chooser));
    +
    + account_chooser_select_by_data(chooser, account);
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/pidginaccountchooser.h Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,98 @@
    +/* pidgin
    + *
    + * Pidgin is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + */
    +
    +#ifndef PIDGIN_ACCOUNT_CHOOSER_H
    +#define PIDGIN_ACCOUNT_CHOOSER_H
    +/**
    + * SECTION:pidgin-account-chooser
    + * @section_id: pidgin-account-chooser
    + * @short_description: <filename>pidginaccountchooser.h</filename>
    + * @title: Pidgin Account Chooser Combo Box Widget
    + */
    +
    +#include <gtk/gtk.h>
    +
    +#include "pidgin.h"
    +
    +#include "account.h"
    +
    +G_BEGIN_DECLS
    +
    +#define PIDGIN_TYPE_ACCOUNT_CHOOSER (pidgin_account_chooser_get_type())
    +
    +G_DECLARE_FINAL_TYPE(PidginAccountChooser, pidgin_account_chooser, PIDGIN,
    + ACCOUNT_CHOOSER, GtkComboBox)
    +
    +/**
    + * pidgin_account_chooser_new:
    + * @default_account: The account to select by default.
    + * @show_all: Whether or not to show all accounts, or just active accounts.
    + *
    + * Creates a combo box filled with accounts.
    + *
    + * Returns: (transfer full): The account chooser combo box.
    + *
    + * Since: 3.0.0
    + */
    +GtkWidget *pidgin_account_chooser_new(PurpleAccount *default_account,
    + gboolean show_all);
    +
    +/**
    + * pidgin_account_chooser_set_filter_func:
    + * @chooser: The account chooser combo box.
    + * @filter_func: A function for checking if an account should be shown. This
    + * can be %NULL to remove any filter.
    + *
    + * Set the filter function used to determine which accounts to show.
    + *
    + * Since: 3.0.0
    + */
    +void pidgin_account_chooser_set_filter_func(
    + PidginAccountChooser *chooser, PurpleFilterAccountFunc filter_func);
    +
    +/**
    + * pidgin_account_chooser_get_selected:
    + * @chooser: The chooser created by pidgin_account_chooser_new().
    + *
    + * Gets the currently selected account from an account combo box.
    + *
    + * Returns: (transfer none): Returns the #PurpleAccount that is currently
    + * selected.
    + *
    + * Since: 3.0.0
    + */
    +PurpleAccount *pidgin_account_chooser_get_selected(GtkWidget *chooser);
    +
    +/**
    + * pidgin_account_chooser_set_selected:
    + * @chooser: The chooser created by pidgin_account_chooser_new().
    + * @account: The #PurpleAccount to select.
    + *
    + * Sets the currently selected account for an account combo box.
    + *
    + * Since: 3.0.0
    + */
    +void pidgin_account_chooser_set_selected(GtkWidget *chooser,
    + PurpleAccount *account);
    +
    +G_END_DECLS
    +
    +#endif /* PIDGIN_ACCOUNT_CHOOSER_H */
    --- a/pidgin/pidgindebug.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/pidgindebug.c Tue Oct 08 21:48:28 2019 -0500
    @@ -28,7 +28,6 @@
    #include "gtkdialogs.h"
    #include "gtkutils.h"
    -#include "gtkwebview.h"
    #include "pidgindebug.h"
    #include "pidginstock.h"
    @@ -64,9 +63,9 @@
    gboolean paused;
    -#if GTK_CHECK_VERSION(3,12,0)
    GtkWidget *popover;
    -#endif
    + GtkWidget *popover_invert;
    + GtkWidget *popover_highlight;
    gboolean invert;
    gboolean highlight;
    GRegex *regex;
    @@ -392,49 +391,31 @@
    }
    static void
    -regex_menu_cb(GtkWidget *item, const gchar *pref) {
    +regex_menu_cb(GtkWidget *item, PidginDebugWindow *win)
    +{
    gboolean active;
    -#if GTK_CHECK_VERSION(3,12,0)
    active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(item));
    -#else
    - active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(item));
    -#endif
    - purple_prefs_set_bool(pref, active);
    + if (item == win->popover_highlight) {
    + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/highlight", active);
    + } else if (item == win->popover_invert) {
    + purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/invert", active);
    + }
    }
    static void
    regex_popup_cb(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event,
    PidginDebugWindow *win)
    {
    -#if GTK_CHECK_VERSION(3,12,0)
    GdkRectangle rect;
    + if (icon_pos != GTK_ENTRY_ICON_PRIMARY) {
    + return;
    + }
    +
    gtk_entry_get_icon_area(entry, icon_pos, &rect);
    gtk_popover_set_pointing_to(GTK_POPOVER(win->popover), &rect);
    -#if GTK_CHECK_VERSION(3,22,0)
    gtk_popover_popup(GTK_POPOVER(win->popover));
    -#else
    - gtk_widget_show(win->popover);
    -#endif
    -#else
    - GtkWidget *menu;
    -
    - menu = gtk_menu_new();
    - pidgin_new_check_item(menu, _("Invert"),
    - G_CALLBACK(regex_menu_cb),
    - PIDGIN_PREFS_ROOT "/debug/invert", win->invert);
    - pidgin_new_check_item(menu, _("Highlight matches"),
    - G_CALLBACK(regex_menu_cb),
    - PIDGIN_PREFS_ROOT "/debug/highlight", win->highlight);
    -
    -#if GTK_CHECK_VERSION(3,22,0)
    - gtk_menu_popup_at_widget(GTK_MENU(menu), GTK_WIDGET(entry),
    - GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, event);
    -#else
    - gtk_menu_popup_at_pointer(GTK_MENU(menu), event);
    -#endif
    -#endif
    }
    static void
    @@ -571,6 +552,12 @@
    widget_class, PidginDebugWindow, expression);
    gtk_widget_class_bind_template_child(
    widget_class, PidginDebugWindow, tags.match);
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginDebugWindow, popover);
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginDebugWindow, popover_invert);
    + gtk_widget_class_bind_template_child(
    + widget_class, PidginDebugWindow, popover_highlight);
    gtk_widget_class_bind_template_callback(widget_class, toolbar_context);
    gtk_widget_class_bind_template_callback(widget_class, save_cb);
    gtk_widget_class_bind_template_callback(widget_class, clear_cb);
    @@ -580,6 +567,7 @@
    gtk_widget_class_bind_template_callback(widget_class,
    regex_changed_cb);
    gtk_widget_class_bind_template_callback(widget_class, regex_popup_cb);
    + gtk_widget_class_bind_template_callback(widget_class, regex_menu_cb);
    gtk_widget_class_bind_template_callback(widget_class,
    regex_key_release_cb);
    gtk_widget_class_bind_template_callback(widget_class,
    @@ -607,11 +595,6 @@
    "background-image: none;"
    "background-color: @success_color;"
    "}";
    -#if GTK_CHECK_VERSION(3,12,0)
    - GtkBuilder *builder;
    - GtkWidget *popover_invert;
    - GtkWidget *popover_highlight;
    -#endif
    gtk_widget_init_template(GTK_WIDGET(win));
    @@ -676,32 +659,10 @@
    purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filterlevel",
    filter_level_pref_changed, win);
    -#if GTK_CHECK_VERSION(3,12,0)
    - builder = gtk_builder_new_from_resource(
    - "/im/pidgin/Pidgin/Debug/filter-popover.ui");
    - win->popover = GTK_WIDGET(gtk_builder_get_object(builder,
    - "popover"));
    - gtk_popover_set_relative_to(GTK_POPOVER(win->popover),
    - win->expression);
    -
    - popover_invert = GTK_WIDGET(gtk_builder_get_object(builder,
    - "popover.invert"));
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(popover_invert),
    + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->popover_invert),
    win->invert);
    - g_signal_connect(G_OBJECT(popover_invert), "toggled",
    - G_CALLBACK(regex_menu_cb),
    - PIDGIN_PREFS_ROOT "/debug/invert");
    -
    - popover_highlight = GTK_WIDGET(gtk_builder_get_object(builder,
    - "popover.highlight"));
    - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(popover_highlight),
    + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->popover_highlight),
    win->highlight);
    - g_signal_connect(G_OBJECT(popover_highlight), "toggled",
    - G_CALLBACK(regex_menu_cb),
    - PIDGIN_PREFS_ROOT "/debug/highlight");
    -
    - g_object_unref(builder);
    -#endif
    }
    /* The *start* and *end* marks bound the beginning and end of an
    @@ -858,13 +819,17 @@
    /* Register the glib/gtk log handlers. */
    REGISTER_G_LOG_HANDLER(NULL);
    REGISTER_G_LOG_HANDLER("Gdk");
    - REGISTER_G_LOG_HANDLER("Gtk");
    REGISTER_G_LOG_HANDLER("GdkPixbuf");
    REGISTER_G_LOG_HANDLER("GLib");
    + REGISTER_G_LOG_HANDLER("GLib-GObject");
    REGISTER_G_LOG_HANDLER("GModule");
    - REGISTER_G_LOG_HANDLER("GLib-GObject");
    + REGISTER_G_LOG_HANDLER("Gnt"); /* just in case we find a gnt plugin */
    + REGISTER_G_LOG_HANDLER("GPlugin");
    + REGISTER_G_LOG_HANDLER("GPluginGtk");
    REGISTER_G_LOG_HANDLER("GThread");
    + REGISTER_G_LOG_HANDLER("Gtk");
    REGISTER_G_LOG_HANDLER("Json");
    + REGISTER_G_LOG_HANDLER("Talkatu");
    #ifdef USE_GSTREAMER
    REGISTER_G_LOG_HANDLER("GStreamer");
    #endif
    --- a/pidgin/pidgindebugplugininfo.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/pidgindebugplugininfo.c Tue Oct 08 21:48:28 2019 -0500
    @@ -61,9 +61,10 @@
    static gint
    purple_debug_plugin_compare_plugin_id(gconstpointer a, gconstpointer b) {
    return g_strcmp0(
    - purple_plugin_info_get_id(purple_plugin_get_info(PURPLE_PLUGIN(a))),
    - purple_plugin_info_get_id(purple_plugin_get_info(PURPLE_PLUGIN(b)))
    - );
    + gplugin_plugin_info_get_id(GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(PURPLE_PLUGIN((gpointer)a)))),
    + gplugin_plugin_info_get_id(GPLUGIN_PLUGIN_INFO(
    + purple_plugin_get_info(PURPLE_PLUGIN((gpointer)b)))));
    }
    static gchar *
    @@ -82,7 +83,9 @@
    PurplePlugin *plugin = PURPLE_PLUGIN(l->data);
    PurplePluginInfo *info = purple_plugin_get_info(plugin);
    PurplePluginExtraCb extra_cb;
    - gchar *name = g_markup_escape_text(purple_plugin_info_get_name(info), -1);
    + gchar *name = g_markup_escape_text(
    + gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(info)),
    + -1);
    gchar *version, *license, *website, *id;
    gchar *authors = NULL, *extra = NULL;
    const gchar *error_message = NULL;
    --- a/pidgin/pidginlog.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/pidginlog.c Tue Oct 08 21:48:28 2019 -0500
    @@ -83,7 +83,7 @@
    static void populate_log_tree(PidginLogViewer *lv);
    static PidginLogViewer *syslog_viewer = NULL;
    -struct log_viewer_hash_t {
    +struct log_viewer_hash {
    PurpleLogType type;
    char *buddyname;
    PurpleAccount *account;
    @@ -92,7 +92,7 @@
    static guint log_viewer_hash(gconstpointer data)
    {
    - const struct log_viewer_hash_t *viewer = data;
    + const struct log_viewer_hash *viewer = data;
    if (viewer->contact != NULL)
    return g_direct_hash(viewer->contact);
    @@ -103,7 +103,7 @@
    static gboolean log_viewer_equal(gconstpointer y, gconstpointer z)
    {
    - const struct log_viewer_hash_t *a, *b;
    + const struct log_viewer_hash *a, *b;
    int ret;
    char *normal;
    @@ -217,7 +217,9 @@
    pidgin_clear_cursor(GTK_WIDGET(lv));
    }
    -static void destroy_cb(GtkWidget *w, gint resp, struct log_viewer_hash_t *ht) {
    +static void
    +destroy_cb(GtkWidget *w, gint resp, struct log_viewer_hash *ht)
    +{
    PidginLogViewer *lv = syslog_viewer;
    #ifdef _WIN32
    @@ -265,8 +267,7 @@
    purple_request_close_with_handle(lv);
    - g_list_foreach(lv->logs, (GFunc)purple_log_free, NULL);
    - g_list_free(lv->logs);
    + g_list_free_full(lv->logs, (GDestroyNotify)purple_log_free);
    g_free(lv->search);
    @@ -582,8 +583,9 @@
    }
    }
    -static PidginLogViewer *display_log_viewer(struct log_viewer_hash_t *ht, GList *logs,
    - const char *title, GtkWidget *icon, int log_size)
    +static PidginLogViewer *
    +display_log_viewer(struct log_viewer_hash *ht, GList *logs, const char *title,
    + GtkWidget *icon, int log_size)
    {
    PidginLogViewer *lv;
    @@ -643,7 +645,7 @@
    /* Log size ************/
    if(log_size) {
    - char *sz_txt = purple_str_size_to_units(log_size);
    + char *sz_txt = g_format_size(log_size);
    char *text = g_strdup_printf("<span weight='bold'>%s</span> %s",
    _("Total log size:"), sz_txt);
    gtk_label_set_markup(GTK_LABEL(lv->size_label), text);
    @@ -718,7 +720,7 @@
    ****************************************************************************/
    void pidgin_log_show(PurpleLogType type, const char *buddyname, PurpleAccount *account) {
    - struct log_viewer_hash_t *ht;
    + struct log_viewer_hash *ht;
    PidginLogViewer *lv = NULL;
    const char *name = buddyname;
    char *title;
    @@ -727,7 +729,7 @@
    g_return_if_fail(account != NULL);
    g_return_if_fail(buddyname != NULL);
    - ht = g_new0(struct log_viewer_hash_t, 1);
    + ht = g_new0(struct log_viewer_hash, 1);
    ht->type = type;
    ht->buddyname = g_strdup(buddyname);
    @@ -772,7 +774,7 @@
    }
    void pidgin_log_show_contact(PurpleContact *contact) {
    - struct log_viewer_hash_t *ht;
    + struct log_viewer_hash *ht;
    PurpleBlistNode *child;
    PidginLogViewer *lv = NULL;
    GList *logs = NULL;
    @@ -784,7 +786,7 @@
    g_return_if_fail(contact != NULL);
    - ht = g_new0(struct log_viewer_hash_t, 1);
    + ht = g_new0(struct log_viewer_hash, 1);
    ht->type = PURPLE_LOG_IM;
    ht->contact = contact;
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/pidginmessage.c Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,191 @@
    +/* pidgin
    + *
    + * Pidgin is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + */
    +
    +#include "pidginmessage.h"
    +
    +struct _PidginMessage {
    + GObject parent;
    +
    + PurpleMessage *msg;
    + GDateTime *timestamp;
    +};
    +
    +enum {
    + PROP_0,
    + PROP_MESSAGE,
    + N_PROPERTIES,
    + /* overrides */
    + PROP_ID = N_PROPERTIES,
    + PROP_CONTENT_TYPE,
    + PROP_AUTHOR,
    + PROP_CONTENTS,
    + PROP_TIMESTAMP,
    + PROP_EDITED,
    +};
    +static GParamSpec *properties[N_PROPERTIES] = {NULL, };
    +
    +/******************************************************************************
    + * Helpers
    + *****************************************************************************/
    +static void
    +pidgin_message_set_message(PidginMessage *msg, PurpleMessage *purple_msg) {
    + if(g_set_object(&msg->msg, purple_msg)) {
    + g_clear_pointer(&msg->timestamp, g_date_time_unref);
    + msg->timestamp = g_date_time_new_from_unix_local(purple_message_get_time(purple_msg));
    +
    + g_object_freeze_notify(G_OBJECT(msg));
    + g_object_notify_by_pspec(G_OBJECT(msg), properties[PROP_MESSAGE]);
    + g_object_notify(G_OBJECT(msg), "timestamp");
    + g_object_thaw_notify(G_OBJECT(msg));
    + }
    +}
    +
    +/******************************************************************************
    + * TalkatuMessage Implementation
    + *****************************************************************************/
    +static void
    +pidgin_message_talkatu_message_init(TalkatuMessageInterface *iface) {
    + /* we don't actually change behavior, we just override properties */
    +}
    +
    +/******************************************************************************
    + * GObject Implementation
    + *****************************************************************************/
    +G_DEFINE_TYPE_EXTENDED(
    + PidginMessage,
    + pidgin_message,
    + G_TYPE_OBJECT,
    + 0,
    + G_IMPLEMENT_INTERFACE(TALKATU_TYPE_MESSAGE, pidgin_message_talkatu_message_init)
    +);
    +
    +static void
    +pidgin_message_get_property(GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) {
    + PidginMessage *msg = PIDGIN_MESSAGE(obj);
    +
    + switch(param_id) {
    + case PROP_MESSAGE:
    + g_value_set_object(value, msg->msg);
    + break;
    + case PROP_ID:
    + g_value_set_uint(value, purple_message_get_id(msg->msg));
    + break;
    + case PROP_CONTENT_TYPE:
    + g_value_set_enum(value, TALKATU_CONTENT_TYPE_PLAIN);
    + break;
    + case PROP_AUTHOR:
    + g_value_set_string(value, purple_message_get_author(msg->msg));
    + break;
    + case PROP_CONTENTS:
    + g_value_set_string(value, purple_message_get_contents(msg->msg));
    + break;
    + case PROP_TIMESTAMP:
    + g_value_set_pointer(value, msg->timestamp);
    + break;
    + case PROP_EDITED:
    + g_value_set_boolean(value, FALSE);
    + break;
    + default:
    + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    + break;
    + }
    +}
    +
    +static void
    +pidgin_message_set_property(GObject *obj, guint param_id, const GValue *value, GParamSpec *pspec) {
    + PidginMessage *msg = PIDGIN_MESSAGE(obj);
    +
    + switch(param_id) {
    + case PROP_MESSAGE:
    + pidgin_message_set_message(msg, g_value_get_object(value));
    + break;
    + case PROP_ID:
    + case PROP_CONTENT_TYPE:
    + case PROP_TIMESTAMP:
    + case PROP_AUTHOR:
    + case PROP_CONTENTS:
    + case PROP_EDITED:
    + /* we don't allow settings these */
    + break;
    + default:
    + G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
    + break;
    + }
    +}
    +
    +static void
    +pidgin_message_init(PidginMessage *msg) {
    +}
    +
    +static void
    +pidgin_message_finalize(GObject *obj) {
    + PidginMessage *msg = PIDGIN_MESSAGE(obj);
    +
    + g_clear_object(&msg->msg);
    + g_clear_pointer(&msg->timestamp, g_date_time_unref);
    +
    + G_OBJECT_CLASS(pidgin_message_parent_class)->finalize(obj);
    +}
    +
    +static void
    +pidgin_message_class_init(PidginMessageClass *klass) {
    + GObjectClass *obj_class = G_OBJECT_CLASS(klass);
    +
    + obj_class->get_property = pidgin_message_get_property;
    + obj_class->set_property = pidgin_message_set_property;
    + obj_class->finalize = pidgin_message_finalize;
    +
    + /* add our custom properties */
    + properties[PROP_MESSAGE] = g_param_spec_object(
    + "message", "message", "The purple message",
    + PURPLE_TYPE_MESSAGE,
    + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY
    + );
    +
    + g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
    +
    + /* add our overridden properties */
    + g_object_class_override_property(obj_class, PROP_ID, "id");
    + g_object_class_override_property(obj_class, PROP_TIMESTAMP, "timestamp");
    + g_object_class_override_property(obj_class, PROP_CONTENT_TYPE, "content-type");
    + g_object_class_override_property(obj_class, PROP_AUTHOR, "author");
    + g_object_class_override_property(obj_class, PROP_CONTENTS, "contents");
    + g_object_class_override_property(obj_class, PROP_EDITED, "edited");
    +}
    +
    +/******************************************************************************
    + * API
    + *****************************************************************************/
    +PidginMessage *
    +pidgin_message_new(PurpleMessage *msg) {
    + return g_object_new(
    + PIDGIN_TYPE_MESSAGE,
    + "message", msg,
    + NULL
    + );
    +}
    +
    +PurpleMessage *
    +pidgin_message_get_message(PidginMessage *msg) {
    + g_return_val_if_fail(PIDGIN_IS_MESSAGE(msg), NULL);
    +
    + return msg->msg;
    +}
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/pidginmessage.h Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,63 @@
    +/* pidgin
    + *
    + * Pidgin is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + */
    +
    +#ifndef PIDGIN_MESSAGE_H
    +#define PIDGIN_MESSAGE_H
    +
    +/**
    + * SECTION:pidginmessage
    + * @section_id: pidgin-pidginmessage
    + * @short_description: <filename>pidginmessage.h</filename>
    + * @title: Pidgin Message API
    + */
    +
    +#include <purple.h>
    +
    +#include <talkatu.h>
    +
    +G_BEGIN_DECLS
    +
    +#define PIDGIN_TYPE_MESSAGE (pidgin_message_get_type())
    +G_DECLARE_FINAL_TYPE(PidginMessage, pidgin_message, PIDGIN, MESSAGE, GObject)
    +
    +/**
    + * pidgin_message_new:
    + * @msg: The #PurpleMessage to wrap.
    + *
    + * Wraps @msg so that it can be used as a #TalkatuMessage.
    + *
    + * Returns: (transfer full): The new #PidginMessage instance.
    + */
    +PidginMessage *pidgin_message_new(PurpleMessage *msg);
    +
    +/**
    + * pidgin_message_get_message:
    + * @msg: The #PidginMessage instance.
    + *
    + * Gets the #PurpleMessage that @msg is wrapping.
    + *
    + * Returns: (transfer none): The #PurpleMessage that @msg is wrapping.
    + */
    +PurpleMessage *pidgin_message_get_message(PidginMessage *msg);
    +
    +G_END_DECLS
    +
    +#endif /* PIDGIN_MESSAGE_H */
    --- a/pidgin/pidgintooltip.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/pidgintooltip.c Tue Oct 08 21:48:28 2019 -0500
    @@ -133,11 +133,11 @@
    GdkRectangle mon_size;
    GtkWidget *tipwindow = pidgin_tooltip.tipwindow;
    - GdkDeviceManager *devmgr;
    + GdkSeat *seat;
    GdkDevice *dev;
    - devmgr = gdk_display_get_device_manager(gdk_display_get_default());
    - dev = gdk_device_manager_get_client_pointer(devmgr);
    + seat = gdk_display_get_default_seat(gdk_display_get_default());
    + dev = gdk_seat_get_pointer(seat);
    gdk_device_get_position(dev, &screen, &x, &y);
    mon_num = gdk_screen_get_monitor_at_point(screen, x, y);
    --- a/pidgin/plugins/cap/cap.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/cap/cap.c Tue Oct 08 21:48:28 2019 -0500
    @@ -24,7 +24,7 @@
    static void generate_prediction(CapStatistics *statistics) {
    if(statistics->buddy) {
    if(statistics->prediction == NULL)
    - statistics->prediction = g_malloc(sizeof(CapPrediction));
    + statistics->prediction = g_new0(CapPrediction, 1);
    statistics->prediction->probability = generate_prediction_for(statistics->buddy);
    statistics->prediction->generated_at = time(NULL);
    }
    @@ -480,7 +480,7 @@
    PurpleAccount *account = purple_connection_get_account(gc);
    const char *my_purple_name = purple_account_get_username(account);
    gchar *my_name = g_strdup(my_purple_name);
    - time_t *offline_time = g_malloc(sizeof(time_t));
    + time_t *offline_time = g_new0(time_t, 1);
    const gchar *account_id = purple_account_get_username(account);
    const gchar *protocol_id = purple_account_get_protocol_id(account);
    char *sql;
    @@ -571,7 +571,7 @@
    return TRUE;
    /* build the path */
    - path = g_build_filename(purple_user_dir(), "cap.db", (gchar *)NULL);
    + path = g_build_filename(purple_data_dir(), "cap.db", (gchar *)NULL);
    /* make database connection here */
    rc = sqlite3_open(path, &_db);
    @@ -728,7 +728,7 @@
    }
    static CapPrefsUI * create_cap_prefs_ui() {
    - CapPrefsUI *ui = g_malloc(sizeof(CapPrefsUI));
    + CapPrefsUI *ui = g_new0(CapPrefsUI, 1);
    ui->ret = gtk_box_new(GTK_ORIENTATION_VERTICAL, 18);
    gtk_container_set_border_width(GTK_CONTAINER(ui->ret), 10);
    --- a/pidgin/plugins/cap/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/cap/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -1,9 +1,9 @@
    -#######################################################################
    -# Check for libsqlite3 (for the Contact Availability Prediction plugin)
    -#######################################################################
    -SQLITE3 = dependency('sqlite3', version : '>= 3.3', required : force_deps)
    +if PLUGINS
    + #######################################################################
    + # Check for libsqlite3 (for the Contact Availability Prediction plugin)
    + #######################################################################
    + SQLITE3 = dependency('sqlite3', version : '>= 3.3')
    -if PLUGINS and SQLITE3.found()
    cap = library('cap', 'cap.c', 'cap.h', 'cap_statistics.h',
    dependencies : [SQLITE3, libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    --- a/pidgin/plugins/contact_priority.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/contact_priority.c Tue Oct 08 21:48:28 2019 -0500
    @@ -26,21 +26,24 @@
    #include "prefs.h"
    #include "version.h"
    +#include "pidginaccountchooser.h"
    +
    #define CONTACT_PRIORITY_PLUGIN_ID "gtk-contact-priority"
    static void
    -select_account(GtkWidget *widget, PurpleAccount *account, gpointer data)
    +select_account(GtkWidget *chooser, gpointer data)
    {
    + PurpleAccount *account = pidgin_account_chooser_get_selected(chooser);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(data),
    (gdouble)purple_account_get_int(account, "score", 0));
    }
    static void
    -account_update(GtkWidget *widget, GtkWidget *optmenu)
    +account_update(GtkWidget *widget, GtkWidget *chooser)
    {
    PurpleAccount *account = NULL;
    - account = pidgin_account_option_menu_get_selected(optmenu);
    + account = pidgin_account_chooser_get_selected(chooser);
    purple_account_set_int(account, "score", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
    }
    @@ -75,7 +78,7 @@
    {
    GtkWidget *ret = NULL, *hbox = NULL, *frame = NULL, *vbox = NULL;
    GtkWidget *label = NULL, *spin = NULL, *check = NULL;
    - GtkWidget *optmenu = NULL;
    + GtkWidget *chooser = NULL;
    GtkAdjustment *adj = NULL;
    GtkSizeGroup *sg = NULL;
    PurpleAccount *account = NULL;
    @@ -144,18 +147,17 @@
    adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -500, 500, 1, 1, 1));
    spin = gtk_spin_button_new(adj, 1, 0);
    - optmenu = pidgin_account_option_menu_new(NULL, TRUE,
    - G_CALLBACK(select_account),
    - NULL, spin);
    - gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
    + chooser = pidgin_account_chooser_new(NULL, TRUE);
    + gtk_box_pack_start(GTK_BOX(hbox), chooser, FALSE, FALSE, 0);
    + g_signal_connect(chooser, "changed", G_CALLBACK(select_account), spin);
    /* this is where we set up the spin button we made above */
    - account = pidgin_account_option_menu_get_selected(optmenu);
    + account = pidgin_account_chooser_get_selected(chooser);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin),
    (gdouble)purple_account_get_int(account, "score", 0));
    gtk_spin_button_set_adjustment(GTK_SPIN_BUTTON(spin), GTK_ADJUSTMENT(adj));
    g_signal_connect(G_OBJECT(spin), "value-changed",
    - G_CALLBACK(account_update), optmenu);
    + G_CALLBACK(account_update), chooser);
    gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0);
    gtk_widget_show_all(ret);
    --- a/pidgin/plugins/disco/gtkdisco.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/disco/gtkdisco.c Tue Oct 08 21:48:28 2019 -0500
    @@ -29,6 +29,7 @@
    #include "gtkutils.h"
    #include "pidgin.h"
    #include "request.h"
    +#include "pidginaccountchooser.h"
    #include "pidgintooltip.h"
    #include "gtk3compat.h"
    @@ -52,11 +53,6 @@
    if (list->dialog && list->dialog->discolist == list)
    list->dialog->discolist = NULL;
    - if (list->tree) {
    - gtk_widget_destroy(list->tree);
    - list->tree = NULL;
    - }
    -
    g_free((gchar*)list->server);
    g_free(list);
    }
    @@ -92,13 +88,13 @@
    list->in_progress = in_progress;
    if (in_progress) {
    - gtk_widget_set_sensitive(dialog->account_widget, FALSE);
    + gtk_widget_set_sensitive(dialog->account_chooser, FALSE);
    gtk_widget_set_sensitive(dialog->stop_button, TRUE);
    gtk_widget_set_sensitive(dialog->browse_button, FALSE);
    } else {
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->progress), 0.0);
    - gtk_widget_set_sensitive(dialog->account_widget, TRUE);
    + gtk_widget_set_sensitive(dialog->account_chooser, TRUE);
    gtk_widget_set_sensitive(dialog->stop_button, FALSE);
    gtk_widget_set_sensitive(dialog->browse_button, TRUE);
    @@ -150,22 +146,16 @@
    return pixbuf;
    }
    -static void pidgin_disco_create_tree(PidginDiscoList *pdl);
    -
    -static void dialog_select_account_cb(GObject *w, PurpleAccount *account,
    - PidginDiscoDialog *dialog)
    +static void
    +dialog_select_account_cb(GtkWidget *chooser, PidginDiscoDialog *dialog)
    {
    + PurpleAccount *account = pidgin_account_chooser_get_selected(chooser);
    gboolean change = (account != dialog->account);
    dialog->account = account;
    gtk_widget_set_sensitive(dialog->browse_button, account != NULL);
    - if (change && dialog->discolist) {
    - if (dialog->discolist->tree) {
    - gtk_widget_destroy(dialog->discolist->tree);
    - dialog->discolist->tree = NULL;
    - }
    - pidgin_disco_list_unref(dialog->discolist);
    - dialog->discolist = NULL;
    + if (change) {
    + g_clear_pointer(&dialog->discolist, pidgin_disco_list_unref);
    }
    }
    @@ -217,13 +207,8 @@
    gtk_widget_set_sensitive(dialog->add_button, FALSE);
    gtk_widget_set_sensitive(dialog->register_button, FALSE);
    - if (dialog->discolist != NULL) {
    - if (dialog->discolist->tree) {
    - gtk_widget_destroy(dialog->discolist->tree);
    - dialog->discolist->tree = NULL;
    - }
    - pidgin_disco_list_unref(dialog->discolist);
    - }
    + g_clear_pointer(&dialog->discolist, pidgin_disco_list_unref);
    + gtk_tree_store_clear(dialog->model);
    pdl = dialog->discolist = g_new0(PidginDiscoList, 1);
    pdl->services = g_hash_table_new_full(NULL, NULL, NULL,
    @@ -233,10 +218,8 @@
    pidgin_disco_list_ref(pdl);
    pdl->dialog = dialog;
    - pidgin_disco_create_tree(pdl);
    - if (dialog->account_widget)
    - gtk_widget_set_sensitive(dialog->account_widget, FALSE);
    + gtk_widget_set_sensitive(dialog->account_chooser, FALSE);
    username = purple_account_get_username(dialog->account);
    at = strchr(username, '@');
    @@ -283,7 +266,7 @@
    static gboolean
    service_click_cb(GtkTreeView *tree, GdkEventButton *event, gpointer user_data)
    {
    - PidginDiscoList *pdl;
    + PidginDiscoDialog *dialog = user_data;
    XmppDiscoService *service;
    GtkWidget *menu;
    @@ -294,17 +277,15 @@
    if (!gdk_event_triggers_context_menu((GdkEvent *)event))
    return FALSE;
    - pdl = user_data;
    -
    /* Figure out what was clicked */
    if (!gtk_tree_view_get_path_at_pos(tree, event->x, event->y, &path,
    NULL, NULL, NULL))
    return FALSE;
    - gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &iter, path);
    + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter, path);
    gtk_tree_path_free(path);
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN,
    - &val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    + SERVICE_COLUMN, &val);
    service = g_value_get_pointer(&val);
    if (!service)
    @@ -312,15 +293,16 @@
    menu = gtk_menu_new();
    - if (service->flags & XMPP_DISCO_ADD)
    + if (service->flags & XMPP_DISCO_ADD) {
    pidgin_new_menu_item(menu, _("Add to Buddy List"), NULL,
    - G_CALLBACK(add_to_blist_cb), pdl->dialog);
    + G_CALLBACK(add_to_blist_cb), dialog);
    + }
    if (service->flags & XMPP_DISCO_REGISTER) {
    GtkWidget *item = pidgin_new_menu_item(menu, _("Register"),
    NULL, NULL, NULL);
    g_signal_connect(G_OBJECT(item), "activate",
    - G_CALLBACK(register_button_cb), pdl->dialog);
    + G_CALLBACK(register_button_cb), dialog);
    }
    gtk_widget_show_all(menu);
    @@ -329,15 +311,15 @@
    }
    static void
    -selection_changed_cb(GtkTreeSelection *selection, PidginDiscoList *pdl)
    +selection_changed_cb(GtkTreeSelection *selection, PidginDiscoDialog *dialog)
    {
    GtkTreeIter iter;
    GValue val;
    - PidginDiscoDialog *dialog = pdl->dialog;
    if (gtk_tree_selection_get_selected(selection, NULL, &iter)) {
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN, &val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    + SERVICE_COLUMN, &val);
    dialog->selected = g_value_get_pointer(&val);
    if (!dialog->selected) {
    gtk_widget_set_sensitive(dialog->add_button, FALSE);
    @@ -357,15 +339,13 @@
    row_expanded_cb(GtkTreeView *tree, GtkTreeIter *arg1, GtkTreePath *rg2,
    gpointer user_data)
    {
    - PidginDiscoList *pdl;
    + PidginDiscoDialog *dialog = user_data;
    XmppDiscoService *service;
    GValue val;
    - pdl = user_data;
    -
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), arg1, SERVICE_COLUMN,
    - &val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), arg1,
    + SERVICE_COLUMN, &val);
    service = g_value_get_pointer(&val);
    xmpp_disco_service_expand(service);
    }
    @@ -376,34 +356,41 @@
    GtkTreeViewColumn *column,
    gpointer user_data)
    {
    - PidginDiscoList *pdl = user_data;
    + PidginDiscoDialog *dialog = user_data;
    GtkTreeIter iter;
    XmppDiscoService *service;
    GValue val;
    - if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &iter, path))
    + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter,
    + path)) {
    return;
    + }
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN,
    - &val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    + SERVICE_COLUMN, &val);
    service = g_value_get_pointer(&val);
    - if (service->flags & XMPP_DISCO_BROWSE)
    - if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(pdl->tree), path))
    - gtk_tree_view_collapse_row(GTK_TREE_VIEW(pdl->tree), path);
    - else
    - gtk_tree_view_expand_row(GTK_TREE_VIEW(pdl->tree), path, FALSE);
    - else if (service->flags & XMPP_DISCO_REGISTER)
    - register_button_cb(NULL, pdl->dialog);
    - else if (service->flags & XMPP_DISCO_ADD)
    - add_to_blist_cb(NULL, pdl->dialog);
    + if (service->flags & XMPP_DISCO_BROWSE) {
    + if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(dialog->tree),
    + path)) {
    + gtk_tree_view_collapse_row(GTK_TREE_VIEW(dialog->tree),
    + path);
    + } else {
    + gtk_tree_view_expand_row(GTK_TREE_VIEW(dialog->tree),
    + path, FALSE);
    + }
    + } else if (service->flags & XMPP_DISCO_REGISTER) {
    + register_button_cb(NULL, dialog);
    + } else if (service->flags & XMPP_DISCO_ADD) {
    + add_to_blist_cb(NULL, dialog);
    + }
    }
    static void
    -destroy_win_cb(GtkWidget *window, gpointer d)
    +destroy_win_cb(GtkWidget *window, G_GNUC_UNUSED gpointer data)
    {
    - PidginDiscoDialog *dialog = d;
    + PidginDiscoDialog *dialog = PIDGIN_DISCO_DIALOG(window);
    PidginDiscoList *list = dialog->discolist;
    if (dialog->prompt_handle)
    @@ -418,8 +405,7 @@
    pidgin_disco_list_unref(list);
    }
    - dialogs = g_list_remove(dialogs, d);
    - g_free(dialog);
    + dialogs = g_list_remove(dialogs, dialog);
    }
    static void stop_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
    @@ -429,9 +415,7 @@
    static void close_button_cb(GtkButton *button, PidginDiscoDialog *dialog)
    {
    - GtkWidget *window = dialog->window;
    -
    - gtk_widget_destroy(window);
    + gtk_widget_destroy(GTK_WIDGET(dialog));
    }
    static gboolean account_filter_func(PurpleAccount *account)
    @@ -453,7 +437,7 @@
    disco_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
    gpointer data, int *w, int *h)
    {
    - PidginDiscoList *pdl = data;
    + PidginDiscoDialog *dialog = data;
    GtkTreeIter iter;
    PangoLayout *layout;
    int width, height;
    @@ -462,12 +446,14 @@
    const char *type = NULL;
    char *markup, *jid, *name, *desc = NULL;
    - if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &iter, path))
    + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model), &iter,
    + path)) {
    return FALSE;
    + }
    val.g_type = 0;
    - gtk_tree_model_get_value(GTK_TREE_MODEL(pdl->model), &iter, SERVICE_COLUMN,
    - &val);
    + gtk_tree_model_get_value(GTK_TREE_MODEL(dialog->model), &iter,
    + SERVICE_COLUMN, &val);
    service = g_value_get_pointer(&val);
    if (!service)
    return FALSE;
    @@ -529,69 +515,6 @@
    return TRUE;
    }
    -static void pidgin_disco_create_tree(PidginDiscoList *pdl)
    -{
    - GtkCellRenderer *text_renderer, *pixbuf_renderer;
    - GtkTreeViewColumn *column;
    - GtkTreeSelection *selection;
    -
    - pdl->model = gtk_tree_store_new(NUM_OF_COLUMNS,
    - GDK_TYPE_PIXBUF, /* PIXBUF_COLUMN */
    - G_TYPE_STRING, /* NAME_COLUMN */
    - G_TYPE_STRING, /* DESCRIPTION_COLUMN */
    - G_TYPE_POINTER /* SERVICE_COLUMN */
    - );
    -
    - pdl->tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pdl->model));
    -
    - selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pdl->tree));
    - g_signal_connect(G_OBJECT(selection), "changed",
    - G_CALLBACK(selection_changed_cb), pdl);
    -
    - g_object_unref(pdl->model);
    -
    - gtk_container_add(GTK_CONTAINER(pdl->dialog->sw), pdl->tree);
    - gtk_widget_show(pdl->tree);
    -
    - text_renderer = gtk_cell_renderer_text_new();
    - pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
    -
    - column = gtk_tree_view_column_new();
    - gtk_tree_view_column_set_title(column, _("Name"));
    -
    - gtk_tree_view_column_pack_start(column, pixbuf_renderer, FALSE);
    - gtk_tree_view_column_set_attributes(column, pixbuf_renderer,
    - "pixbuf", PIXBUF_COLUMN, NULL);
    -
    - gtk_tree_view_column_pack_start(column, text_renderer, TRUE);
    - gtk_tree_view_column_set_attributes(column, text_renderer,
    - "text", NAME_COLUMN, NULL);
    -
    - gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
    - GTK_TREE_VIEW_COLUMN_GROW_ONLY);
    - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), NAME_COLUMN);
    - gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(pdl->tree), column);
    -
    - column = gtk_tree_view_column_new_with_attributes(_("Description"), text_renderer,
    - "text", DESCRIPTION_COLUMN, NULL);
    - gtk_tree_view_column_set_sizing(GTK_TREE_VIEW_COLUMN(column),
    - GTK_TREE_VIEW_COLUMN_GROW_ONLY);
    - gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column), DESCRIPTION_COLUMN);
    - gtk_tree_view_column_set_reorderable(GTK_TREE_VIEW_COLUMN(column), TRUE);
    - gtk_tree_view_append_column(GTK_TREE_VIEW(pdl->tree), column);
    -
    - g_signal_connect(G_OBJECT(pdl->tree), "button-press-event", G_CALLBACK(service_click_cb), pdl);
    - g_signal_connect(G_OBJECT(pdl->tree), "row-expanded", G_CALLBACK(row_expanded_cb), pdl);
    - g_signal_connect(G_OBJECT(pdl->tree), "row-activated", G_CALLBACK(row_activated_cb), pdl);
    -
    - pidgin_tooltip_setup_for_treeview(pdl->tree, pdl,
    - disco_create_tooltip,
    - disco_paint_tooltip);
    -}
    -
    void pidgin_disco_signed_off_cb(PurpleConnection *pc)
    {
    GList *node;
    @@ -604,16 +527,15 @@
    if (list->in_progress)
    pidgin_disco_list_set_in_progress(list, FALSE);
    - if (list->tree) {
    - gtk_widget_destroy(list->tree);
    - list->tree = NULL;
    - }
    + gtk_tree_store_clear(dialog->model);
    pidgin_disco_list_unref(list);
    dialog->discolist = NULL;
    - gtk_widget_set_sensitive(dialog->browse_button,
    - pidgin_account_option_menu_get_selected(dialog->account_widget) != NULL);
    + gtk_widget_set_sensitive(
    + dialog->browse_button,
    + pidgin_account_chooser_get_selected(
    + dialog->account_chooser) != NULL);
    gtk_widget_set_sensitive(dialog->register_button, FALSE);
    gtk_widget_set_sensitive(dialog->add_button, FALSE);
    @@ -624,87 +546,103 @@
    void pidgin_disco_dialogs_destroy_all(void)
    {
    while (dialogs) {
    - PidginDiscoDialog *dialog = dialogs->data;
    + GtkWidget *dialog = dialogs->data;
    - gtk_widget_destroy(dialog->window);
    + gtk_widget_destroy(dialog);
    /* destroy_win_cb removes the dialog from the list */
    }
    }
    -PidginDiscoDialog *pidgin_disco_dialog_new(void)
    +/******************************************************************************
    + * GObject implementation
    + *****************************************************************************/
    +
    +G_DEFINE_DYNAMIC_TYPE(PidginDiscoDialog, pidgin_disco_dialog, GTK_TYPE_DIALOG)
    +
    +static void
    +pidgin_disco_dialog_class_init(PidginDiscoDialogClass *klass)
    {
    - PidginDiscoDialog *dialog;
    - GtkWidget *window, *vbox, *vbox2, *bbox;
    + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    +
    + gtk_widget_class_set_template_from_resource(
    + widget_class, "/im/pidgin/Pidgin/Plugin/XMPPDisco/disco.ui");
    +
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + account_chooser);
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + progress);
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + stop_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + browse_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + register_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + add_button);
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + tree);
    + gtk_widget_class_bind_template_child(widget_class, PidginDiscoDialog,
    + model);
    - dialog = g_new0(PidginDiscoDialog, 1);
    + gtk_widget_class_bind_template_callback(widget_class, destroy_win_cb);
    + gtk_widget_class_bind_template_callback(widget_class, stop_button_cb);
    + gtk_widget_class_bind_template_callback(widget_class, browse_button_cb);
    + gtk_widget_class_bind_template_callback(widget_class,
    + register_button_cb);
    + gtk_widget_class_bind_template_callback(widget_class, add_to_blist_cb);
    + gtk_widget_class_bind_template_callback(widget_class, close_button_cb);
    + gtk_widget_class_bind_template_callback(widget_class,
    + dialog_select_account_cb);
    + gtk_widget_class_bind_template_callback(widget_class, row_activated_cb);
    + gtk_widget_class_bind_template_callback(widget_class, row_expanded_cb);
    + gtk_widget_class_bind_template_callback(widget_class, service_click_cb);
    + gtk_widget_class_bind_template_callback(widget_class,
    + selection_changed_cb);
    +}
    +
    +static void
    +pidgin_disco_dialog_class_finalize(PidginDiscoDialogClass *klass)
    +{
    +}
    +
    +static void
    +pidgin_disco_dialog_init(PidginDiscoDialog *dialog)
    +{
    dialogs = g_list_prepend(dialogs, dialog);
    - /* Create the window. */
    - dialog->window = window = pidgin_create_dialog(_("Service Discovery"), PIDGIN_HIG_BORDER, "service discovery", TRUE);
    -
    - g_signal_connect(G_OBJECT(window), "destroy",
    - G_CALLBACK(destroy_win_cb), dialog);
    -
    - /* Create the parent vbox for everything. */
    - vbox = pidgin_dialog_get_vbox_with_properties(GTK_DIALOG(window), FALSE, PIDGIN_HIG_BORDER);
    -
    - vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, PIDGIN_HIG_BORDER);
    - gtk_container_add(GTK_CONTAINER(vbox), vbox2);
    - gtk_widget_show(vbox2);
    + gtk_widget_init_template(GTK_WIDGET(dialog));
    /* accounts dropdown list */
    - dialog->account_widget = pidgin_account_option_menu_new(NULL, FALSE,
    - G_CALLBACK(dialog_select_account_cb), account_filter_func, dialog);
    - dialog->account = pidgin_account_option_menu_get_selected(dialog->account_widget);
    - pidgin_add_widget_to_vbox(GTK_BOX(vbox2), _("_Account:"), NULL, dialog->account_widget, TRUE, NULL);
    -
    - /* scrolled window */
    - dialog->sw = pidgin_make_scrollable(NULL, GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS, GTK_SHADOW_IN, -1, 250);
    - gtk_box_pack_start(GTK_BOX(vbox2), dialog->sw, TRUE, TRUE, 0);
    -
    - /* progress bar */
    - dialog->progress = gtk_progress_bar_new();
    - gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(dialog->progress), 0.1);
    - gtk_box_pack_start(GTK_BOX(vbox2), dialog->progress, FALSE, FALSE, 0);
    - gtk_widget_show(dialog->progress);
    -
    - /* button box */
    - bbox = pidgin_dialog_get_action_area(GTK_DIALOG(window));
    - gtk_box_set_spacing(GTK_BOX(bbox), PIDGIN_HIG_BOX_SPACE);
    - gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
    -
    - /* stop button */
    - dialog->stop_button =
    - pidgin_dialog_add_button(GTK_DIALOG(window), _("_Stop"),
    - G_CALLBACK(stop_button_cb), dialog);
    - gtk_widget_set_sensitive(dialog->stop_button, FALSE);
    + pidgin_account_chooser_set_filter_func(
    + PIDGIN_ACCOUNT_CHOOSER(dialog->account_chooser),
    + account_filter_func);
    + dialog->account =
    + pidgin_account_chooser_get_selected(dialog->account_chooser);
    /* browse button */
    - dialog->browse_button =
    - pidgin_dialog_add_button(GTK_DIALOG(window), _("_Browse"),
    - G_CALLBACK(browse_button_cb), dialog);
    gtk_widget_set_sensitive(dialog->browse_button, dialog->account != NULL);
    - /* register button */
    - dialog->register_button =
    - pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("Register"),
    - G_CALLBACK(register_button_cb), dialog);
    - gtk_widget_set_sensitive(dialog->register_button, FALSE);
    + pidgin_tooltip_setup_for_treeview(GTK_WIDGET(dialog->tree), dialog,
    + disco_create_tooltip,
    + disco_paint_tooltip);
    +}
    +
    +/******************************************************************************
    + * Public API
    + *****************************************************************************/
    - /* add button */
    - dialog->add_button =
    - pidgin_dialog_add_button(GTK_DIALOG(dialog->window), _("_Add"),
    - G_CALLBACK(add_to_blist_cb), dialog);
    - gtk_widget_set_sensitive(dialog->add_button, FALSE);
    +void
    +pidgin_disco_dialog_register(PurplePlugin *plugin)
    +{
    + pidgin_disco_dialog_register_type(G_TYPE_MODULE(plugin));
    +}
    - /* close button */
    - dialog->close_button =
    - pidgin_dialog_add_button(GTK_DIALOG(window), _("_Close"),
    - G_CALLBACK(close_button_cb), dialog);
    -
    - /* show the dialog window and return the dialog */
    - gtk_widget_show(dialog->window);
    -
    +PidginDiscoDialog *
    +pidgin_disco_dialog_new(void)
    +{
    + PidginDiscoDialog *dialog =
    + g_object_new(PIDGIN_TYPE_DISCO_DIALOG, NULL);
    + gtk_widget_show_all(GTK_WIDGET(dialog));
    return dialog;
    }
    @@ -732,14 +670,17 @@
    rr = g_hash_table_lookup(pdl->services, parent);
    path = gtk_tree_row_reference_get_path(rr);
    if (path) {
    - gtk_tree_model_get_iter(GTK_TREE_MODEL(pdl->model), &parent_iter, path);
    + gtk_tree_model_get_iter(GTK_TREE_MODEL(dialog->model),
    + &parent_iter, path);
    gtk_tree_path_free(path);
    - if (gtk_tree_model_iter_children(GTK_TREE_MODEL(pdl->model), &child,
    - &parent_iter)) {
    + if (gtk_tree_model_iter_children(
    + GTK_TREE_MODEL(dialog->model), &child,
    + &parent_iter)) {
    PidginDiscoList *tmp;
    - gtk_tree_model_get(GTK_TREE_MODEL(pdl->model), &child,
    - SERVICE_COLUMN, &tmp, -1);
    + gtk_tree_model_get(
    + GTK_TREE_MODEL(dialog->model), &child,
    + SERVICE_COLUMN, &tmp, -1);
    if (!tmp)
    append = FALSE;
    }
    @@ -748,35 +689,36 @@
    if (service == NULL) {
    if (parent != NULL && !append)
    - gtk_tree_store_remove(pdl->model, &child);
    + gtk_tree_store_remove(dialog->model, &child);
    return;
    }
    - if (append)
    - gtk_tree_store_append(pdl->model, &iter, (parent ? &parent_iter : NULL));
    - else
    + if (append) {
    + gtk_tree_store_append(dialog->model, &iter,
    + (parent ? &parent_iter : NULL));
    + } else {
    iter = child;
    + }
    if (service->flags & XMPP_DISCO_BROWSE) {
    GtkTreeRowReference *rr;
    GtkTreePath *path;
    - gtk_tree_store_append(pdl->model, &child, &iter);
    + gtk_tree_store_append(dialog->model, &child, &iter);
    - path = gtk_tree_model_get_path(GTK_TREE_MODEL(pdl->model), &iter);
    - rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(pdl->model), path);
    + path = gtk_tree_model_get_path(GTK_TREE_MODEL(dialog->model),
    + &iter);
    + rr = gtk_tree_row_reference_new(GTK_TREE_MODEL(dialog->model),
    + path);
    g_hash_table_insert(pdl->services, service, rr);
    gtk_tree_path_free(path);
    }
    pixbuf = pidgin_disco_load_icon(service, "16");
    - gtk_tree_store_set(pdl->model, &iter,
    - PIXBUF_COLUMN, pixbuf,
    - NAME_COLUMN, service->name,
    - DESCRIPTION_COLUMN, service->description,
    - SERVICE_COLUMN, service,
    - -1);
    + gtk_tree_store_set(dialog->model, &iter, PIXBUF_COLUMN, pixbuf,
    + NAME_COLUMN, service->name, DESCRIPTION_COLUMN,
    + service->description, SERVICE_COLUMN, service, -1);
    if (pixbuf)
    g_object_unref(pixbuf);
    --- a/pidgin/plugins/disco/gtkdisco.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/disco/gtkdisco.h Tue Oct 08 21:48:28 2019 -0500
    @@ -22,31 +22,39 @@
    #ifndef PIDGIN_XMPP_DISCO_UI_H
    #define PIDGIN_XMPP_DISCO_UI_H
    -typedef struct _PidginDiscoDialog PidginDiscoDialog;
    +#include <gtk/gtk.h>
    +
    typedef struct _PidginDiscoList PidginDiscoList;
    #include "xmppdisco.h"
    +G_BEGIN_DECLS
    +
    struct _PidginDiscoDialog {
    - GtkWidget *window;
    - GtkWidget *account_widget;
    + GtkDialog parent;
    - GtkWidget *sw;
    + GtkWidget *account_chooser;
    +
    GtkWidget *progress;
    GtkWidget *stop_button;
    GtkWidget *browse_button;
    GtkWidget *register_button;
    GtkWidget *add_button;
    - GtkWidget *close_button;
    XmppDiscoService *selected;
    + GtkTreeView *tree;
    + GtkTreeStore *model;
    PurpleAccount *account;
    PidginDiscoList *discolist;
    gpointer *prompt_handle;
    };
    +#define PIDGIN_TYPE_DISCO_DIALOG (pidgin_disco_dialog_get_type())
    +G_DECLARE_FINAL_TYPE(PidginDiscoDialog, pidgin_disco_dialog, PIDGIN,
    + DISCO_DIALOG, GtkDialog)
    +
    struct _PidginDiscoList {
    PurpleConnection *pc;
    gboolean in_progress;
    @@ -56,12 +64,15 @@
    guint fetch_count;
    PidginDiscoDialog *dialog;
    - GtkTreeStore *model;
    - GtkWidget *tree;
    GHashTable *services;
    };
    /**
    + * Registers dynamic GObjects.
    + */
    +void pidgin_disco_dialog_register(PurplePlugin *plugin);
    +
    +/**
    * Shows a new service discovery dialog.
    */
    PidginDiscoDialog *pidgin_disco_dialog_new(void);
    @@ -79,4 +90,7 @@
    void pidgin_disco_list_unref(PidginDiscoList *list);
    void pidgin_disco_list_set_in_progress(PidginDiscoList *list, gboolean in_progress);
    +
    +G_END_DECLS
    +
    #endif /* PIDGIN_XMPP_DISCO_UI_H */
    --- a/pidgin/plugins/disco/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/disco/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -6,6 +6,12 @@
    ]
    if PLUGINS
    + xmppdisco_resource = gnome.compile_resources('xmppdiscoresources',
    + 'resources/xmppdisco.gresource.xml',
    + source_dir : 'resources',
    + c_name : 'xmppdisco')
    + xmppdisco_SOURCES += xmppdisco_resource
    +
    xmppdisco = library('xmppdisco', xmppdisco_SOURCES,
    dependencies : [libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/plugins/disco/resources/disco.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,248 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!-- Generated with glade 3.22.1 -->
    +<interface>
    + <requires lib="gtk+" version="3.18"/>
    + <requires lib="pidgin" version="3.0"/>
    + <object class="GtkTreeStore" id="model">
    + <columns>
    + <!-- column-name pixbuf -->
    + <column type="GdkPixbuf"/>
    + <!-- column-name name -->
    + <column type="gchararray"/>
    + <!-- column-name description -->
    + <column type="gchararray"/>
    + <!-- column-name service -->
    + <column type="gpointer"/>
    + </columns>
    + </object>
    + <template class="PidginDiscoDialog" parent="GtkDialog">
    + <property name="can_focus">False</property>
    + <property name="title" translatable="yes">Service Discovery</property>
    + <property name="role">service discovery</property>
    + <property name="type_hint">dialog</property>
    + <signal name="destroy" handler="destroy_win_cb" swapped="no"/>
    + <child>
    + <placeholder/>
    + </child>
    + <child internal-child="vbox">
    + <object class="GtkBox">
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">2</property>
    + <child internal-child="action_area">
    + <object class="GtkButtonBox">
    + <property name="can_focus">False</property>
    + <property name="layout_style">end</property>
    + <child>
    + <object class="GtkButton" id="stop_button">
    + <property name="label" translatable="yes">_Stop</property>
    + <property name="visible">True</property>
    + <property name="sensitive">False</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="stop_button_cb" object="PidginDiscoDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="browse_button">
    + <property name="label" translatable="yes">_Browse</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="browse_button_cb" object="PidginDiscoDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="register_button">
    + <property name="label" translatable="yes">Register</property>
    + <property name="visible">True</property>
    + <property name="sensitive">False</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <signal name="clicked" handler="register_button_cb" object="PidginDiscoDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="add_button">
    + <property name="label" translatable="yes">_Add</property>
    + <property name="visible">True</property>
    + <property name="sensitive">False</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="add_to_blist_cb" object="PidginDiscoDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="close_button">
    + <property name="label" translatable="yes">_Close</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="close_button_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">4</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Account:</property>
    + <property name="use_underline">True</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="PidginAccountChooser" id="account_chooser">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <signal name="changed" handler="dialog_select_account_cb" object="PidginDiscoDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkScrolledWindow">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="hscrollbar_policy">always</property>
    + <property name="vscrollbar_policy">always</property>
    + <property name="shadow_type">in</property>
    + <property name="min_content_height">250</property>
    + <child>
    + <object class="GtkTreeView" id="tree">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="model">model</property>
    + <signal name="button-press-event" handler="service_click_cb" object="PidginDiscoDialog" swapped="no"/>
    + <signal name="row-activated" handler="row_activated_cb" object="PidginDiscoDialog" swapped="no"/>
    + <signal name="row-expanded" handler="row_expanded_cb" object="PidginDiscoDialog" swapped="no"/>
    + <child internal-child="selection">
    + <object class="GtkTreeSelection">
    + <signal name="changed" handler="selection_changed_cb" swapped="no"/>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="resizable">True</property>
    + <property name="title" translatable="yes">Name</property>
    + <property name="reorderable">True</property>
    + <property name="sort_column_id">1</property>
    + <child>
    + <object class="GtkCellRendererPixbuf"/>
    + <attributes>
    + <attribute name="pixbuf">0</attribute>
    + </attributes>
    + </child>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">1</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="resizable">True</property>
    + <property name="title" translatable="yes">Description</property>
    + <property name="reorderable">True</property>
    + <property name="sort_column_id">2</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">2</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkProgressBar" id="progress">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </template>
    +</interface>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/plugins/disco/resources/xmppdisco.gresource.xml Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,6 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<gresources>
    + <gresource prefix="/im/pidgin/Pidgin/Plugin/XMPPDisco/">
    + <file compressed="true">disco.ui</file>
    + </gresource>
    +</gresources>
    --- a/pidgin/plugins/disco/xmppdisco.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/disco/xmppdisco.c Tue Oct 08 21:48:28 2019 -0500
    @@ -645,10 +645,13 @@
    xmpp_protocol = purple_protocols_find(XMPP_PROTOCOL_ID);
    if (NULL == xmpp_protocol) {
    - g_set_error(error, PLUGIN_DOMAIN, 0, _("XMPP protocol is not loaded."));
    + g_set_error_literal(error, PLUGIN_DOMAIN, 0,
    + _("XMPP protocol is not loaded."));
    return FALSE;
    }
    + pidgin_disco_dialog_register(plugin);
    +
    purple_signal_connect(purple_connections_get_handle(), "signing-off",
    plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
    --- a/pidgin/plugins/gestures/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gestures/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -8,7 +8,8 @@
    if PLUGINS
    gestures = library('gestures', gestures_SOURCES,
    + build_by_default: false,
    dependencies : [x11, libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    + install : false, install_dir : PIDGIN_PLUGINDIR)
    endif
    --- a/pidgin/plugins/gestures/stroke-draw.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gestures/stroke-draw.c Tue Oct 08 21:48:28 2019 -0500
    @@ -57,13 +57,13 @@
    {
    gint x, y;
    struct gstroke_metrics *metrics;
    - GdkDeviceManager *devmgr;
    + GdkSeat *seat;
    GdkDevice *dev;
    g_return_if_fail(widget != NULL);
    - devmgr = gdk_display_get_device_manager(gtk_widget_get_display(widget));
    - dev = gdk_device_manager_get_client_pointer(devmgr);
    + seat = gdk_display_get_default_seat(gtk_widget_get_display(widget));
    + dev = gdk_seat_get_pointer(seat);
    gdk_window_get_device_position(gtk_widget_get_window(widget),
    dev, &x, &y, NULL);
    @@ -118,8 +118,9 @@
    timer_id = 0;
    - if( event != NULL )
    - gdk_device_ungrab(gdk_event_get_device(event), event->button.time);
    + if (event != NULL) {
    + gdk_seat_ungrab(gdk_event_get_seat(event));
    + }
    if (gstroke_draw_strokes() && gstroke_disp != NULL) {
    /* get rid of the invisible stroke window */
    @@ -159,10 +160,9 @@
    cursor = gdk_cursor_new_for_display(display, GDK_PENCIL);
    }
    - gdk_device_grab(gdk_event_get_device(event),
    - gtk_widget_get_window(widget), GDK_OWNERSHIP_WINDOW,
    - FALSE, GDK_BUTTON_RELEASE_MASK, cursor,
    - event->button.time);
    + gdk_seat_grab(gdk_event_get_seat(event), gtk_widget_get_window(widget),
    + GDK_SEAT_CAPABILITY_ALL_POINTING, FALSE, cursor, event,
    + NULL, NULL);
    timer_id = g_timeout_add (GSTROKE_TIMEOUT_DURATION,
    gstroke_timeout, widget);
    return TRUE;
    @@ -181,7 +181,7 @@
    last_mouse_position.invalid = TRUE;
    original_widget = NULL;
    g_source_remove (timer_id);
    - gdk_device_ungrab(gdk_event_get_device(event), event->button.time);
    + gdk_seat_ungrab(gdk_event_get_seat(event));
    timer_id = 0;
    {
    --- a/pidgin/plugins/gestures/stroke.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gestures/stroke.c Tue Oct 08 21:48:28 2019 -0500
    @@ -22,12 +22,11 @@
    void
    _gstroke_init (struct gstroke_metrics *metrics)
    {
    - if (metrics->pointList != NULL) {
    - /* FIXME: does this free the data too?*/
    - g_slist_free (metrics->pointList);
    - metrics->pointList = NULL;
    - metrics->point_count = 0;
    - }
    + if (metrics->pointList != NULL) {
    + g_slist_free_full(metrics->pointList, g_free);
    + metrics->pointList = NULL;
    + metrics->point_count = 0;
    + }
    }
    /* figure out which bin the point falls in */
    @@ -197,8 +196,6 @@
    #endif
    if (metrics->point_count < GSTROKE_MAX_POINTS) {
    - new_point_p = g_malloc(sizeof (struct s_point));
    -
    if (metrics->pointList == NULL) {
    /* first point in list - initialize metrics */
    @@ -207,27 +204,28 @@
    metrics->max_x = -1;
    metrics->max_y = -1;
    + new_point_p = g_new0(struct s_point, 1);
    metrics->pointList = g_slist_prepend(metrics->pointList, new_point_p);
    metrics->point_count = 0;
    } else {
    -
    -#define LAST_POINT ((p_point)(g_slist_last (metrics->pointList)->data))
    + p_point last_point = (p_point)g_slist_last(metrics->pointList)->data;
    /* interpolate between last and current point */
    - delx = x - LAST_POINT->x;
    - dely = y - LAST_POINT->y;
    + delx = x - last_point->x;
    + dely = y - last_point->y;
    if (abs(delx) > abs(dely)) { /* step by the greatest delta direction */
    - iy = LAST_POINT->y;
    + iy = last_point->y;
    /* go from the last point to the current, whatever direction it may be */
    - for (ix = LAST_POINT->x; (delx > 0) ? (ix < x) : (ix > x); ix += (delx > 0) ? 1 : -1) {
    + for (ix = last_point->x; (delx > 0) ? (ix < x) : (ix > x); ix += (delx > 0) ? 1 : -1) {
    /* step the other axis by the correct increment */
    iy += fabs(((float) dely / (float) delx)) * (float) ((dely < 0) ? -1.0 : 1.0);
    /* add the interpolated point */
    + new_point_p = g_new0(struct s_point, 1);
    new_point_p->x = ix;
    new_point_p->y = iy;
    metrics->pointList = g_slist_append (metrics->pointList, new_point_p);
    @@ -239,19 +237,21 @@
    if (((gint) iy) > metrics->max_y) metrics->max_y = (gint) iy;
    metrics->point_count++;
    - new_point_p = malloc(sizeof(struct s_point));
    }
    } else { /* same thing, but for dely larger than delx case... */
    - ix = LAST_POINT->x;
    + p_point last_point = (p_point)g_slist_last(metrics->pointList)->data;
    +
    + ix = last_point->x;
    /* go from the last point to the current, whatever direction it may be
    */
    - for (iy = LAST_POINT->y; (dely > 0) ? (iy < y) : (iy > y); iy += (dely > 0) ? 1 : -1) {
    + for (iy = last_point->y; (dely > 0) ? (iy < y) : (iy > y); iy += (dely > 0) ? 1 : -1) {
    /* step the other axis by the correct increment */
    ix += fabs(((float) delx / (float) dely)) * (float) ((delx < 0) ? -1.0 : 1.0);
    /* add the interpolated point */
    + new_point_p = g_new0(struct s_point, 1);
    new_point_p->y = iy;
    new_point_p->x = ix;
    metrics->pointList = g_slist_append(metrics->pointList, new_point_p);
    @@ -262,12 +262,11 @@
    if (((gint) iy) < metrics->min_y) metrics->min_y = (gint) iy;
    if (((gint) iy) > metrics->max_y) metrics->max_y = (gint) iy;
    metrics->point_count++;
    -
    - new_point_p = malloc(sizeof(struct s_point));
    }
    }
    /* add the sampled point */
    + new_point_p = g_new0(struct s_point, 1);
    metrics->pointList = g_slist_append(metrics->pointList, new_point_p);
    }
    --- a/pidgin/plugins/gevolution/add_buddy_dialog.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gevolution/add_buddy_dialog.c Tue Oct 08 21:48:28 2019 -0500
    @@ -45,11 +45,7 @@
    {
    gtk_widget_destroy(dialog->win);
    - if (dialog->contacts != NULL)
    - {
    - g_list_foreach(dialog->contacts, (GFunc)g_object_unref, NULL);
    - g_list_free(dialog->contacts);
    - }
    + g_list_free_full(dialog->contacts, g_object_unref);
    if (dialog->book != NULL)
    g_object_unref(dialog->book);
    @@ -224,8 +220,7 @@
    if (pixbuf != NULL)
    g_object_unref(G_OBJECT(pixbuf));
    - g_list_foreach(list, (GFunc)g_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_free);
    }
    static void
    @@ -245,8 +240,7 @@
    if (dialog->contacts != NULL)
    {
    - g_list_foreach(dialog->contacts, (GFunc)g_object_unref, NULL);
    - g_list_free(dialog->contacts);
    + g_list_free_full(dialog->contacts, g_object_unref);
    dialog->contacts = NULL;
    }
    --- a/pidgin/plugins/gevolution/assoc-buddy.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gevolution/assoc-buddy.c Tue Oct 08 21:48:28 2019 -0500
    @@ -44,11 +44,7 @@
    {
    gtk_widget_destroy(dialog->win);
    - if (dialog->contacts != NULL)
    - {
    - g_list_foreach(dialog->contacts, (GFunc)g_object_unref, NULL);
    - g_list_free(dialog->contacts);
    - }
    + g_list_free_full(dialog->contacts, g_object_unref);
    g_object_unref(dialog->book);
    gevo_addrbooks_model_unref(dialog->addrbooks);
    @@ -147,8 +143,7 @@
    if (dialog->contacts != NULL)
    {
    - g_list_foreach(dialog->contacts, (GFunc) g_object_unref, NULL);
    - g_list_free(dialog->contacts);
    + g_list_free_full(dialog->contacts, g_object_unref);
    dialog->contacts = NULL;
    }
    @@ -304,8 +299,7 @@
    purple_debug_error("evolution", "Error adding contact to book\n");
    /* Free the list. */
    - g_list_foreach(list, (GFunc)g_free, NULL);
    - g_list_free(list);
    + g_list_free_full(list, g_free);
    delete_win_cb(NULL, NULL, dialog);
    }
    --- a/pidgin/plugins/gevolution/eds-utils.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gevolution/eds-utils.c Tue Oct 08 21:48:28 2019 -0500
    @@ -137,7 +137,7 @@
    /* Break off the first contact and free the rest. */
    cards->next = NULL;
    cards2->prev = NULL;
    - g_list_foreach(cards2, (GFunc)g_object_unref, NULL);
    + g_list_free_full(cards2, g_object_unref);
    }
    /* Free the whole list. */
    --- a/pidgin/plugins/gevolution/gevo-util.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gevolution/gevo-util.c Tue Oct 08 21:48:28 2019 -0500
    @@ -69,14 +69,12 @@
    g_list_free(list);
    list = NULL;
    - if (purple_blist_get_buddy_list()->root == NULL) {
    + gnode = purple_blist_get_default_root();
    + if (gnode == NULL) {
    list = g_list_append(list,
    (gpointer)PURPLE_BLIST_DEFAULT_GROUP_NAME);
    } else {
    - for (gnode = purple_blist_get_buddy_list()->root;
    - gnode != NULL;
    - gnode = gnode->next)
    - {
    + for (; gnode != NULL; gnode = gnode->next) {
    if (PURPLE_IS_GROUP(gnode))
    {
    g = PURPLE_GROUP(gnode);
    --- a/pidgin/plugins/gevolution/gevolution.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gevolution/gevolution.c Tue Oct 08 21:48:28 2019 -0500
    @@ -110,8 +110,7 @@
    g_free(me);
    }
    - g_list_foreach(ims, (GFunc)g_free, NULL);
    - g_list_free(ims);
    + g_list_free_full(ims, g_free);
    }
    static void
    --- a/pidgin/plugins/gevolution/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gevolution/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -1,14 +1,16 @@
    +if PLUGINS
    #######################################################################
    # Check for stuff needed by the Evolution integration plugin.
    #######################################################################
    EVOLUTION_ADDRESSBOOK = [
    - dependency('libebook-1.2', required : force_deps),
    - dependency('libedata-book-1.2', required : force_deps),
    - dependency('evolution-data-server-1.2', version : '>= 3.6', required : force_deps),
    + dependency('libebook-1.2', required : get_option('gevolution')),
    + dependency('libedata-book-1.2', required : get_option('gevolution')),
    + dependency('evolution-data-server-1.2', version : '>= 3.6', required : get_option('gevolution')),
    ]
    -enable_gevolution = true
    foreach dep : EVOLUTION_ADDRESSBOOK
    - enable_gevolution = enable_gevolution and dep.found()
    + if not dep.found()
    + EVOLUTION_ADDRESSBOOK += [disabler()]
    + endif
    endforeach
    gevolution_SOURCES = [
    @@ -21,11 +23,10 @@
    'eds-utils.c'
    ]
    -if PLUGINS and enable_gevolution
    - gmodule = dependency('gmodule-2.0')
    +gmodule = dependency('gmodule-2.0', required : get_option('gevolution'))
    - gevolution = library('gevolution', gevolution_SOURCES,
    - dependencies : EVOLUTION_ADDRESSBOOK + [libpurple_dep, libpidgin_dep, glib, gmodule],
    - name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    +gevolution = library('gevolution', gevolution_SOURCES,
    + dependencies : EVOLUTION_ADDRESSBOOK + [libpurple_dep, libpidgin_dep, glib, gmodule],
    + name_prefix : '',
    + install : true, install_dir : PIDGIN_PLUGINDIR)
    endif
    --- a/pidgin/plugins/gevolution/new_person_dialog.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/gevolution/new_person_dialog.c Tue Oct 08 21:48:28 2019 -0500
    @@ -23,6 +23,7 @@
    #include "gtk3compat.h"
    #include "gtkutils.h"
    +#include "pidginaccountchooser.h"
    #include "debug.h"
    @@ -208,10 +209,9 @@
    }
    static void
    -select_account_cb(GObject *w, PurpleAccount *account,
    - GevoNewPersonDialog *dialog)
    +select_account_cb(GtkWidget *chooser, GevoNewPersonDialog *dialog)
    {
    - dialog->account = account;
    + dialog->account = pidgin_account_chooser_get_selected(chooser);
    }
    void
    @@ -277,9 +277,9 @@
    {
    /* Add the account type stuff. */
    dialog->accounts_menu =
    - pidgin_account_option_menu_new(account, FALSE,
    - G_CALLBACK(select_account_cb),
    - NULL, dialog);
    + pidgin_account_chooser_new(account, FALSE);
    + g_signal_connect(dialog->accounts_menu, "changed",
    + G_CALLBACK(select_account_cb), dialog);
    add_pref_box(sg, vbox, _("Account type:"), dialog->accounts_menu);
    /* Username */
    --- a/pidgin/plugins/history.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/history.c Tue Oct 08 21:48:28 2019 -0500
    @@ -167,8 +167,7 @@
    g_object_ref(G_OBJECT(gtkconv->webview));
    g_idle_add(_scroll_webview_to_end, gtkconv->webview);
    - g_list_foreach(logs, (GFunc)purple_log_free, NULL);
    - g_list_free(logs);
    + g_list_free_full(logs, (GDestroyNotify)purple_log_free);
    }
    static void
    --- a/pidgin/plugins/mailchk.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/mailchk.c Tue Oct 08 21:48:28 2019 -0500
    @@ -56,7 +56,7 @@
    check_timeout(gpointer data)
    {
    gint count = check_mail();
    - PurpleBuddyList *list = purple_blist_get_buddy_list();
    + PurpleBuddyList *list = purple_blist_get_default();
    if (count == -1)
    return FALSE;
    @@ -91,7 +91,7 @@
    static void
    signon_cb(PurpleConnection *gc)
    {
    - PurpleBuddyList *list = purple_blist_get_buddy_list();
    + PurpleBuddyList *list = purple_blist_get_default();
    if (list && !timer) {
    check_timeout(NULL); /* we want the box to be drawn immediately */
    timer = g_timeout_add_seconds(2, check_timeout, NULL);
    @@ -101,7 +101,7 @@
    static void
    signoff_cb(PurpleConnection *gc)
    {
    - PurpleBuddyList *list = purple_blist_get_buddy_list();
    + PurpleBuddyList *list = purple_blist_get_default();
    if ((!list || !PIDGIN_BLIST(list)->vbox) && timer) {
    g_source_remove(timer);
    timer = 0;
    @@ -138,7 +138,7 @@
    static gboolean
    plugin_load(PurplePlugin *plugin, GError **error)
    {
    - PurpleBuddyList *list = purple_blist_get_buddy_list();
    + PurpleBuddyList *list = purple_blist_get_default();
    void *conn_handle = purple_connections_get_handle();
    if (!check_timeout(NULL)) {
    --- a/pidgin/plugins/markerline.c Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,243 +0,0 @@
    -/*
    - * Markerline - Draw a line to indicate new messages in a conversation.
    - * Copyright (C) 2006
    - *
    - * This program is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU General Public License as
    - * published by the Free Software Foundation; either version 2 of the
    - * License, or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful, but
    - * WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    - * General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    - * 02111-1301, USA.
    - */
    -#include "internal.h"
    -
    -#define PLUGIN_ID "gtk-plugin_pack-markerline"
    -#define PLUGIN_NAME N_("Markerline")
    -#define PLUGIN_CATEGORY N_("User interface")
    -#define PLUGIN_STATIC_NAME Markerline
    -#define PLUGIN_SUMMARY N_("Draw a line to indicate new messages in a conversation.")
    -#define PLUGIN_DESCRIPTION N_("Draw a line to indicate new messages in a conversation.")
    -#define PLUGIN_AUTHORS {"Sadrul H Chowdhury <sadrul@users.sourceforge.net>", NULL}
    -
    -/* System headers */
    -#include <gdk/gdk.h>
    -#include <glib.h>
    -#include <gtk/gtk.h>
    -
    -/* Purple headers */
    -#include <gtkconv.h>
    -#include <gtkplugin.h>
    -#include <gtkwebview.h>
    -#include <action.h>
    -#include <version.h>
    -
    -#define PREF_PREFIX "/plugins/gtk/" PLUGIN_ID
    -#define PREF_IMS PREF_PREFIX "/ims"
    -#define PREF_CHATS PREF_PREFIX "/chats"
    -
    -static void
    -update_marker_for_gtkconv(PidginConversation *gtkconv)
    -{
    - PurpleConversation *conv;
    -
    - g_return_if_fail(gtkconv != NULL);
    -
    - conv = gtkconv->active_conv;
    -
    - if ((PURPLE_IS_CHAT_CONVERSATION(conv) && !purple_prefs_get_bool(PREF_CHATS)) ||
    - (PURPLE_IS_IM_CONVERSATION(conv) && !purple_prefs_get_bool(PREF_IMS)))
    - return;
    -
    - pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(gtkconv->webview),
    - "var mhr = document.getElementById(\"markerhr\");"
    - "if (!mhr) {"
    - "mhr = document.createElement(\"hr\");"
    - "mhr.setAttribute(\"id\", \"markerhr\");"
    - "mhr.setAttribute(\"color\", \"#ff0000\");"
    - "mhr.setAttribute(\"size\", \"1\");"
    - "}"
    - "document.getElementById(\"Chat\").appendChild(mhr);");
    -}
    -
    -static gboolean
    -focus_removed(GtkWidget *widget, GdkEventVisibility *event, PidginConvWindow *win)
    -{
    - PurpleConversation *conv;
    - PidginConversation *gtkconv;
    -
    - conv = pidgin_conv_window_get_active_conversation(win);
    - g_return_val_if_fail(conv != NULL, FALSE);
    -
    - gtkconv = PIDGIN_CONVERSATION(conv);
    - update_marker_for_gtkconv(gtkconv);
    -
    - return FALSE;
    -}
    -
    -static void
    -page_switched(GtkWidget *widget, GtkWidget *page, gint num, PidginConvWindow *win)
    -{
    - focus_removed(NULL, NULL, win);
    -}
    -
    -static void
    -detach_from_gtkconv(PidginConversation *gtkconv, gpointer null)
    -{
    - pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(gtkconv->webview),
    - "var mhr = document.getElementById(\"markerhr\");"
    - "if (mhr) mhr.parentNode.removeChild(mhr);");
    -}
    -
    -static void
    -detach_from_pidgin_window(PidginConvWindow *win, gpointer null)
    -{
    - g_list_foreach(pidgin_conv_window_get_gtkconvs(win), (GFunc)detach_from_gtkconv, NULL);
    - g_signal_handlers_disconnect_by_func(G_OBJECT(win->notebook), page_switched, win);
    - g_signal_handlers_disconnect_by_func(G_OBJECT(win->window), focus_removed, win);
    -}
    -
    -static void
    -attach_to_gtkconv(PidginConversation *gtkconv, gpointer null)
    -{
    - detach_from_gtkconv(gtkconv, NULL);
    - update_marker_for_gtkconv(gtkconv);
    -}
    -
    -static void
    -attach_to_pidgin_window(PidginConvWindow *win, gpointer null)
    -{
    - g_list_foreach(pidgin_conv_window_get_gtkconvs(win), (GFunc)attach_to_gtkconv, NULL);
    -
    - g_signal_connect(G_OBJECT(win->window), "focus_out_event",
    - G_CALLBACK(focus_removed), win);
    -
    - g_signal_connect(G_OBJECT(win->notebook), "switch_page",
    - G_CALLBACK(page_switched), win);
    -}
    -
    -static void
    -detach_from_all_windows(void)
    -{
    - g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)detach_from_pidgin_window, NULL);
    -}
    -
    -static void
    -attach_to_all_windows(void)
    -{
    - g_list_foreach(pidgin_conv_windows_get_list(), (GFunc)attach_to_pidgin_window, NULL);
    -}
    -
    -static void
    -conv_created(PidginConversation *gtkconv, gpointer null)
    -{
    - PidginConvWindow *win;
    -
    - win = pidgin_conv_get_window(gtkconv);
    - if (!win)
    - return;
    -
    - detach_from_pidgin_window(win, NULL);
    - attach_to_pidgin_window(win, NULL);
    -}
    -
    -static void
    -jump_to_markerline(PurpleConversation *conv, gpointer null)
    -{
    - PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
    -
    - if (!gtkconv)
    - return;
    -
    - pidgin_webview_safe_execute_script(PIDGIN_WEBVIEW(gtkconv->webview),
    - "var mhr = document.getElementById(\"markerhr\");"
    - "if (mhr) {"
    - "window.scroll(0, mhr.offsetTop);"
    - "}");
    -}
    -
    -static void
    -conv_menu_cb(PurpleConversation *conv, GList **list)
    -{
    - gboolean enabled = ((PURPLE_IS_IM_CONVERSATION(conv) && purple_prefs_get_bool(PREF_IMS)) ||
    - (PURPLE_IS_CHAT_CONVERSATION(conv) && purple_prefs_get_bool(PREF_CHATS)));
    - PurpleActionMenu *action = purple_action_menu_new(_("Jump to markerline"),
    - enabled ? PURPLE_CALLBACK(jump_to_markerline) : NULL, NULL, NULL);
    - *list = g_list_append(*list, action);
    -}
    -
    -static PurplePluginPrefFrame *
    -get_plugin_pref_frame(PurplePlugin *plugin)
    -{
    - PurplePluginPrefFrame *frame;
    - PurplePluginPref *pref;
    -
    - frame = purple_plugin_pref_frame_new();
    -
    - pref = purple_plugin_pref_new_with_label(_("Draw Markerline in "));
    - purple_plugin_pref_frame_add(frame, pref);
    -
    - pref = purple_plugin_pref_new_with_name_and_label(PREF_IMS,
    - _("_IM windows"));
    - purple_plugin_pref_frame_add(frame, pref);
    -
    - pref = purple_plugin_pref_new_with_name_and_label(PREF_CHATS,
    - _("C_hat windows"));
    - purple_plugin_pref_frame_add(frame, pref);
    -
    - return frame;
    -}
    -
    -static PidginPluginInfo *
    -plugin_query(GError **error)
    -{
    - const gchar * const authors[] = PLUGIN_AUTHORS;
    -
    - return pidgin_plugin_info_new(
    - "id", PLUGIN_ID,
    - "name", PLUGIN_NAME,
    - "version", DISPLAY_VERSION,
    - "category", PLUGIN_CATEGORY,
    - "summary", PLUGIN_SUMMARY,
    - "description", PLUGIN_DESCRIPTION,
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "pref-frame-cb", get_plugin_pref_frame,
    - NULL
    - );
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin, GError **error)
    -{
    - purple_prefs_add_none(PREF_PREFIX);
    - purple_prefs_add_bool(PREF_IMS, FALSE);
    - purple_prefs_add_bool(PREF_CHATS, TRUE);
    -
    - attach_to_all_windows();
    -
    - purple_signal_connect(pidgin_conversations_get_handle(), "conversation-displayed",
    - plugin, PURPLE_CALLBACK(conv_created), NULL);
    -
    - purple_signal_connect(purple_conversations_get_handle(), "conversation-extended-menu",
    - plugin, PURPLE_CALLBACK(conv_menu_cb), NULL);
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin, GError **error)
    -{
    - detach_from_all_windows();
    -
    - return TRUE;
    -}
    -
    -PURPLE_PLUGIN_INIT(PLUGIN_STATIC_NAME, plugin_query, plugin_load, plugin_unload);
    --- a/pidgin/plugins/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -1,6 +1,4 @@
    -if get_option('gevolution')
    - subdir('gevolution')
    -endif
    +subdir('gevolution')
    if false
    subdir('musicmessaging')
    @@ -16,6 +14,7 @@
    subdir('disco')
    subdir('ticker')
    +subdir('xmppconsole')
    if IS_WIN32
    subdir('win32/winprefs')
    @@ -41,9 +40,10 @@
    name_prefix : '')
    history = library('history', 'history.c',
    + build_by_default: false,
    dependencies : [libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    + install : false, install_dir : PIDGIN_PLUGINDIR)
    iconaway = library('iconaway', 'iconaway.c',
    dependencies : [libpurple_dep, libpidgin_dep, glib],
    @@ -53,17 +53,14 @@
    imgupload = library('imgupload', 'imgupload.c',
    dependencies : [json, libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    -
    - markerline = library('markerline', 'markerline.c',
    - dependencies : [webkit, libpurple_dep, libpidgin_dep, glib],
    - name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    + build_by_default: false,
    + install : false, install_dir : PIDGIN_PLUGINDIR)
    notify = library('notify', 'notify.c',
    dependencies : [libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    + build_by_default: false,
    + install : false, install_dir : PIDGIN_PLUGINDIR)
    relnot = library('relnot', 'relnot.c',
    dependencies : [libpurple_dep, libpidgin_dep, glib],
    @@ -73,22 +70,14 @@
    screencap = library('screencap', 'screencap.c',
    dependencies : [libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    -
    - sendbutton = library('sendbutton', 'sendbutton.c',
    - dependencies : [libpurple_dep, libpidgin_dep, glib],
    - name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    + build_by_default: false,
    + install : false, install_dir : PIDGIN_PLUGINDIR)
    spellchk = library('spellchk', 'spellchk.c',
    dependencies : [libpurple_dep, libpidgin_dep, glib],
    name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    -
    - xmppconsole = library('xmppconsole', 'xmppconsole.c',
    - dependencies : [libpurple_dep, libpidgin_dep, glib],
    - name_prefix : '',
    - install : true, install_dir : PIDGIN_PLUGINDIR)
    + build_by_default: false,
    + install : false, install_dir : PIDGIN_PLUGINDIR)
    if enable_unity
    unity = library('unity', 'unity.c',
    --- a/pidgin/plugins/musicmessaging/musicmessaging.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/musicmessaging/musicmessaging.c Tue Oct 08 21:48:28 2019 -0500
    @@ -534,7 +534,7 @@
    static void init_conversation (PurpleConversation *conv)
    {
    MMConversation *mmconv;
    - mmconv = g_malloc(sizeof(MMConversation));
    + mmconv = g_new0(MMConversation, 1);
    mmconv->conv = conv;
    mmconv->started = FALSE;
    --- a/pidgin/plugins/raw.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/raw.c Tue Oct 08 21:48:28 2019 -0500
    @@ -32,6 +32,7 @@
    #include "gtk3compat.h"
    #include "gtkplugin.h"
    #include "gtkutils.h"
    +#include "pidginaccountchooser.h"
    #include "protocols/jabber/jabber.h"
    @@ -98,10 +99,9 @@
    }
    static void
    -account_changed_cb(GtkWidget *dropdown, PurpleAccount *new_account,
    - void *user_data)
    +account_changed_cb(GtkWidget *chooser, void *user_data)
    {
    - account = new_account;
    + account = pidgin_account_chooser_get_selected(chooser);
    }
    static PidginPluginInfo *
    @@ -149,8 +149,9 @@
    gtk_container_add(GTK_CONTAINER(window), hbox);
    /* Account drop-down menu. */
    - dropdown = pidgin_account_option_menu_new(NULL, FALSE,
    - G_CALLBACK(account_changed_cb), NULL, NULL);
    + dropdown = pidgin_account_chooser_new(NULL, FALSE);
    + g_signal_connect(dropdown, "changed", G_CALLBACK(account_changed_cb),
    + NULL);
    if (purple_connections_get_all())
    account = (PurpleAccount *)purple_connections_get_all()->data;
    --- a/pidgin/plugins/screencap.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/screencap.c Tue Oct 08 21:48:28 2019 -0500
    @@ -371,16 +371,11 @@
    g_signal_connect(G_OBJECT(drawing_area), "leave-notify-event",
    G_CALLBACK(scrncap_drawing_area_leave), draw_cursor);
    -#if GTK_CHECK_VERSION(3,14,0)
    box = drawing_area;
    g_object_set(drawing_area,
    "halign", GTK_ALIGN_CENTER,
    "valign", GTK_ALIGN_CENTER,
    NULL);
    -#else
    - box = gtk_alignment_new(0.5, 0.5, 0, 0);
    - gtk_container_add(GTK_CONTAINER(box), drawing_area);
    -#endif
    scroll_area = pidgin_make_scrollable(box,
    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC,
    GTK_SHADOW_NONE, -1, -1);
    @@ -622,13 +617,8 @@
    "or press Escape button to cancel"));
    gtk_label_set_markup(GTK_LABEL(hint), hint_msg);
    g_free(hint_msg);
    -#if GTK_CHECK_VERSION(3,12,0)
    gtk_widget_set_margin_start(hint, 10);
    gtk_widget_set_margin_end(hint, 10);
    -#else
    - gtk_widget_set_margin_left(hint, 10);
    - gtk_widget_set_margin_right(hint, 10);
    -#endif
    gtk_widget_set_margin_top(hint, 7);
    gtk_widget_set_margin_bottom(hint, 7);
    hint_box = gtk_event_box_new();
    --- a/pidgin/plugins/sendbutton.c Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,188 +0,0 @@
    -/*
    - * SendButton - Add a Send button to the conversation window entry area.
    - * Copyright (C) 2008 Etan Reisner <deryni@pidgin.im>
    - *
    - * This program is free software; you can redistribute it and/or
    - * modify it under the terms of the GNU General Public License
    - * as published by the Free Software Foundation; either version 2
    - * of the License, or (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    - */
    -
    -#ifdef HAVE_CONFIG_H
    -#include <config.h>
    -#endif
    -
    -#include "internal.h"
    -
    -#include "version.h"
    -
    -#include "pidgin.h"
    -
    -#include "gtkconv.h"
    -#include "gtkplugin.h"
    -
    -static void
    -send_button_cb(GtkButton *button, PidginConversation *gtkconv)
    -{
    - g_signal_emit_by_name(gtkconv->entry, "message_send");
    -}
    -
    -static void
    -input_buffer_changed(GtkTextBuffer *text_buffer, GtkWidget *send_button)
    -{
    - if (gtk_text_buffer_get_char_count(text_buffer) != 0)
    - gtk_widget_set_sensitive(send_button, TRUE);
    - else
    - gtk_widget_set_sensitive(send_button, FALSE);
    -}
    -
    -static void
    -create_send_button_pidgin(PidginConversation *gtkconv)
    -{
    - GtkWidget *send_button;
    - GtkTextBuffer *buf;
    - guint signal_id;
    -
    - send_button = g_object_get_data(G_OBJECT(gtkconv->lower_hbox),
    - "send_button");
    -
    - if (send_button != NULL)
    - return;
    -
    - send_button = gtk_button_new_with_mnemonic(_("_Send"));
    - g_signal_connect(G_OBJECT(send_button), "clicked",
    - G_CALLBACK(send_button_cb), gtkconv);
    - gtk_box_pack_end(GTK_BOX(gtkconv->lower_hbox), send_button, FALSE,
    - FALSE, 0);
    - gtk_widget_show(send_button);
    -
    - buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
    - if (buf) {
    - signal_id = g_signal_connect(G_OBJECT(buf), "changed",
    - G_CALLBACK(input_buffer_changed),
    - send_button);
    - g_object_set_data(G_OBJECT(send_button), "buffer-signal",
    - GINT_TO_POINTER(signal_id));
    - input_buffer_changed(buf, send_button);
    - }
    -
    - g_object_set_data(G_OBJECT(gtkconv->lower_hbox), "send_button",
    - send_button);
    -}
    -
    -static void
    -remove_send_button_pidgin(PidginConversation *gtkconv)
    -{
    - GtkWidget *send_button = NULL;
    -
    - send_button = g_object_get_data(G_OBJECT(gtkconv->lower_hbox),
    - "send_button");
    - if (send_button != NULL) {
    - GtkTextBuffer *buf;
    - guint signal_id;
    -
    - buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(gtkconv->entry));
    - signal_id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(send_button),
    - "buffer-signal"));
    - if (buf && signal_id)
    - g_signal_handler_disconnect(G_OBJECT(buf), signal_id);
    -
    - gtk_widget_destroy(send_button);
    - g_object_set_data(G_OBJECT(gtkconv->lower_hbox),
    - "send_button", NULL);
    - }
    -}
    -
    -static void
    -conversation_displayed_cb(PidginConversation *gtkconv)
    -{
    - GtkWidget *send_button = NULL;
    -
    - send_button = g_object_get_data(G_OBJECT(gtkconv->lower_hbox),
    - "send_button");
    - if (send_button == NULL) {
    - create_send_button_pidgin(gtkconv);
    - }
    -}
    -
    -static PidginPluginInfo *
    -plugin_query(GError **error)
    -{
    - const gchar * const authors[] = {
    - "Etan Reisner <deryni@pidgin.im>",
    - NULL
    - };
    -
    - return pidgin_plugin_info_new(
    - "id", "gtksendbutton",
    - "name", N_("Send Button"),
    - "version", DISPLAY_VERSION,
    - "category", N_("User interface"),
    - "summary", N_("Conversation Window Send Button."),
    - "description", N_("Adds a Send button to the entry area of the "
    - "conversation window. Intended for use when no "
    - "physical keyboard is present."),
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - NULL
    - );
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin, GError **error)
    -{
    - GList *convs = purple_conversations_get_all();
    - void *gtk_conv_handle = pidgin_conversations_get_handle();
    -
    - purple_signal_connect(gtk_conv_handle, "conversation-displayed", plugin,
    - PURPLE_CALLBACK(conversation_displayed_cb), NULL);
    - /*
    - purple_signal_connect(gtk_conv_handle, "conversation-hiding", plugin,
    - PURPLE_CALLBACK(conversation_hiding_cb), NULL);
    - */
    -
    - while (convs) {
    -
    - PurpleConversation *conv = (PurpleConversation *)convs->data;
    -
    - /* Setup Send button */
    - if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
    - create_send_button_pidgin(PIDGIN_CONVERSATION(conv));
    - }
    -
    - convs = convs->next;
    - }
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin, GError **error)
    -{
    - GList *convs = purple_conversations_get_all();
    -
    - while (convs) {
    - PurpleConversation *conv = (PurpleConversation *)convs->data;
    -
    - /* Remove Send button */
    - if (PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
    - remove_send_button_pidgin(PIDGIN_CONVERSATION(conv));
    - }
    -
    - convs = convs->next;
    - }
    -
    - return TRUE;
    -}
    -
    -PURPLE_PLUGIN_INIT(sendbutton, plugin_query, plugin_load, plugin_unload);
    --- a/pidgin/plugins/spellchk.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/spellchk.c Tue Oct 08 21:48:28 2019 -0500
    @@ -1785,7 +1785,7 @@
    gboolean complete = TRUE;
    gboolean case_sensitive = FALSE;
    - buf = g_build_filename(purple_user_dir(), "dict", NULL);
    + buf = g_build_filename(purple_config_dir(), "dict", NULL);
    if (!(g_file_get_contents(buf, &ibuf, &size, NULL) && ibuf)) {
    ibuf = g_strdup(defaultconf);
    size = strlen(defaultconf);
    @@ -2080,7 +2080,7 @@
    } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter));
    }
    - purple_util_write_data_to_file("dict", data->str, -1);
    + purple_util_write_data_to_config_file("dict", data->str, -1);
    g_string_free(data, TRUE);
    }
    --- a/pidgin/plugins/ticker/gtkticker.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/ticker/gtkticker.c Tue Oct 08 21:48:28 2019 -0500
    @@ -65,8 +65,15 @@
    static GtkContainerClass *parent_class = NULL;
    +G_DEFINE_DYNAMIC_TYPE(GtkTicker, gtk_ticker, GTK_TYPE_CONTAINER);
    -PURPLE_DEFINE_TYPE(GtkTicker, gtk_ticker, GTK_TYPE_CONTAINER);
    +/* This exists solely because the above macro makes gtk_ticker_register_type
    + * static. */
    +void
    +gtk_ticker_register(PurplePlugin *plugin)
    +{
    + gtk_ticker_register_type(G_TYPE_MODULE(plugin));
    +}
    static void gtk_ticker_finalize(GObject *object) {
    gtk_ticker_stop_scroll(GTK_TICKER(object));
    @@ -100,6 +107,11 @@
    container_class->child_type = gtk_ticker_child_type;
    }
    +static void
    +gtk_ticker_class_finalize(G_GNUC_UNUSED GtkTickerClass *klass)
    +{
    +}
    +
    static GType gtk_ticker_child_type (GtkContainer *container)
    {
    return GTK_TYPE_WIDGET;
    --- a/pidgin/plugins/ticker/gtkticker.h Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/ticker/gtkticker.h Tue Oct 08 21:48:28 2019 -0500
    @@ -48,7 +48,8 @@
    G_MODULE_EXPORT
    G_DECLARE_FINAL_TYPE(GtkTicker, gtk_ticker, GTK, TICKER, GtkContainer)
    -void gtk_ticker_register_type (PurplePlugin *plugin);
    +G_GNUC_INTERNAL
    +void gtk_ticker_register(PurplePlugin *plugin);
    GtkWidget* gtk_ticker_new (void);
    void gtk_ticker_add (GtkTicker *ticker,
    --- a/pidgin/plugins/ticker/ticker.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/ticker/ticker.c Tue Oct 08 21:48:28 2019 -0500
    @@ -231,10 +231,8 @@
    PurpleBlistNode *gnode, *cnode, *bnode;
    PurpleBuddy *b;
    - for(gnode = purple_blist_get_root();
    - gnode;
    - gnode = purple_blist_node_get_sibling_next(gnode))
    - {
    + for (gnode = purple_blist_get_default_root(); gnode;
    + gnode = purple_blist_node_get_sibling_next(gnode)) {
    if(!PURPLE_IS_GROUP(gnode))
    continue;
    for(cnode = purple_blist_node_get_first_child(gnode);
    @@ -343,7 +341,7 @@
    {
    void *blist_handle = purple_blist_get_handle();
    - gtk_ticker_register_type(plugin);
    + gtk_ticker_register(plugin);
    purple_signal_connect(purple_connections_get_handle(), "signed-off",
    plugin, PURPLE_CALLBACK(signoff_cb), NULL);
    --- a/pidgin/plugins/win32/winprefs/winprefs.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/plugins/win32/winprefs/winprefs.c Tue Oct 08 21:48:28 2019 -0500
    @@ -88,7 +88,9 @@
    if(!blist)
    return;
    - gtk_window_set_keep_above(GTK_WINDOW(PIDGIN_BLIST(purple_blist_get_buddy_list())->window), val);
    + gtk_window_set_keep_above(
    + GTK_WINDOW(PIDGIN_BLIST(purple_blist_get_default())->window),
    + val);
    }
    static void blist_dock_cb(gboolean val) {
    @@ -330,9 +332,10 @@
    handle = plugin;
    /* blist docking init */
    - if(purple_blist_get_buddy_list() && PIDGIN_BLIST(purple_blist_get_buddy_list())
    - && PIDGIN_BLIST(purple_blist_get_buddy_list())->window) {
    - blist_create_cb(purple_blist_get_buddy_list(), NULL);
    + if (purple_blist_get_default() &&
    + PIDGIN_BLIST(purple_blist_get_default()) &&
    + PIDGIN_BLIST(purple_blist_get_default())->window) {
    + blist_create_cb(purple_blist_get_default(), NULL);
    }
    /* This really shouldn't happen anymore generally, but if for some strange
    --- a/pidgin/plugins/xmppconsole.c Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,975 +0,0 @@
    -/*
    - * Purple - XMPP debugging tool
    - *
    - * Copyright (C) 2002-2003, Sean Egan
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU General Public License as published by
    - * the Free Software Foundation; either version 2 of the License, or
    - * (at your option) any later version.
    - *
    - * This program is distributed in the hope that it will be useful,
    - * but WITHOUT ANY WARRANTY; without even the implied warranty of
    - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    - * GNU General Public License for more details.
    - *
    - * You should have received a copy of the GNU General Public License
    - * along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - *
    - */
    -
    -#include "internal.h"
    -#include "gtkplugin.h"
    -#include "version.h"
    -#include "protocol.h"
    -#include "xmlnode.h"
    -
    -#include "gtkutils.h"
    -
    -#include <gdk/gdkkeysyms.h>
    -
    -#include "gtk3compat.h"
    -
    -#define PLUGIN_ID "gtk-xmpp"
    -#define PLUGIN_DOMAIN (g_quark_from_static_string(PLUGIN_ID))
    -
    -typedef struct {
    - PurpleConnection *gc;
    - GtkWidget *window;
    - GtkWidget *hbox;
    - GtkWidget *dropdown;
    - GtkWidget *view;
    - GtkTextBuffer *buffer;
    - struct {
    - GtkTextTag *info;
    - GtkTextTag *incoming;
    - GtkTextTag *outgoing;
    - GtkTextTag *bracket;
    - GtkTextTag *tag;
    - GtkTextTag *attr;
    - GtkTextTag *value;
    - GtkTextTag *xmlns;
    - } tags;
    - GtkWidget *entry;
    - GtkTextBuffer *entry_buffer;
    - GtkWidget *sw;
    - int count;
    - GList *accounts;
    -} XmppConsole;
    -
    -XmppConsole *console = NULL;
    -static void *xmpp_console_handle = NULL;
    -
    -static const gchar *xmpp_prpls[] = {
    - "prpl-jabber", "prpl-gtalk", NULL
    -};
    -
    -#define EMPTY_HTML \
    -"<html><head><style type='text/css'>" \
    - "body { word-wrap: break-word; margin: 0; }" \
    - "div.tab { padding-left: 1em; }" \
    - "div.info { color: #777777; }" \
    - "div.incoming { background-color: #ffcece; }" \
    - "div.outgoing { background-color: #dcecc4; }" \
    - "span.bracket { color: #940f8c; }" \
    - "span.tag { color: #8b1dab; font-weight: bold; }" \
    - "span.attr { color: #a02961; font-weight: bold; }" \
    - "span.value { color: #324aa4; }" \
    - "span.xmlns { color: #2cb12f; font-weight: bold;}" \
    -"</style></head></html>"
    -
    -static gboolean
    -xmppconsole_is_xmpp_account(PurpleAccount *account)
    -{
    - const gchar *prpl_name;
    - int i;
    -
    - prpl_name = purple_account_get_protocol_id(account);
    -
    - i = 0;
    - while (xmpp_prpls[i] != NULL) {
    - if (purple_strequal(xmpp_prpls[i], prpl_name))
    - return TRUE;
    - i++;
    - }
    -
    - return FALSE;
    -}
    -
    -static void
    -purple_xmlnode_append_to_buffer(PurpleXmlNode *node, gint indent_level, GtkTextIter *iter, GtkTextTag *tag)
    -{
    - PurpleXmlNode *c;
    - gboolean need_end = FALSE, pretty = TRUE;
    - gint i;
    -
    - g_return_if_fail(node != NULL);
    -
    - for (i = 0; i < indent_level; i++) {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "\t", 1, tag, NULL);
    - }
    -
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "<", 1,
    - tag, console->tags.bracket, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, node->name, -1,
    - tag, console->tags.tag, NULL);
    -
    - if (node->xmlns) {
    - if ((!node->parent ||
    - !node->parent->xmlns ||
    - !purple_strequal(node->xmlns, node->parent->xmlns)) &&
    - !purple_strequal(node->xmlns, "jabber:client"))
    - {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, " ", 1,
    - tag, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "xmlns", 5,
    - tag, console->tags.attr, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "='", 2,
    - tag, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, node->xmlns, -1,
    - tag, console->tags.xmlns, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "'", 1,
    - tag, NULL);
    - }
    - }
    - for (c = node->child; c; c = c->next)
    - {
    - if (c->type == PURPLE_XMLNODE_TYPE_ATTRIB) {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, " ", 1,
    - tag, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, c->name, -1,
    - tag, console->tags.attr, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "='", 2,
    - tag, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, c->data, -1,
    - tag, console->tags.value, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "'", 1,
    - tag, NULL);
    - } else if (c->type == PURPLE_XMLNODE_TYPE_TAG || c->type == PURPLE_XMLNODE_TYPE_DATA) {
    - if (c->type == PURPLE_XMLNODE_TYPE_DATA)
    - pretty = FALSE;
    - need_end = TRUE;
    - }
    - }
    -
    - if (need_end) {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, ">", 1,
    - tag, console->tags.bracket, NULL);
    - if (pretty) {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "\n", 1,
    - tag, NULL);
    - }
    -
    - for (c = node->child; c; c = c->next)
    - {
    - if (c->type == PURPLE_XMLNODE_TYPE_TAG) {
    - purple_xmlnode_append_to_buffer(c, indent_level + 1, iter, tag);
    - } else if (c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, c->data, c->data_sz,
    - tag, NULL);
    - }
    - }
    -
    - if (pretty) {
    - for (i = 0; i < indent_level; i++) {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "\t", 1, tag, NULL);
    - }
    - }
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "<", 1,
    - tag, console->tags.bracket, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "/", 1,
    - tag, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, node->name, -1,
    - tag, console->tags.tag, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, ">", 1,
    - tag, console->tags.bracket, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "\n", 1,
    - tag, NULL);
    - } else {
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "/", 1,
    - tag, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, ">", 1,
    - tag, console->tags.bracket, NULL);
    - gtk_text_buffer_insert_with_tags(console->buffer, iter, "\n", 1,
    - tag, NULL);
    - }
    -}
    -
    -static void
    -purple_xmlnode_received_cb(PurpleConnection *gc, PurpleXmlNode **packet, gpointer null)
    -{
    - GtkTextIter iter;
    -
    - if (!console || console->gc != gc)
    - return;
    -
    - gtk_text_buffer_get_end_iter(console->buffer, &iter);
    - purple_xmlnode_append_to_buffer(*packet, 0, &iter, console->tags.incoming);
    -}
    -
    -static void
    -purple_xmlnode_sent_cb(PurpleConnection *gc, char **packet, gpointer null)
    -{
    - GtkTextIter iter;
    - PurpleXmlNode *node;
    -
    - if (!console || console->gc != gc)
    - return;
    - node = purple_xmlnode_from_str(*packet, -1);
    -
    - if (!node)
    - return;
    -
    - gtk_text_buffer_get_end_iter(console->buffer, &iter);
    - purple_xmlnode_append_to_buffer(node, 0, &iter, console->tags.outgoing);
    - purple_xmlnode_free(node);
    -}
    -
    -static gboolean
    -message_send_cb(GtkWidget *widget, GdkEventKey *event, gpointer p)
    -{
    - PurpleProtocol *protocol = NULL;
    - PurpleConnection *gc;
    - gchar *text;
    - GtkTextIter start, end;
    -
    - if (event->keyval != GDK_KEY_KP_Enter && event->keyval != GDK_KEY_Return)
    - return FALSE;
    -
    - gc = console->gc;
    -
    - if (gc)
    - protocol = purple_connection_get_protocol(gc);
    -
    - gtk_text_buffer_get_bounds(console->entry_buffer, &start, &end);
    - text = gtk_text_buffer_get_text(console->entry_buffer, &start, &end, FALSE);
    -
    - if (protocol)
    - purple_protocol_server_iface_send_raw(protocol, gc, text, strlen(text));
    -
    - g_free(text);
    - gtk_text_buffer_set_text(console->entry_buffer, "", 0);
    -
    - return TRUE;
    -}
    -
    -static void
    -entry_changed_cb(GtkTextBuffer *buffer, void *data)
    -{
    - GtkTextIter start, end;
    - char *xmlstr, *str;
    -#if 0
    - int wrapped_lines;
    - int lines;
    - GdkRectangle oneline;
    - int height;
    - int pad_top, pad_inside, pad_bottom;
    -#endif
    - PurpleXmlNode *node;
    - GtkStyleContext *style;
    -
    -#if 0
    - /* TODO WebKit: Do entry auto-sizing... */
    - wrapped_lines = 1;
    - gtk_text_buffer_get_start_iter(buffer, &iter);
    - gtk_text_view_get_iter_location(GTK_TEXT_VIEW(console->entry), &iter, &oneline);
    - while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(console->entry), &iter))
    - wrapped_lines++;
    -
    - lines = gtk_text_buffer_get_line_count(buffer);
    -
    - /* Show a maximum of 64 lines */
    - lines = MIN(lines, 6);
    - wrapped_lines = MIN(wrapped_lines, 6);
    -
    - pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(console->entry));
    - pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(console->entry));
    - pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(console->entry));
    -
    - height = (oneline.height + pad_top + pad_bottom) * lines;
    - height += (oneline.height + pad_inside) * (wrapped_lines - lines);
    -
    - gtk_widget_set_size_request(console->sw, -1, height + 6);
    -#endif
    -
    - gtk_text_buffer_get_bounds(buffer, &start, &end);
    - str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
    - if (!str)
    - return;
    - xmlstr = g_strdup_printf("<xml>%s</xml>", str);
    - node = purple_xmlnode_from_str(xmlstr, -1);
    - style = gtk_widget_get_style_context(console->entry);
    - if (node) {
    - gtk_style_context_remove_class(style, GTK_STYLE_CLASS_ERROR);
    - } else {
    - gtk_style_context_add_class(style, GTK_STYLE_CLASS_ERROR);
    - }
    - g_free(str);
    - g_free(xmlstr);
    - if (node)
    - purple_xmlnode_free(node);
    -}
    -
    -static void
    -load_text_and_set_caret(const gchar *pre_text, const gchar *post_text)
    -{
    - GtkTextIter where;
    - GtkTextMark *mark;
    -
    - gtk_text_buffer_begin_user_action(console->entry_buffer);
    -
    - gtk_text_buffer_set_text(console->entry_buffer, pre_text, -1);
    -
    - gtk_text_buffer_get_end_iter(console->entry_buffer, &where);
    - mark = gtk_text_buffer_create_mark(console->entry_buffer, NULL, &where, TRUE);
    -
    - gtk_text_buffer_insert(console->entry_buffer, &where, post_text, -1);
    -
    - gtk_text_buffer_get_iter_at_mark(console->entry_buffer, &where, mark);
    - gtk_text_buffer_place_cursor(console->entry_buffer, &where);
    - gtk_text_buffer_delete_mark(console->entry_buffer, mark);
    -
    - gtk_text_buffer_end_user_action(console->entry_buffer);
    -}
    -
    -static void iq_clicked_cb(GtkWidget *w, gpointer nul)
    -{
    - GtkWidget *vbox, *hbox, *to_entry, *label, *type_combo;
    - GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    - const gchar *to;
    - int result;
    - char *stanza;
    -
    - GtkWidget *dialog = gtk_dialog_new_with_buttons("<iq/>",
    - GTK_WINDOW(console->window),
    - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
    - _("_Cancel"),
    - GTK_RESPONSE_REJECT,
    - _("_OK"),
    - GTK_RESPONSE_ACCEPT,
    - NULL);
    - gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
    - gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
    - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("To:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - to_entry = gtk_entry_new();
    - gtk_entry_set_activates_default (GTK_ENTRY (to_entry), TRUE);
    - gtk_box_pack_start(GTK_BOX(hbox), to_entry, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new("Type:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    -
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    - type_combo = gtk_combo_box_text_new();
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "get");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "set");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "result");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "error");
    - gtk_combo_box_set_active(GTK_COMBO_BOX(type_combo), 0);
    - gtk_box_pack_start(GTK_BOX(hbox), type_combo, FALSE, FALSE, 0);
    -
    - gtk_widget_show_all(vbox);
    -
    - result = gtk_dialog_run(GTK_DIALOG(dialog));
    - if (result != GTK_RESPONSE_ACCEPT) {
    - gtk_widget_destroy(dialog);
    - return;
    - }
    -
    - to = gtk_entry_get_text(GTK_ENTRY(to_entry));
    - stanza = g_strdup_printf("<iq %s%s%s id='console%x' type='%s'>",
    - to && *to ? "to='" : "",
    - to && *to ? to : "",
    - to && *to ? "'" : "",
    - g_random_int(),
    - gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(type_combo)));
    - load_text_and_set_caret(stanza, "</iq>");
    - gtk_widget_grab_focus(console->entry);
    - g_free(stanza);
    -
    - gtk_widget_destroy(dialog);
    - g_object_unref(sg);
    -}
    -
    -static void presence_clicked_cb(GtkWidget *w, gpointer nul)
    -{
    - GtkWidget *vbox;
    - GtkWidget *hbox;
    - GtkWidget *to_entry;
    - GtkWidget *status_entry;
    - GtkWidget *priority_entry;
    - GtkWidget *label;
    - GtkWidget *show_combo;
    - GtkWidget *type_combo;
    - GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    - const gchar *to, *status, *priority;
    - gchar *type, *show;
    - int result;
    - char *stanza;
    -
    - GtkWidget *dialog = gtk_dialog_new_with_buttons("<presence/>",
    - GTK_WINDOW(console->window),
    - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
    - _("_Cancel"),
    - GTK_RESPONSE_REJECT,
    - _("_OK"),
    - GTK_RESPONSE_ACCEPT,
    - NULL);
    - gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
    - gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
    - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("To:");
    - gtk_size_group_add_widget(sg, label);
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - to_entry = gtk_entry_new();
    - gtk_entry_set_activates_default (GTK_ENTRY (to_entry), TRUE);
    - gtk_box_pack_start(GTK_BOX(hbox), to_entry, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new("Type:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    - type_combo = gtk_combo_box_text_new();
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "default");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "unavailable");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "subscribe");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "unsubscribe");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "subscribed");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "unsubscribed");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "probe");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "error");
    - gtk_combo_box_set_active(GTK_COMBO_BOX(type_combo), 0);
    - gtk_box_pack_start(GTK_BOX(hbox), type_combo, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new("Show:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    - show_combo = gtk_combo_box_text_new();
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo), "default");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo), "away");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo), "dnd");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo), "xa");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(show_combo), "chat");
    -
    - gtk_combo_box_set_active(GTK_COMBO_BOX(show_combo), 0);
    - gtk_box_pack_start(GTK_BOX(hbox), show_combo, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("Status:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - status_entry = gtk_entry_new();
    - gtk_entry_set_activates_default (GTK_ENTRY (status_entry), TRUE);
    - gtk_box_pack_start(GTK_BOX(hbox), status_entry, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("Priority:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - priority_entry = gtk_spin_button_new_with_range(-128, 127, 1);
    - gtk_spin_button_set_value(GTK_SPIN_BUTTON(priority_entry), 0);
    - gtk_box_pack_start(GTK_BOX(hbox), priority_entry, FALSE, FALSE, 0);
    -
    - gtk_widget_show_all(vbox);
    -
    - result = gtk_dialog_run(GTK_DIALOG(dialog));
    - if (result != GTK_RESPONSE_ACCEPT) {
    - gtk_widget_destroy(dialog);
    - return;
    - }
    -
    - to = gtk_entry_get_text(GTK_ENTRY(to_entry));
    - type = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(type_combo));
    - if (purple_strequal(type, "default")) {
    - g_free(type);
    - type = g_strdup("");
    - }
    - show = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(show_combo));
    - if (purple_strequal(show, "default")) {
    - g_free(show);
    - show = g_strdup("");
    - }
    - status = gtk_entry_get_text(GTK_ENTRY(status_entry));
    - priority = gtk_entry_get_text(GTK_ENTRY(priority_entry));
    - if (purple_strequal(priority, "0"))
    - priority = "";
    -
    - stanza = g_strdup_printf("<presence %s%s%s id='console%x' %s%s%s>"
    - "%s%s%s%s%s%s%s%s%s",
    - *to ? "to='" : "",
    - *to ? to : "",
    - *to ? "'" : "",
    - g_random_int(),
    -
    - *type ? "type='" : "",
    - *type ? type : "",
    - *type ? "'" : "",
    -
    - *show ? "<show>" : "",
    - *show ? show : "",
    - *show ? "</show>" : "",
    -
    - *status ? "<status>" : "",
    - *status ? status : "",
    - *status ? "</status>" : "",
    -
    - *priority ? "<priority>" : "",
    - *priority ? priority : "",
    - *priority ? "</priority>" : "");
    -
    - load_text_and_set_caret(stanza, "</presence>");
    - gtk_widget_grab_focus(console->entry);
    - g_free(stanza);
    - g_free(type);
    - g_free(show);
    -
    - gtk_widget_destroy(dialog);
    - g_object_unref(sg);
    -}
    -
    -static void message_clicked_cb(GtkWidget *w, gpointer nul)
    -{
    - GtkWidget *vbox;
    - GtkWidget *hbox;
    - GtkWidget *to_entry;
    - GtkWidget *body_entry;
    - GtkWidget *thread_entry;
    - GtkWidget *subject_entry;
    - GtkWidget *label;
    - GtkWidget *type_combo;
    - GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
    - const gchar *to, *body, *thread, *subject;
    - char *stanza;
    - int result;
    -
    - GtkWidget *dialog = gtk_dialog_new_with_buttons("<message/>",
    - GTK_WINDOW(console->window),
    - GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
    - _("_Cancel"),
    - GTK_RESPONSE_REJECT,
    - _("_OK"),
    - GTK_RESPONSE_ACCEPT,
    - NULL);
    - gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
    - gtk_container_set_border_width(GTK_CONTAINER(dialog), 12);
    - vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("To:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - to_entry = gtk_entry_new();
    - gtk_entry_set_activates_default (GTK_ENTRY (to_entry), TRUE);
    - gtk_box_pack_start(GTK_BOX(hbox), to_entry, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    - label = gtk_label_new("Type:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    - type_combo = gtk_combo_box_text_new();
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "chat");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "headline");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "groupchat");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "normal");
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(type_combo), "error");
    - gtk_combo_box_set_active(GTK_COMBO_BOX(type_combo), 0);
    - gtk_box_pack_start(GTK_BOX(hbox), type_combo, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("Body:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - body_entry = gtk_entry_new();
    - gtk_entry_set_activates_default (GTK_ENTRY (body_entry), TRUE);
    - gtk_box_pack_start(GTK_BOX(hbox), body_entry, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("Subject:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - subject_entry = gtk_entry_new();
    - gtk_entry_set_activates_default (GTK_ENTRY (subject_entry), TRUE);
    - gtk_box_pack_start(GTK_BOX(hbox), subject_entry, FALSE, FALSE, 0);
    -
    - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
    -
    - label = gtk_label_new("Thread:");
    - gtk_label_set_xalign(GTK_LABEL(label), 0);
    - gtk_size_group_add_widget(sg, label);
    - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
    -
    - thread_entry = gtk_entry_new();
    - gtk_entry_set_activates_default (GTK_ENTRY (thread_entry), TRUE);
    - gtk_box_pack_start(GTK_BOX(hbox), thread_entry, FALSE, FALSE, 0);
    -
    - gtk_widget_show_all(vbox);
    -
    - result = gtk_dialog_run(GTK_DIALOG(dialog));
    - if (result != GTK_RESPONSE_ACCEPT) {
    - gtk_widget_destroy(dialog);
    - return;
    - }
    -
    - to = gtk_entry_get_text(GTK_ENTRY(to_entry));
    - body = gtk_entry_get_text(GTK_ENTRY(body_entry));
    - thread = gtk_entry_get_text(GTK_ENTRY(thread_entry));
    - subject = gtk_entry_get_text(GTK_ENTRY(subject_entry));
    -
    - stanza = g_strdup_printf("<message %s%s%s id='console%x' type='%s'>"
    - "%s%s%s%s%s%s%s%s%s",
    -
    - *to ? "to='" : "",
    - *to ? to : "",
    - *to ? "'" : "",
    - g_random_int(),
    - gtk_combo_box_text_get_active_text(
    - GTK_COMBO_BOX_TEXT(type_combo)),
    -
    - *body ? "<body>" : "",
    - *body ? body : "",
    - *body ? "</body>" : "",
    -
    - *subject ? "<subject>" : "",
    - *subject ? subject : "",
    - *subject ? "</subject>" : "",
    -
    - *thread ? "<thread>" : "",
    - *thread ? thread : "",
    - *thread ? "</thread>" : "");
    -
    - load_text_and_set_caret(stanza, "</message>");
    - gtk_widget_grab_focus(console->entry);
    - g_free(stanza);
    -
    - gtk_widget_destroy(dialog);
    - g_object_unref(sg);
    -}
    -
    -static void
    -signing_on_cb(PurpleConnection *gc)
    -{
    - PurpleAccount *account;
    -
    - if (!console)
    - return;
    -
    - account = purple_connection_get_account(gc);
    - if (!xmppconsole_is_xmpp_account(account))
    - return;
    -
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console->dropdown),
    - purple_account_get_username(account));
    - console->accounts = g_list_append(console->accounts, gc);
    - console->count++;
    -
    - if (console->count == 1) {
    - console->gc = gc;
    - gtk_text_buffer_set_text(console->buffer, "", 0);
    - gtk_combo_box_set_active(GTK_COMBO_BOX(console->dropdown), 0);
    - } else
    - gtk_widget_show_all(console->hbox);
    -}
    -
    -static void
    -signed_off_cb(PurpleConnection *gc)
    -{
    - int i = 0;
    - GList *l;
    -
    - if (!console)
    - return;
    -
    - l = console->accounts;
    - while (l) {
    - PurpleConnection *g = l->data;
    - if (gc == g)
    - break;
    - i++;
    - l = l->next;
    - }
    -
    - if (l == NULL)
    - return;
    -
    - gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(console->dropdown), i);
    - console->accounts = g_list_remove(console->accounts, gc);
    - console->count--;
    -
    - if (gc == console->gc) {
    - GtkTextIter end;
    - gtk_text_buffer_get_end_iter(console->buffer, &end);
    - gtk_text_buffer_insert_with_tags(console->buffer, &end, _("Logged out."), -1,
    - console->tags.info, NULL);
    - console->gc = NULL;
    - }
    -}
    -
    -static void
    -console_destroy(GtkWidget *window, gpointer nul)
    -{
    - g_list_free(console->accounts);
    - g_free(console);
    - console = NULL;
    -}
    -
    -static void
    -dropdown_changed_cb(GtkComboBox *widget, gpointer nul)
    -{
    - if (!console)
    - return;
    -
    - console->gc = g_list_nth_data(console->accounts, gtk_combo_box_get_active(GTK_COMBO_BOX(console->dropdown)));
    - gtk_text_buffer_set_text(console->buffer, "", 0);
    -}
    -
    -static void
    -create_console(PurplePluginAction *action)
    -{
    - GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
    - GtkWidget *label;
    - GtkWidget *toolbar;
    - GList *connections;
    - GtkToolItem *button;
    - GtkCssProvider *entry_css;
    - GtkStyleContext *context;
    -
    - if (console) {
    - gtk_window_present(GTK_WINDOW(console->window));
    - return;
    - }
    -
    - console = g_new0(XmppConsole, 1);
    -
    - console->window = pidgin_create_window(_("XMPP Console"), PIDGIN_HIG_BORDER, NULL, TRUE);
    - g_signal_connect(G_OBJECT(console->window), "destroy", G_CALLBACK(console_destroy), NULL);
    - gtk_window_set_default_size(GTK_WINDOW(console->window), 580, 400);
    - gtk_container_add(GTK_CONTAINER(console->window), vbox);
    -
    - console->hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);
    - gtk_box_pack_start(GTK_BOX(vbox), console->hbox, FALSE, FALSE, 0);
    - label = gtk_label_new(_("Account: "));
    - gtk_label_set_xalign(GTK_LABEL(label), 0.0);
    - gtk_box_pack_start(GTK_BOX(console->hbox), label, FALSE, FALSE, 0);
    - console->dropdown = gtk_combo_box_text_new();
    - for (connections = purple_connections_get_all(); connections; connections = connections->next) {
    - PurpleConnection *gc = connections->data;
    - if (xmppconsole_is_xmpp_account(purple_connection_get_account(gc))) {
    - console->count++;
    - console->accounts = g_list_append(console->accounts, gc);
    - gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console->dropdown),
    - purple_account_get_username(purple_connection_get_account(gc)));
    - if (!console->gc)
    - console->gc = gc;
    - }
    - }
    - gtk_combo_box_set_active(GTK_COMBO_BOX(console->dropdown), 0);
    - gtk_box_pack_start(GTK_BOX(console->hbox), console->dropdown, TRUE, TRUE, 0);
    - g_signal_connect(G_OBJECT(console->dropdown), "changed", G_CALLBACK(dropdown_changed_cb), NULL);
    -
    - console->view = gtk_text_view_new();
    - gtk_text_view_set_editable(GTK_TEXT_VIEW(console->view), FALSE);
    - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(console->view), GTK_WRAP_WORD);
    -
    - console->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->view));
    - console->tags.info = gtk_text_buffer_create_tag(console->buffer, "info",
    - "foreground", "#777777", NULL);
    - console->tags.incoming = gtk_text_buffer_create_tag(console->buffer, "incoming",
    - "paragraph-background", "#ffcece", NULL);
    - console->tags.outgoing = gtk_text_buffer_create_tag(console->buffer, "outgoing",
    - "paragraph-background", "#dcecc4", NULL);
    - console->tags.bracket = gtk_text_buffer_create_tag(console->buffer, "bracket",
    - "foreground", "#940f8c", NULL);
    - console->tags.tag = gtk_text_buffer_create_tag(console->buffer, "tag",
    - "foreground", "#8b1dab",
    - "weight", PANGO_WEIGHT_BOLD, NULL);
    - console->tags.attr = gtk_text_buffer_create_tag(console->buffer, "attr",
    - "foreground", "#a02961",
    - "weight", PANGO_WEIGHT_BOLD, NULL);
    - console->tags.value = gtk_text_buffer_create_tag(console->buffer, "value",
    - "foreground", "#324aa4", NULL);
    - console->tags.xmlns = gtk_text_buffer_create_tag(console->buffer, "xmlns",
    - "foreground", "#2cb12f",
    - "weight", PANGO_WEIGHT_BOLD, NULL);
    -
    - if (console->count == 0) {
    - GtkTextIter start, end;
    - gtk_text_buffer_set_text(console->buffer, _("Not connected to XMPP"), -1);
    - gtk_text_buffer_get_bounds(console->buffer, &start, &end);
    - gtk_text_buffer_apply_tag(console->buffer, console->tags.info, &start, &end);
    - }
    - gtk_box_pack_start(GTK_BOX(vbox),
    - pidgin_make_scrollable(console->view, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_ETCHED_IN, -1, -1),
    - TRUE, TRUE, 0);
    -
    - toolbar = gtk_toolbar_new();
    - button = gtk_tool_button_new(NULL, "<iq/>");
    - gtk_tool_item_set_is_important(button, TRUE);
    - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(iq_clicked_cb), NULL);
    - gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
    -
    - button = gtk_tool_button_new(NULL, "<presence/>");
    - gtk_tool_item_set_is_important(button, TRUE);
    - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(presence_clicked_cb), NULL);
    - gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
    -
    - button = gtk_tool_button_new(NULL, "<message/>");
    - gtk_tool_item_set_is_important(button, TRUE);
    - g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(message_clicked_cb), NULL);
    - gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(button));
    -
    - gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
    -
    - console->entry = gtk_text_view_new();
    - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(console->entry), GTK_WRAP_WORD);
    - entry_css = gtk_css_provider_new();
    - gtk_css_provider_load_from_data(entry_css,
    - "textview." GTK_STYLE_CLASS_ERROR " text {background-color:#ffcece;}",
    - -1, NULL);
    - context = gtk_widget_get_style_context(console->entry);
    - gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(entry_css),
    - GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    - console->entry_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(console->entry));
    - g_signal_connect(G_OBJECT(console->entry),"key-press-event", G_CALLBACK(message_send_cb), console);
    -
    - console->sw = pidgin_make_scrollable(console->entry, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_ETCHED_IN, -1, -1);
    - gtk_box_pack_start(GTK_BOX(vbox), console->sw, FALSE, FALSE, 0);
    - g_signal_connect(G_OBJECT(console->entry_buffer), "changed", G_CALLBACK(entry_changed_cb), NULL);
    -
    - entry_changed_cb(console->entry_buffer, NULL);
    -
    - gtk_widget_show_all(console->window);
    - if (console->count < 2)
    - gtk_widget_hide(console->hbox);
    -}
    -
    -static GList *
    -actions(PurplePlugin *plugin)
    -{
    - GList *l = NULL;
    - PurplePluginAction *act = NULL;
    -
    - act = purple_plugin_action_new(_("XMPP Console"), create_console);
    - l = g_list_append(l, act);
    -
    - return l;
    -}
    -
    -static PidginPluginInfo *
    -plugin_query(GError **error)
    -{
    - const gchar * const authors[] = {
    - "Sean Egan <seanegan@gmail.com>",
    - NULL
    - };
    -
    - return pidgin_plugin_info_new(
    - "id", PLUGIN_ID,
    - "name", N_("XMPP Console"),
    - "version", DISPLAY_VERSION,
    - "category", N_("Protocol utility"),
    - "summary", N_("Send and receive raw XMPP stanzas."),
    - "description", N_("This plugin is useful for debugging XMPP servers "
    - "or clients."),
    - "authors", authors,
    - "website", PURPLE_WEBSITE,
    - "abi-version", PURPLE_ABI_VERSION,
    - "actions-cb", actions,
    - NULL
    - );
    -}
    -
    -static gboolean
    -plugin_load(PurplePlugin *plugin, GError **error)
    -{
    - int i;
    - gboolean any_registered = FALSE;
    -
    - xmpp_console_handle = plugin;
    -
    - i = 0;
    - while (xmpp_prpls[i] != NULL) {
    - PurpleProtocol *xmpp;
    -
    - xmpp = purple_protocols_find(xmpp_prpls[i]);
    - i++;
    -
    - if (!xmpp)
    - continue;
    - any_registered = TRUE;
    -
    - purple_signal_connect(xmpp, "jabber-receiving-xmlnode",
    - xmpp_console_handle,
    - PURPLE_CALLBACK(purple_xmlnode_received_cb), NULL);
    - purple_signal_connect(xmpp, "jabber-sending-text",
    - xmpp_console_handle,
    - PURPLE_CALLBACK(purple_xmlnode_sent_cb), NULL);
    - }
    -
    - if (!any_registered) {
    - g_set_error(error, PLUGIN_DOMAIN, 0, _("No XMPP protocol is loaded."));
    - return FALSE;
    - }
    -
    - purple_signal_connect(purple_connections_get_handle(), "signing-on",
    - plugin, PURPLE_CALLBACK(signing_on_cb), NULL);
    - purple_signal_connect(purple_connections_get_handle(), "signed-off",
    - plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
    -
    - return TRUE;
    -}
    -
    -static gboolean
    -plugin_unload(PurplePlugin *plugin, GError **error)
    -{
    - if (console)
    - gtk_widget_destroy(console->window);
    - return TRUE;
    -}
    -
    -PURPLE_PLUGIN_INIT(xmppconsole, plugin_query, plugin_load, plugin_unload);
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/plugins/xmppconsole/console.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,597 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!-- Generated with glade 3.22.1 -->
    +<interface>
    + <requires lib="gtk+" version="3.18"/>
    + <object class="GtkTextBuffer" id="entry_buffer">
    + <signal name="changed" handler="entry_changed_cb" swapped="no"/>
    + </object>
    + <object class="GtkAdjustment" id="presence.priority_adjustment">
    + <property name="lower">-128</property>
    + <property name="upper">127</property>
    + <property name="step_increment">1</property>
    + <property name="page_increment">10</property>
    + </object>
    + <object class="GtkTextTagTable" id="tags.table">
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.info">
    + <property name="foreground_rgba">rgb(119,119,119)</property>
    + </object>
    + </child>
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.incoming">
    + <property name="paragraph_background_rgba">rgb(255,206,206)</property>
    + </object>
    + </child>
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.outgoing">
    + <property name="paragraph_background_rgba">rgb(220,236,196)</property>
    + </object>
    + </child>
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.bracket">
    + <property name="foreground_rgba">rgb(148,15,140)</property>
    + </object>
    + </child>
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.tag">
    + <property name="foreground_rgba">rgb(139,29,171)</property>
    + <property name="weight">700</property>
    + </object>
    + </child>
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.attr">
    + <property name="foreground_rgba">rgb(160,41,97)</property>
    + <property name="weight">700</property>
    + </object>
    + </child>
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.value">
    + <property name="foreground_rgba">rgb(50,74,164)</property>
    + </object>
    + </child>
    + <child type="tag">
    + <object class="GtkTextTag" id="tags.xmlns">
    + <property name="foreground_rgba">rgb(44,177,47)</property>
    + <property name="weight">700</property>
    + </object>
    + </child>
    + </object>
    + <object class="GtkTextBuffer" id="buffer">
    + <property name="tag_table">tags.table</property>
    + </object>
    + <object class="GtkWindow" id="PidginXmppConsole">
    + <property name="can_focus">False</property>
    + <property name="title" translatable="yes">XMPP Console</property>
    + <property name="default_width">580</property>
    + <property name="default_height">400</property>
    + <signal name="destroy" handler="console_destroy" swapped="no"/>
    + <child>
    + <placeholder/>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <child>
    + <object class="GtkBox" id="hbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="margin_left">6</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Account:</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBoxText" id="dropdown">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <signal name="changed" handler="dropdown_changed_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkScrolledWindow">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="shadow_type">etched-in</property>
    + <child>
    + <object class="GtkTextView">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="editable">False</property>
    + <property name="wrap_mode">word</property>
    + <property name="buffer">buffer</property>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToolbar">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="toolbar_style">text</property>
    + <child>
    + <object class="GtkToggleToolButton" id="iq">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label">&lt;iq/&gt;</property>
    + <signal name="toggled" handler="toggle_button_toggled_cb" object="iq.popover" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="homogeneous">False</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToggleToolButton" id="presence">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label">&lt;presence/&gt;</property>
    + <signal name="toggled" handler="toggle_button_toggled_cb" object="presence.popover" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="homogeneous">False</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToggleToolButton" id="message">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label">&lt;message/&gt;</property>
    + <signal name="toggled" handler="toggle_button_toggled_cb" object="message.popover" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="homogeneous">False</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkScrolledWindow" id="sw">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="shadow_type">etched-in</property>
    + <child>
    + <object class="GtkTextView" id="entry">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="wrap_mode">word</property>
    + <property name="buffer">entry_buffer</property>
    + <signal name="key-press-event" handler="message_send_cb" object="PidginXmppConsole" swapped="no"/>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + <object class="GtkPopover" id="iq.popover">
    + <property name="can_focus">False</property>
    + <property name="relative_to">iq</property>
    + <property name="position">right</property>
    + <signal name="closed" handler="popover_closed_cb" object="iq" swapped="no"/>
    + <child>
    + <object class="GtkGrid">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="column_spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">To:</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Type:</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="iq.to">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="activates_default">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBoxText" id="iq.type">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="active">0</property>
    + <items>
    + <item>get</item>
    + <item>set</item>
    + <item>result</item>
    + <item>error</item>
    + </items>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">Insert</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="can_default">True</property>
    + <property name="has_default">True</property>
    + <property name="receives_default">True</property>
    + <signal name="clicked" handler="iq_clicked_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">2</property>
    + <property name="width">2</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + <object class="GtkPopover" id="message.popover">
    + <property name="can_focus">False</property>
    + <property name="relative_to">message</property>
    + <property name="position">right</property>
    + <signal name="closed" handler="popover_closed_cb" object="message" swapped="no"/>
    + <child>
    + <object class="GtkGrid">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="column_spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">To:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Type:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Body:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Subject:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Thread:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="message.to">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="activates_default">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="message.body">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="activates_default">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="message.subject">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="activates_default">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="message.thread">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="activates_default">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">Insert</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="can_default">True</property>
    + <property name="has_default">True</property>
    + <property name="receives_default">True</property>
    + <signal name="clicked" handler="message_clicked_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">5</property>
    + <property name="width">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBoxText" id="message.type">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="active">0</property>
    + <items>
    + <item>chat</item>
    + <item>headline</item>
    + <item>groupchat</item>
    + <item>normal</item>
    + <item>error</item>
    + </items>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + <object class="GtkPopover" id="presence.popover">
    + <property name="can_focus">False</property>
    + <property name="relative_to">presence</property>
    + <property name="position">right</property>
    + <signal name="closed" handler="popover_closed_cb" object="presence" swapped="no"/>
    + <child>
    + <object class="GtkGrid">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="column_spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">To:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Type:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Show:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Status:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Priority:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="presence.to">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBoxText" id="presence.type">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="active">0</property>
    + <items>
    + <item>default</item>
    + <item>unavailable</item>
    + <item>subscribe</item>
    + <item>unsubscribe</item>
    + <item>subscribed</item>
    + <item>unsubscribed</item>
    + <item>probe</item>
    + <item>error</item>
    + </items>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBoxText" id="presence.show">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="active">0</property>
    + <items>
    + <item>default</item>
    + <item>away</item>
    + <item>dnd</item>
    + <item>xa</item>
    + <item>chat</item>
    + </items>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="presence.status">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="activates_default">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">Insert</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="can_default">True</property>
    + <property name="has_default">True</property>
    + <property name="receives_default">True</property>
    + <signal name="clicked" handler="presence_clicked_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">5</property>
    + <property name="width">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="presence.priority">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="adjustment">presence.priority_adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">4</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    +</interface>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/plugins/xmppconsole/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,11 @@
    +if PLUGINS
    + xmppconsole_resource = gnome.compile_resources('xmppconsoleresources',
    + 'xmppconsole.gresource.xml',
    + c_name : 'xmppconsole')
    +
    + library('xmppconsole',
    + 'xmppconsole.c', xmppconsole_resource,
    + dependencies : [libpurple_dep, libpidgin_dep, glib],
    + name_prefix : '',
    + install : true, install_dir : PIDGIN_PLUGINDIR)
    +endif # PLUGINS
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/plugins/xmppconsole/xmppconsole.c Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,788 @@
    +/*
    + * Purple - XMPP debugging tool
    + *
    + * Pidgin is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * This program is free software; you can redistribute it and/or modify
    + * it under the terms of the GNU General Public License as published by
    + * the Free Software Foundation; either version 2 of the License, or
    + * (at your option) any later version.
    + *
    + * This program is distributed in the hope that it will be useful,
    + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    + * GNU General Public License for more details.
    + *
    + * You should have received a copy of the GNU General Public License
    + * along with this program; if not, write to the Free Software
    + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    +
    +#include "internal.h"
    +#include "gtkplugin.h"
    +#include "version.h"
    +#include "protocol.h"
    +#include "xmlnode.h"
    +
    +#include "gtkutils.h"
    +
    +#include <gdk/gdkkeysyms.h>
    +
    +#include "gtk3compat.h"
    +
    +#define PLUGIN_ID "gtk-xmpp"
    +#define PLUGIN_DOMAIN (g_quark_from_static_string(PLUGIN_ID))
    +
    +typedef struct {
    + PurpleConnection *gc;
    + GtkWidget *window;
    + GtkWidget *hbox;
    + GtkWidget *dropdown;
    + GtkTextBuffer *buffer;
    + struct {
    + GtkTextTag *info;
    + GtkTextTag *incoming;
    + GtkTextTag *outgoing;
    + GtkTextTag *bracket;
    + GtkTextTag *tag;
    + GtkTextTag *attr;
    + GtkTextTag *value;
    + GtkTextTag *xmlns;
    + } tags;
    + GtkWidget *entry;
    + GtkTextBuffer *entry_buffer;
    + GtkWidget *sw;
    + int count;
    + GList *accounts;
    +
    + struct {
    + GtkPopover *popover;
    + GtkEntry *to;
    + GtkComboBoxText *type;
    + } iq;
    +
    + struct {
    + GtkPopover *popover;
    + GtkEntry *to;
    + GtkComboBoxText *type;
    + GtkComboBoxText *show;
    + GtkEntry *status;
    + GtkEntry *priority;
    + } presence;
    +
    + struct {
    + GtkPopover *popover;
    + GtkEntry *to;
    + GtkComboBoxText *type;
    + GtkEntry *body;
    + GtkEntry *subject;
    + GtkEntry *thread;
    + } message;
    +} XmppConsole;
    +
    +XmppConsole *console = NULL;
    +static void *xmpp_console_handle = NULL;
    +
    +static const gchar *xmpp_prpls[] = {
    + "prpl-jabber", "prpl-gtalk", NULL
    +};
    +
    +static gboolean
    +xmppconsole_is_xmpp_account(PurpleAccount *account)
    +{
    + const gchar *prpl_name;
    + int i;
    +
    + prpl_name = purple_account_get_protocol_id(account);
    +
    + i = 0;
    + while (xmpp_prpls[i] != NULL) {
    + if (purple_strequal(xmpp_prpls[i], prpl_name))
    + return TRUE;
    + i++;
    + }
    +
    + return FALSE;
    +}
    +
    +static void
    +purple_xmlnode_append_to_buffer(PurpleXmlNode *node, gint indent_level, GtkTextIter *iter, GtkTextTag *tag)
    +{
    + PurpleXmlNode *c;
    + gboolean need_end = FALSE, pretty = TRUE;
    + gint i;
    +
    + g_return_if_fail(node != NULL);
    +
    + for (i = 0; i < indent_level; i++) {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "\t", 1, tag, NULL);
    + }
    +
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "<", 1,
    + tag, console->tags.bracket, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, node->name, -1,
    + tag, console->tags.tag, NULL);
    +
    + if (node->xmlns) {
    + if ((!node->parent ||
    + !node->parent->xmlns ||
    + !purple_strequal(node->xmlns, node->parent->xmlns)) &&
    + !purple_strequal(node->xmlns, "jabber:client"))
    + {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, " ", 1,
    + tag, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "xmlns", 5,
    + tag, console->tags.attr, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "='", 2,
    + tag, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, node->xmlns, -1,
    + tag, console->tags.xmlns, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "'", 1,
    + tag, NULL);
    + }
    + }
    + for (c = node->child; c; c = c->next)
    + {
    + if (c->type == PURPLE_XMLNODE_TYPE_ATTRIB) {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, " ", 1,
    + tag, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, c->name, -1,
    + tag, console->tags.attr, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "='", 2,
    + tag, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, c->data, -1,
    + tag, console->tags.value, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "'", 1,
    + tag, NULL);
    + } else if (c->type == PURPLE_XMLNODE_TYPE_TAG || c->type == PURPLE_XMLNODE_TYPE_DATA) {
    + if (c->type == PURPLE_XMLNODE_TYPE_DATA)
    + pretty = FALSE;
    + need_end = TRUE;
    + }
    + }
    +
    + if (need_end) {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, ">", 1,
    + tag, console->tags.bracket, NULL);
    + if (pretty) {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "\n", 1,
    + tag, NULL);
    + }
    +
    + for (c = node->child; c; c = c->next)
    + {
    + if (c->type == PURPLE_XMLNODE_TYPE_TAG) {
    + purple_xmlnode_append_to_buffer(c, indent_level + 1, iter, tag);
    + } else if (c->type == PURPLE_XMLNODE_TYPE_DATA && c->data_sz > 0) {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, c->data, c->data_sz,
    + tag, NULL);
    + }
    + }
    +
    + if (pretty) {
    + for (i = 0; i < indent_level; i++) {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "\t", 1, tag, NULL);
    + }
    + }
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "<", 1,
    + tag, console->tags.bracket, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "/", 1,
    + tag, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, node->name, -1,
    + tag, console->tags.tag, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, ">", 1,
    + tag, console->tags.bracket, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "\n", 1,
    + tag, NULL);
    + } else {
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "/", 1,
    + tag, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, ">", 1,
    + tag, console->tags.bracket, NULL);
    + gtk_text_buffer_insert_with_tags(console->buffer, iter, "\n", 1,
    + tag, NULL);
    + }
    +}
    +
    +static void
    +purple_xmlnode_received_cb(PurpleConnection *gc, PurpleXmlNode **packet, gpointer null)
    +{
    + GtkTextIter iter;
    +
    + if (!console || console->gc != gc)
    + return;
    +
    + gtk_text_buffer_get_end_iter(console->buffer, &iter);
    + purple_xmlnode_append_to_buffer(*packet, 0, &iter, console->tags.incoming);
    +}
    +
    +static void
    +purple_xmlnode_sent_cb(PurpleConnection *gc, char **packet, gpointer null)
    +{
    + GtkTextIter iter;
    + PurpleXmlNode *node;
    +
    + if (!console || console->gc != gc)
    + return;
    + node = purple_xmlnode_from_str(*packet, -1);
    +
    + if (!node)
    + return;
    +
    + gtk_text_buffer_get_end_iter(console->buffer, &iter);
    + purple_xmlnode_append_to_buffer(node, 0, &iter, console->tags.outgoing);
    + purple_xmlnode_free(node);
    +}
    +
    +static gboolean
    +message_send_cb(GtkWidget *widget, GdkEventKey *event, gpointer p)
    +{
    + PurpleProtocol *protocol = NULL;
    + PurpleConnection *gc;
    + gchar *text;
    + GtkTextIter start, end;
    +
    + if (event->keyval != GDK_KEY_KP_Enter && event->keyval != GDK_KEY_Return)
    + return FALSE;
    +
    + gc = console->gc;
    +
    + if (gc)
    + protocol = purple_connection_get_protocol(gc);
    +
    + gtk_text_buffer_get_bounds(console->entry_buffer, &start, &end);
    + text = gtk_text_buffer_get_text(console->entry_buffer, &start, &end, FALSE);
    +
    + if (protocol)
    + purple_protocol_server_iface_send_raw(protocol, gc, text, strlen(text));
    +
    + g_free(text);
    + gtk_text_buffer_set_text(console->entry_buffer, "", 0);
    +
    + return TRUE;
    +}
    +
    +static void
    +entry_changed_cb(GtkTextBuffer *buffer, void *data)
    +{
    + GtkTextIter start, end;
    + char *xmlstr, *str;
    + GtkTextIter iter;
    + int wrapped_lines;
    + int lines;
    + GdkRectangle oneline;
    + int height;
    + int pad_top, pad_inside, pad_bottom;
    + PurpleXmlNode *node;
    + GtkStyleContext *style;
    +
    + wrapped_lines = 1;
    + gtk_text_buffer_get_start_iter(buffer, &iter);
    + gtk_text_view_get_iter_location(GTK_TEXT_VIEW(console->entry), &iter, &oneline);
    + while (gtk_text_view_forward_display_line(GTK_TEXT_VIEW(console->entry),
    + &iter)) {
    + wrapped_lines++;
    + }
    +
    + lines = gtk_text_buffer_get_line_count(buffer);
    +
    + /* Show a maximum of 64 lines */
    + lines = MIN(lines, 6);
    + wrapped_lines = MIN(wrapped_lines, 6);
    +
    + pad_top = gtk_text_view_get_pixels_above_lines(GTK_TEXT_VIEW(console->entry));
    + pad_bottom = gtk_text_view_get_pixels_below_lines(GTK_TEXT_VIEW(console->entry));
    + pad_inside = gtk_text_view_get_pixels_inside_wrap(GTK_TEXT_VIEW(console->entry));
    +
    + height = (oneline.height + pad_top + pad_bottom) * lines;
    + height += (oneline.height + pad_inside) * (wrapped_lines - lines);
    +
    + gtk_widget_set_size_request(console->sw, -1, height + 6);
    +
    + gtk_text_buffer_get_bounds(buffer, &start, &end);
    + str = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
    + if (!str) {
    + return;
    + }
    +
    + xmlstr = g_strdup_printf("<xml>%s</xml>", str);
    + node = purple_xmlnode_from_str(xmlstr, -1);
    + style = gtk_widget_get_style_context(console->entry);
    + if (node) {
    + gtk_style_context_remove_class(style, GTK_STYLE_CLASS_ERROR);
    + } else {
    + gtk_style_context_add_class(style, GTK_STYLE_CLASS_ERROR);
    + }
    + g_free(str);
    + g_free(xmlstr);
    + if (node)
    + purple_xmlnode_free(node);
    +}
    +
    +static void
    +load_text_and_set_caret(const gchar *pre_text, const gchar *post_text)
    +{
    + GtkTextIter where;
    + GtkTextMark *mark;
    +
    + gtk_text_buffer_begin_user_action(console->entry_buffer);
    +
    + gtk_text_buffer_set_text(console->entry_buffer, pre_text, -1);
    +
    + gtk_text_buffer_get_end_iter(console->entry_buffer, &where);
    + mark = gtk_text_buffer_create_mark(console->entry_buffer, NULL, &where, TRUE);
    +
    + gtk_text_buffer_insert(console->entry_buffer, &where, post_text, -1);
    +
    + gtk_text_buffer_get_iter_at_mark(console->entry_buffer, &where, mark);
    + gtk_text_buffer_place_cursor(console->entry_buffer, &where);
    + gtk_text_buffer_delete_mark(console->entry_buffer, mark);
    +
    + gtk_text_buffer_end_user_action(console->entry_buffer);
    +}
    +
    +static void
    +popover_closed_cb(GtkPopover *popover, gpointer data)
    +{
    + GtkToggleToolButton *button = GTK_TOGGLE_TOOL_BUTTON(data);
    +
    + gtk_toggle_tool_button_set_active(button, FALSE);
    +}
    +
    +static void
    +toggle_button_toggled_cb(GtkToolButton *button, gpointer data)
    +{
    + GtkPopover *popover = GTK_POPOVER(data);
    +
    + gtk_popover_popup(popover);
    +}
    +
    +static void
    +iq_clicked_cb(GtkWidget *w, gpointer data)
    +{
    + XmppConsole *console = (XmppConsole *)data;
    + const gchar *to;
    + char *stanza;
    +
    + to = gtk_entry_get_text(console->iq.to);
    + stanza = g_strdup_printf(
    + "<iq %s%s%s id='console%x' type='%s'>", to && *to ? "to='" : "",
    + to && *to ? to : "", to && *to ? "'" : "", g_random_int(),
    + gtk_combo_box_text_get_active_text(console->iq.type));
    + load_text_and_set_caret(stanza, "</iq>");
    + gtk_widget_grab_focus(console->entry);
    + g_free(stanza);
    +
    + /* Reset everything. */
    + gtk_entry_set_text(console->iq.to, "");
    + gtk_combo_box_set_active(GTK_COMBO_BOX(console->iq.type), 0);
    + gtk_popover_popdown(console->iq.popover);
    +}
    +
    +static void
    +presence_clicked_cb(GtkWidget *w, gpointer data)
    +{
    + XmppConsole *console = (XmppConsole *)data;
    + const gchar *to, *status, *priority;
    + gchar *type, *show;
    + char *stanza;
    +
    + to = gtk_entry_get_text(console->presence.to);
    + type = gtk_combo_box_text_get_active_text(console->presence.type);
    + if (purple_strequal(type, "default")) {
    + g_free(type);
    + type = g_strdup("");
    + }
    + show = gtk_combo_box_text_get_active_text(console->presence.show);
    + if (purple_strequal(show, "default")) {
    + g_free(show);
    + show = g_strdup("");
    + }
    + status = gtk_entry_get_text(console->presence.status);
    + priority = gtk_entry_get_text(console->presence.priority);
    + if (purple_strequal(priority, "0"))
    + priority = "";
    +
    + stanza = g_strdup_printf("<presence %s%s%s id='console%x' %s%s%s>"
    + "%s%s%s%s%s%s%s%s%s",
    + *to ? "to='" : "",
    + *to ? to : "",
    + *to ? "'" : "",
    + g_random_int(),
    +
    + *type ? "type='" : "",
    + *type ? type : "",
    + *type ? "'" : "",
    +
    + *show ? "<show>" : "",
    + *show ? show : "",
    + *show ? "</show>" : "",
    +
    + *status ? "<status>" : "",
    + *status ? status : "",
    + *status ? "</status>" : "",
    +
    + *priority ? "<priority>" : "",
    + *priority ? priority : "",
    + *priority ? "</priority>" : "");
    +
    + load_text_and_set_caret(stanza, "</presence>");
    + gtk_widget_grab_focus(console->entry);
    + g_free(stanza);
    + g_free(type);
    + g_free(show);
    +
    + /* Reset everything. */
    + gtk_entry_set_text(console->presence.to, "");
    + gtk_combo_box_set_active(GTK_COMBO_BOX(console->presence.type), 0);
    + gtk_combo_box_set_active(GTK_COMBO_BOX(console->presence.show), 0);
    + gtk_entry_set_text(console->presence.status, "");
    + gtk_entry_set_text(console->presence.priority, "0");
    + gtk_popover_popdown(console->presence.popover);
    +}
    +
    +static void
    +message_clicked_cb(GtkWidget *w, gpointer data)
    +{
    + XmppConsole *console = (XmppConsole *)data;
    + const gchar *to, *body, *thread, *subject;
    + char *stanza;
    +
    + to = gtk_entry_get_text(console->message.to);
    + body = gtk_entry_get_text(console->message.body);
    + thread = gtk_entry_get_text(console->message.thread);
    + subject = gtk_entry_get_text(console->message.subject);
    +
    + stanza = g_strdup_printf(
    + "<message %s%s%s id='console%x' type='%s'>"
    + "%s%s%s%s%s%s%s%s%s",
    +
    + *to ? "to='" : "", *to ? to : "", *to ? "'" : "",
    + g_random_int(),
    + gtk_combo_box_text_get_active_text(console->message.type),
    +
    + *body ? "<body>" : "", *body ? body : "",
    + *body ? "</body>" : "",
    +
    + *subject ? "<subject>" : "", *subject ? subject : "",
    + *subject ? "</subject>" : "",
    +
    + *thread ? "<thread>" : "", *thread ? thread : "",
    + *thread ? "</thread>" : "");
    +
    + load_text_and_set_caret(stanza, "</message>");
    + gtk_widget_grab_focus(console->entry);
    + g_free(stanza);
    +
    + /* Reset everything. */
    + gtk_entry_set_text(console->message.to, "");
    + gtk_combo_box_set_active(GTK_COMBO_BOX(console->message.type), 0);
    + gtk_entry_set_text(console->message.body, "");
    + gtk_entry_set_text(console->message.subject, "0");
    + gtk_entry_set_text(console->message.thread, "0");
    + gtk_popover_popdown(console->message.popover);
    +}
    +
    +static void
    +signing_on_cb(PurpleConnection *gc)
    +{
    + PurpleAccount *account;
    +
    + if (!console)
    + return;
    +
    + account = purple_connection_get_account(gc);
    + if (!xmppconsole_is_xmpp_account(account))
    + return;
    +
    + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console->dropdown),
    + purple_account_get_username(account));
    + console->accounts = g_list_append(console->accounts, gc);
    + console->count++;
    +
    + if (console->count == 1) {
    + console->gc = gc;
    + gtk_text_buffer_set_text(console->buffer, "", 0);
    + gtk_combo_box_set_active(GTK_COMBO_BOX(console->dropdown), 0);
    + } else
    + gtk_widget_show_all(console->hbox);
    +}
    +
    +static void
    +signed_off_cb(PurpleConnection *gc)
    +{
    + int i = 0;
    + GList *l;
    +
    + if (!console)
    + return;
    +
    + l = console->accounts;
    + while (l) {
    + PurpleConnection *g = l->data;
    + if (gc == g)
    + break;
    + i++;
    + l = l->next;
    + }
    +
    + if (l == NULL)
    + return;
    +
    + gtk_combo_box_text_remove(GTK_COMBO_BOX_TEXT(console->dropdown), i);
    + console->accounts = g_list_remove(console->accounts, gc);
    + console->count--;
    +
    + if (gc == console->gc) {
    + GtkTextIter end;
    + gtk_text_buffer_get_end_iter(console->buffer, &end);
    + gtk_text_buffer_insert_with_tags(console->buffer, &end, _("Logged out."), -1,
    + console->tags.info, NULL);
    + console->gc = NULL;
    + }
    +}
    +
    +static void
    +console_destroy(GtkWidget *window, gpointer nul)
    +{
    + g_list_free(console->accounts);
    + g_free(console);
    + console = NULL;
    +}
    +
    +static void
    +dropdown_changed_cb(GtkComboBox *widget, gpointer nul)
    +{
    + if (!console)
    + return;
    +
    + console->gc = g_list_nth_data(console->accounts, gtk_combo_box_get_active(GTK_COMBO_BOX(console->dropdown)));
    + gtk_text_buffer_set_text(console->buffer, "", 0);
    +}
    +
    +static void
    +create_console(PurplePluginAction *action)
    +{
    + GtkBuilder *builder;
    + GList *connections;
    + GtkCssProvider *entry_css;
    + GtkStyleContext *context;
    +
    + if (console) {
    + gtk_window_present(GTK_WINDOW(console->window));
    + return;
    + }
    +
    + console = g_new0(XmppConsole, 1);
    +
    + builder = gtk_builder_new_from_resource(
    + "/im/pidgin/Pidgin/Plugin/XMPPConsole/console.ui");
    + gtk_builder_set_translation_domain(builder, PACKAGE);
    + console->window = GTK_WIDGET(
    + gtk_builder_get_object(builder, "PidginXmppConsole"));
    + gtk_builder_add_callback_symbol(builder, "console_destroy",
    + G_CALLBACK(console_destroy));
    +
    + console->hbox = GTK_WIDGET(gtk_builder_get_object(builder, "hbox"));
    + console->dropdown =
    + GTK_WIDGET(gtk_builder_get_object(builder, "dropdown"));
    + for (connections = purple_connections_get_all(); connections; connections = connections->next) {
    + PurpleConnection *gc = connections->data;
    + if (xmppconsole_is_xmpp_account(purple_connection_get_account(gc))) {
    + console->count++;
    + console->accounts = g_list_append(console->accounts, gc);
    + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(console->dropdown),
    + purple_account_get_username(purple_connection_get_account(gc)));
    + if (!console->gc)
    + console->gc = gc;
    + }
    + }
    + gtk_combo_box_set_active(GTK_COMBO_BOX(console->dropdown), 0);
    + gtk_builder_add_callback_symbol(builder, "dropdown_changed_cb",
    + G_CALLBACK(dropdown_changed_cb));
    +
    + console->buffer =
    + GTK_TEXT_BUFFER(gtk_builder_get_object(builder, "buffer"));
    + console->tags.info =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.info"));
    + console->tags.incoming =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.incoming"));
    + console->tags.outgoing =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.outgoing"));
    + console->tags.bracket =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.bracket"));
    + console->tags.tag =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.tag"));
    + console->tags.attr =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.attr"));
    + console->tags.value =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.value"));
    + console->tags.xmlns =
    + GTK_TEXT_TAG(gtk_builder_get_object(builder, "tags.xmlns"));
    +
    + if (console->count == 0) {
    + GtkTextIter start, end;
    + gtk_text_buffer_set_text(console->buffer, _("Not connected to XMPP"), -1);
    + gtk_text_buffer_get_bounds(console->buffer, &start, &end);
    + gtk_text_buffer_apply_tag(console->buffer, console->tags.info, &start, &end);
    + }
    +
    + /* Popover for <iq/> button. */
    + console->iq.popover =
    + GTK_POPOVER(gtk_builder_get_object(builder, "iq.popover"));
    + console->iq.to = GTK_ENTRY(gtk_builder_get_object(builder, "iq.to"));
    + console->iq.type =
    + GTK_COMBO_BOX_TEXT(gtk_builder_get_object(builder, "iq.type"));
    +
    + /* Popover for <presence/> button. */
    + console->presence.popover = GTK_POPOVER(
    + gtk_builder_get_object(builder, "presence.popover"));
    + console->presence.to =
    + GTK_ENTRY(gtk_builder_get_object(builder, "presence.to"));
    + console->presence.type = GTK_COMBO_BOX_TEXT(
    + gtk_builder_get_object(builder, "presence.type"));
    + console->presence.show = GTK_COMBO_BOX_TEXT(
    + gtk_builder_get_object(builder, "presence.show"));
    + console->presence.status =
    + GTK_ENTRY(gtk_builder_get_object(builder, "presence.status"));
    + console->presence.priority =
    + GTK_ENTRY(gtk_builder_get_object(builder, "presence.priority"));
    +
    + /* Popover for <message/> button. */
    + console->message.popover =
    + GTK_POPOVER(gtk_builder_get_object(builder, "message.popover"));
    + console->message.to =
    + GTK_ENTRY(gtk_builder_get_object(builder, "message.to"));
    + console->message.type = GTK_COMBO_BOX_TEXT(
    + gtk_builder_get_object(builder, "message.type"));
    + console->message.body =
    + GTK_ENTRY(gtk_builder_get_object(builder, "message.body"));
    + console->message.subject =
    + GTK_ENTRY(gtk_builder_get_object(builder, "message.subject"));
    + console->message.thread =
    + GTK_ENTRY(gtk_builder_get_object(builder, "message.thread"));
    +
    + gtk_builder_add_callback_symbols(
    + builder, "toggle_button_toggled_cb",
    + G_CALLBACK(toggle_button_toggled_cb), "popover_closed_cb",
    + G_CALLBACK(popover_closed_cb), "iq_clicked_cb",
    + G_CALLBACK(iq_clicked_cb), "presence_clicked_cb",
    + G_CALLBACK(presence_clicked_cb), "message_clicked_cb",
    + G_CALLBACK(message_clicked_cb), NULL);
    +
    + console->entry = GTK_WIDGET(gtk_builder_get_object(builder, "entry"));
    + entry_css = gtk_css_provider_new();
    + gtk_css_provider_load_from_data(entry_css,
    + "textview." GTK_STYLE_CLASS_ERROR " text {background-color:#ffcece;}",
    + -1, NULL);
    + context = gtk_widget_get_style_context(console->entry);
    + gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(entry_css),
    + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    + console->entry_buffer = GTK_TEXT_BUFFER(
    + gtk_builder_get_object(builder, "entry_buffer"));
    + gtk_builder_add_callback_symbol(builder, "message_send_cb",
    + G_CALLBACK(message_send_cb));
    +
    + console->sw = GTK_WIDGET(gtk_builder_get_object(builder, "sw"));
    + gtk_builder_add_callback_symbol(builder, "entry_changed_cb",
    + G_CALLBACK(entry_changed_cb));
    +
    + entry_changed_cb(console->entry_buffer, NULL);
    +
    + gtk_widget_show_all(console->window);
    + if (console->count < 2)
    + gtk_widget_hide(console->hbox);
    +
    + gtk_builder_connect_signals(builder, console);
    + g_object_unref(builder);
    +}
    +
    +static GList *
    +actions(PurplePlugin *plugin)
    +{
    + GList *l = NULL;
    + PurplePluginAction *act = NULL;
    +
    + act = purple_plugin_action_new(_("XMPP Console"), create_console);
    + l = g_list_append(l, act);
    +
    + return l;
    +}
    +
    +static PidginPluginInfo *
    +plugin_query(GError **error)
    +{
    + const gchar * const authors[] = {
    + "Sean Egan <seanegan@gmail.com>",
    + NULL
    + };
    +
    + return pidgin_plugin_info_new(
    + "id", PLUGIN_ID,
    + "name", N_("XMPP Console"),
    + "version", DISPLAY_VERSION,
    + "category", N_("Protocol utility"),
    + "summary", N_("Send and receive raw XMPP stanzas."),
    + "description", N_("This plugin is useful for debugging XMPP servers "
    + "or clients."),
    + "authors", authors,
    + "website", PURPLE_WEBSITE,
    + "abi-version", PURPLE_ABI_VERSION,
    + "actions-cb", actions,
    + NULL
    + );
    +}
    +
    +static gboolean
    +plugin_load(PurplePlugin *plugin, GError **error)
    +{
    + int i;
    + gboolean any_registered = FALSE;
    +
    + xmpp_console_handle = plugin;
    +
    + i = 0;
    + while (xmpp_prpls[i] != NULL) {
    + PurpleProtocol *xmpp;
    +
    + xmpp = purple_protocols_find(xmpp_prpls[i]);
    + i++;
    +
    + if (!xmpp)
    + continue;
    + any_registered = TRUE;
    +
    + purple_signal_connect(xmpp, "jabber-receiving-xmlnode",
    + xmpp_console_handle,
    + PURPLE_CALLBACK(purple_xmlnode_received_cb), NULL);
    + purple_signal_connect(xmpp, "jabber-sending-text",
    + xmpp_console_handle,
    + PURPLE_CALLBACK(purple_xmlnode_sent_cb), NULL);
    + }
    +
    + if (!any_registered) {
    + g_set_error_literal(error, PLUGIN_DOMAIN, 0,
    + _("No XMPP protocol is loaded."));
    + return FALSE;
    + }
    +
    + purple_signal_connect(purple_connections_get_handle(), "signing-on",
    + plugin, PURPLE_CALLBACK(signing_on_cb), NULL);
    + purple_signal_connect(purple_connections_get_handle(), "signed-off",
    + plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
    +
    + return TRUE;
    +}
    +
    +static gboolean
    +plugin_unload(PurplePlugin *plugin, GError **error)
    +{
    + if (console)
    + gtk_widget_destroy(console->window);
    + return TRUE;
    +}
    +
    +PURPLE_PLUGIN_INIT(xmppconsole, plugin_query, plugin_load, plugin_unload);
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/plugins/xmppconsole/xmppconsole.gresource.xml Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,6 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<gresources>
    + <gresource prefix="/im/pidgin/Pidgin/Plugin/XMPPConsole/">
    + <file compressed="true">console.ui</file>
    + </gresource>
    +</gresources>
    --- a/pidgin/resources/About/about.ui Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/resources/About/about.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -2,7 +2,7 @@
    <!-- Generated with glade 3.22.1 -->
    <interface>
    <requires lib="Talkatu" version="0.0"/>
    - <requires lib="gtk+" version="3.10"/>
    + <requires lib="gtk+" version="3.20"/>
    <object class="GtkTreeStore" id="build_info_store">
    <columns>
    <!-- column-name title -->
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/resources/Accounts/chooser.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,33 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!-- Generated with glade 3.22.1 -->
    +<interface>
    + <requires lib="gtk+" version="3.20"/>
    + <object class="GtkListStore" id="model">
    + <columns>
    + <!-- column-name icon -->
    + <column type="GdkPixbuf"/>
    + <!-- column-name name -->
    + <column type="gchararray"/>
    + <!-- column-name account -->
    + <column type="PurpleAccount"/>
    + </columns>
    + </object>
    + <template class="PidginAccountChooser" parent="GtkComboBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">model</property>
    + <signal name="destroy" handler="account_menu_destroyed_cb" swapped="no"/>
    + <child>
    + <object class="GtkCellRendererPixbuf"/>
    + <attributes>
    + <attribute name="pixbuf">0</attribute>
    + </attributes>
    + </child>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">1</attribute>
    + </attributes>
    + </child>
    + </template>
    +</interface>
    --- a/pidgin/resources/Conversations/invite_dialog.ui Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/resources/Conversations/invite_dialog.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -1,7 +1,7 @@
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.22.1 -->
    <interface>
    - <requires lib="gtk+" version="3.10"/>
    + <requires lib="gtk+" version="3.20"/>
    <template class="PidginInviteDialog" parent="GtkDialog">
    <property name="can_focus">False</property>
    <property name="border_width">6</property>
    --- a/pidgin/resources/Debug/debug.ui Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/resources/Debug/debug.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -1,7 +1,7 @@
    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.22.1 -->
    <interface>
    - <requires lib="gtk+" version="3.10"/>
    + <requires lib="gtk+" version="3.20"/>
    <object class="GtkTextTagTable" id="message-format">
    <child type="tag">
    <object class="GtkTextTag" id="tags.level[0]">
    @@ -276,4 +276,45 @@
    </object>
    </child>
    </template>
    + <object class="GtkPopover" id="popover">
    + <property name="can_focus">False</property>
    + <property name="relative_to">expression</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <child>
    + <object class="GtkCheckButton" id="popover_invert">
    + <property name="label" translatable="yes">Invert</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="draw_indicator">True</property>
    + <signal name="toggled" handler="regex_menu_cb" object="PidginDebugWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="popover_highlight">
    + <property name="label" translatable="yes">Highlight matches</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="draw_indicator">True</property>
    + <signal name="toggled" handler="regex_menu_cb" object="PidginDebugWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    </interface>
    --- a/pidgin/resources/Debug/filter-popover.ui Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,43 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8"?>
    -<!-- Generated with glade 3.22.1 -->
    -<interface>
    - <requires lib="gtk+" version="3.12"/>
    - <object class="GtkPopover" id="popover">
    - <property name="can_focus">False</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <child>
    - <object class="GtkCheckButton" id="popover.invert">
    - <property name="label" translatable="yes">Invert</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="popover.highlight">
    - <property name="label" translatable="yes">Highlight matches</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    -</interface>
    --- a/pidgin/resources/Debug/plugininfo.ui Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/resources/Debug/plugininfo.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -2,7 +2,7 @@
    <!-- Generated with glade 3.22.1 -->
    <interface>
    <requires lib="Talkatu" version="0.0"/>
    - <requires lib="gtk+" version="3.10"/>
    + <requires lib="gtk+" version="3.20"/>
    <!-- interface-local-resource-path ../ -->
    <object class="TalkatuHtmlBuffer" id="buffer"/>
    <template class="PidginDebugPluginInfo" parent="GtkDialog">
    --- a/pidgin/resources/Log/log-viewer.ui Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/resources/Log/log-viewer.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -2,7 +2,7 @@
    <!-- Generated with glade 3.22.1 -->
    <interface>
    <requires lib="Talkatu" version="0.0"/>
    - <requires lib="gtk+" version="3.10"/>
    + <requires lib="gtk+" version="3.20"/>
    <object class="TalkatuHtmlBuffer" id="log_buffer"/>
    <object class="GtkTreeStore" id="treestore">
    <columns>
    --- a/pidgin/resources/Prefs/prefs.ui Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/resources/Prefs/prefs.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -2,7 +2,7 @@
    <!-- Generated with glade 3.22.1 -->
    <interface>
    <requires lib="Talkatu" version="0.0"/>
    - <requires lib="gtk+" version="3.10"/>
    + <requires lib="gtk+" version="3.20"/>
    <object class="GtkListStore" id="away.auto_reply.store">
    <columns>
    <!-- column-name text -->
    @@ -212,6 +212,14 @@
    </row>
    </data>
    </object>
    + <object class="GtkListStore" id="keyring.active.store">
    + <columns>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name value -->
    + <column type="gchararray"/>
    + </columns>
    + </object>
    <object class="GtkListStore" id="logging.format.store">
    <columns>
    <!-- column-name text -->
    @@ -279,6 +287,115 @@
    </row>
    </data>
    </object>
    + <object class="GtkListStore" id="sound.event.store">
    + <columns>
    + <!-- column-name active -->
    + <column type="gboolean"/>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name pref -->
    + <column type="gchararray"/>
    + <!-- column-name id -->
    + <column type="guint"/>
    + </columns>
    + </object>
    + <object class="GtkListStore" id="sound.method.store_raw">
    + <columns>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name value -->
    + <column type="gchararray"/>
    + <!-- column-name any -->
    + <column type="gboolean"/>
    + <!-- column-name gstreamer -->
    + <column type="gboolean"/>
    + <!-- column-name win32 -->
    + <column type="gboolean"/>
    + </columns>
    + <data>
    + <row>
    + <col id="0" translatable="yes">Automatic</col>
    + <col id="1">automatic</col>
    + <col id="2">True</col>
    + <col id="3">False</col>
    + <col id="4">False</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">DirectSound</col>
    + <col id="1">directsound</col>
    + <col id="2">False</col>
    + <col id="3">True</col>
    + <col id="4">True</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">ESD</col>
    + <col id="1">esd</col>
    + <col id="2">False</col>
    + <col id="3">True</col>
    + <col id="4">False</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">ALSA</col>
    + <col id="1">alsa</col>
    + <col id="2">False</col>
    + <col id="3">True</col>
    + <col id="4">False</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">PlaySound</col>
    + <col id="1">playsoundw</col>
    + <col id="2">False</col>
    + <col id="3">False</col>
    + <col id="4">True</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">Console beep</col>
    + <col id="1">beep</col>
    + <col id="2">False</col>
    + <col id="3">False</col>
    + <col id="4">False</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">Command</col>
    + <col id="1">custom</col>
    + <col id="2">False</col>
    + <col id="3">False</col>
    + <col id="4">False</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">No sounds</col>
    + <col id="1">none</col>
    + <col id="2">True</col>
    + <col id="3">False</col>
    + <col id="4">False</col>
    + </row>
    + </data>
    + </object>
    + <object class="GtkTreeModelFilter" id="sound.method.store">
    + <property name="child_model">sound.method.store_raw</property>
    + </object>
    + <object class="GtkListStore" id="sound.while_status.store">
    + <columns>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name value -->
    + <column type="gint"/>
    + </columns>
    + <data>
    + <row>
    + <col id="0" translatable="yes">Only when available</col>
    + <col id="1">1</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">Only when not available</col>
    + <col id="1">2</col>
    + </row>
    + <row>
    + <col id="0" translatable="yes">Always</col>
    + <col id="1">3</col>
    + </row>
    + </data>
    + </object>
    <template class="PidginPrefsWindow" parent="GtkDialog">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Preferences</property>
    @@ -319,120 +436,56 @@
    </packing>
    </child>
    <child>
    - <object class="GtkNotebook" id="notebook">
    + <object class="GtkBox">
    <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="tab_pos">left</property>
    + <property name="can_focus">False</property>
    <child>
    - <object class="GtkBox" id="iface.page">
    + <object class="GtkStackSidebar">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="margin_left">12</property>
    - <property name="margin_right">12</property>
    - <property name="margin_top">12</property>
    - <property name="margin_bottom">12</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">18</property>
    + <property name="stack">stack</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkStack" id="stack">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    <child>
    - <object class="GtkFrame">
    + <object class="GtkBox" id="iface.page">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    <child>
    - <object class="GtkAlignment">
    + <object class="GtkFrame">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="bottom_padding">12</property>
    - <property name="left_padding">12</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    <child>
    - <object class="GtkBox">
    + <object class="GtkAlignment">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="label1">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Show system tray icon:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">iface.docklet.combo</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkComboBox" id="iface.docklet.combo">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="model">iface.docklet.store</property>
    - <child>
    - <object class="GtkCellRendererText"/>
    - <attributes>
    - <attribute name="text">0</attribute>
    - </attributes>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">System Tray Icon</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="bottom_padding">12</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    + <property name="bottom_padding">12</property>
    + <property name="left_padding">12</property>
    <child>
    <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="label2">
    + <object class="GtkLabel" id="label1">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Hide new IM conversations:</property>
    + <property name="label" translatable="yes">_Show system tray icon:</property>
    <property name="use_underline">True</property>
    + <property name="mnemonic_widget">iface.docklet.combo</property>
    <property name="xalign">0</property>
    </object>
    <packing>
    @@ -442,10 +495,10 @@
    </packing>
    </child>
    <child>
    - <object class="GtkComboBox" id="iface.im.hide_new.combo">
    + <object class="GtkComboBox" id="iface.docklet.combo">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="model">iface.im.hide_new.store</property>
    + <property name="model">iface.docklet.store</property>
    <child>
    <object class="GtkCellRendererText"/>
    <attributes>
    @@ -460,114 +513,55 @@
    </packing>
    </child>
    </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    </child>
    - <child>
    - <object class="GtkCheckButton" id="iface.win32.minimize_new_convs">
    - <property name="label" translatable="yes">Minimi_ze new conversation windows</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">System Tray Icon</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    </object>
    </child>
    </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    </child>
    - <child type="label">
    - <object class="GtkLabel">
    + <child>
    + <object class="GtkFrame">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Conversation Window</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="bottom_padding">12</property>
    - <property name="left_padding">12</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    <child>
    - <object class="GtkBox">
    + <object class="GtkAlignment">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    + <property name="bottom_padding">12</property>
    + <property name="left_padding">12</property>
    <child>
    - <object class="GtkCheckButton" id="iface.conversations.tabs">
    - <property name="label" translatable="yes">Show IMs and chats in _tabbed windows</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="iface.conversations.tabs_vbox">
    + <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="orientation">vertical</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkCheckButton" id="iface.conversations.close_on_tabs">
    - <property name="label" translatable="yes">Show closed b_utton on tabs</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="label3">
    + <object class="GtkLabel" id="label2">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Placement:</property>
    + <property name="label" translatable="yes">_Hide new IM conversations:</property>
    <property name="use_underline">True</property>
    - <property name="mnemonic_widget">iface.conversations.tab_side.combo</property>
    <property name="xalign">0</property>
    </object>
    <packing>
    @@ -577,10 +571,10 @@
    </packing>
    </child>
    <child>
    - <object class="GtkComboBox" id="iface.conversations.tab_side.combo">
    + <object class="GtkComboBox" id="iface.im.hide_new.combo">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="model">iface.conversations.tab_side.store</property>
    + <property name="model">iface.im.hide_new.store</property>
    <child>
    <object class="GtkCellRendererText"/>
    <attributes>
    @@ -598,22 +592,92 @@
    <packing>
    <property name="expand">False</property>
    <property name="fill">True</property>
    - <property name="position">1</property>
    + <property name="position">0</property>
    </packing>
    </child>
    <child>
    - <object class="GtkBox">
    + <object class="GtkCheckButton" id="iface.win32.minimize_new_convs">
    + <property name="label" translatable="yes">Minimi_ze new conversation windows</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Conversation Window</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="bottom_padding">12</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkCheckButton" id="iface.conversations.tabs">
    + <property name="label" translatable="yes">Show IMs and chats in _tabbed windows</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="iface.conversations.tabs_vbox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="label4">
    + <object class="GtkCheckButton" id="iface.conversations.close_on_tabs">
    + <property name="label" translatable="yes">Show closed b_utton on tabs</property>
    <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">N_ew conversations:</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    <property name="use_underline">True</property>
    - <property name="mnemonic_widget">iface.conversations.placement.combo</property>
    - <property name="xalign">0</property>
    + <property name="draw_indicator">True</property>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -622,10 +686,445 @@
    </packing>
    </child>
    <child>
    - <object class="GtkComboBox" id="iface.conversations.placement.combo">
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label3">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Placement:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">iface.conversations.tab_side.combo</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="iface.conversations.tab_side.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">iface.conversations.tab_side.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label4">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">N_ew conversations:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">iface.conversations.placement.combo</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="iface.conversations.placement.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">iface.conversations.placement.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Tabs</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">iface</property>
    + <property name="title" translatable="yes">Interface</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="browser.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkStack" id="browser.stack">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="hhomogeneous">False</property>
    + <property name="vhomogeneous">False</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Browser preferences are configured in GNOME preferences</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="model">iface.conversations.placement.store</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="browser.gnome_not_found">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Browser configuration program was not found.</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="browser.gnome_program">
    + <property name="label" translatable="yes">Configure _Browser</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="browser_button_clicked_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">gnome</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label5">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Browser:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">browser.browser.combo</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="browser.browser.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">browser.browser.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="browser.place_hbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label6">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Open link in:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">browser.place.combo</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="browser.place.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">browser.place.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="browser.manual_command_hbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label7">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Manual:
    +(%s for URL)</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">browser.manual_command</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="browser.manual_command">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <signal name="focus-out-event" handler="manual_browser_set" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">nongnome</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Browser Selection</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">browser</property>
    + <property name="title" translatable="yes">Browser</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="conversations.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Chat notification:</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="conversations.notification_chat.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">conversations.notification_chat.store</property>
    <child>
    <object class="GtkCellRendererText"/>
    <attributes>
    @@ -643,76 +1142,247 @@
    <packing>
    <property name="expand">False</property>
    <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.show_incoming_formatting">
    + <property name="label" translatable="yes">Show _formatting on incoming messages</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.im.close_immediately">
    + <property name="label" translatable="yes">Close IMs immediately when the tab is closed</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    <property name="position">2</property>
    </packing>
    </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.im.show_buddy_icons">
    + <property name="label" translatable="yes">Show _detailed information</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.im.animate_buddy_icons">
    + <property name="label" translatable="yes">Enable buddy ic_on animation</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.im.send_typing">
    + <property name="label" translatable="yes">_Notify buddies that you are typing to them</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">5</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.spellcheck">
    + <property name="label" translatable="yes">Highlight _misspelled words</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">6</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.use_smooth_scrolling">
    + <property name="label" translatable="yes">Use smooth-scrolling</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">7</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="conversations.win32.blink_im">
    + <property name="label" translatable="yes">F_lash window when IMs are received</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">8</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkCheckButton" id="conversations.resize_custom_smileys">
    + <property name="label" translatable="yes">Resize incoming custom smileys</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Maximum size:</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="conversations.custom_smileys_size">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="input_purpose">number</property>
    + <property name="adjustment">conversations.custom_smiley_size.adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">9</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Minimum input area height in lines:</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="conversations.minimum_entry_lines">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="input_purpose">digits</property>
    + <property name="adjustment">conversations.minimum_entry_lines.adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">10</property>
    + </packing>
    + </child>
    </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    </child>
    </object>
    </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Conversations</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Tabs</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - <child type="tab">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Interface</property>
    - </object>
    - <packing>
    - <property name="tab_fill">False</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="browser.page">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="margin_left">12</property>
    - <property name="margin_right">12</property>
    - <property name="margin_top">12</property>
    - <property name="margin_bottom">12</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">18</property>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    + <object class="GtkFrame" id="conversations.font_frame">
    <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    <child>
    - <object class="GtkStack" id="browser.stack">
    + <object class="GtkAlignment">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="hhomogeneous">False</property>
    - <property name="vhomogeneous">False</property>
    + <property name="left_padding">12</property>
    <child>
    <object class="GtkBox">
    <property name="visible">True</property>
    @@ -720,21 +1390,14 @@
    <property name="orientation">vertical</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkBox">
    + <object class="GtkCheckButton" id="conversations.use_theme_font">
    + <property name="label" translatable="yes">Use font from _theme</property>
    <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Browser preferences are configured in GNOME preferences</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + <signal name="toggled" handler="apply_custom_font" object="PidginPrefsWindow" swapped="no"/>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -743,18 +1406,16 @@
    </packing>
    </child>
    <child>
    - <object class="GtkBox">
    + <object class="GtkBox" id="conversations.custom_font_hbox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="browser.gnome_not_found">
    + <object class="GtkLabel">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Browser configuration program was not found.</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    + <property name="label" translatable="yes">Conversation _font:</property>
    + <property name="use_underline">True</property>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -763,13 +1424,13 @@
    </packing>
    </child>
    <child>
    - <object class="GtkButton" id="browser.gnome_program">
    - <property name="label" translatable="yes">Configure _Browser</property>
    + <object class="GtkFontButton" id="conversations.custom_font">
    <property name="visible">True</property>
    <property name="can_focus">True</property>
    <property name="receives_default">True</property>
    - <property name="use_underline">True</property>
    - <signal name="clicked" handler="browser_button_clicked_cb" swapped="no"/>
    + <property name="font">Sans 12</property>
    + <property name="preview_text"/>
    + <signal name="font-set" handler="pidgin_custom_font_set" object="PidginPrefsWindow" swapped="no"/>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -785,10 +1446,122 @@
    </packing>
    </child>
    </object>
    - <packing>
    - <property name="name">gnome</property>
    - </packing>
    </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Font</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox" id="conversations.sample_box">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <child>
    + <object class="TalkatuEditor">
    + <property name="orientation">vertical</property>
    + <property name="visible">True</property>
    + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
    + <child internal-child="toolbar">
    + <object class="TalkatuToolbar">
    + <property name="visible">True</property>
    + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
    + </object>
    + <packing>
    + <property name="fill">False</property>
    + </packing>
    + </child>
    + <child internal-child="view">
    + <object class="TalkatuView" id="conversations.format_view">
    + <property name="width_request">450</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
    + <property name="wrap_mode">word</property>
    + <property name="buffer">conversations.format_buffer</property>
    + </object>
    + <packing>
    + <property name="fill">False</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Default Formatting</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">conversations</property>
    + <property name="title" translatable="yes">Conversations</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="logging.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    <child>
    <object class="GtkBox">
    <property name="visible">True</property>
    @@ -801,13 +1574,12 @@
    <property name="can_focus">False</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="label5">
    + <object class="GtkLabel">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Browser:</property>
    + <property name="label" translatable="yes">Log _format:</property>
    <property name="use_underline">True</property>
    - <property name="mnemonic_widget">browser.browser.combo</property>
    - <property name="xalign">0</property>
    + <property name="mnemonic_widget">logging.format.combo</property>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -816,10 +1588,10 @@
    </packing>
    </child>
    <child>
    - <object class="GtkComboBox" id="browser.browser.combo">
    + <object class="GtkComboBox" id="logging.format.combo">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="model">browser.browser.store</property>
    + <property name="model">logging.format.store</property>
    <child>
    <object class="GtkCellRendererText"/>
    <attributes>
    @@ -841,17 +1613,170 @@
    </packing>
    </child>
    <child>
    - <object class="GtkBox" id="browser.place_hbox">
    + <object class="GtkCheckButton" id="logging.log_ims">
    + <property name="label" translatable="yes">Log all _instant messages</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="logging.log_chats">
    + <property name="label" translatable="yes">Log all c_hats</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="logging.log_system">
    + <property name="label" translatable="yes">Log all _status changes to system log</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Logging</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">logging</property>
    + <property name="title" translatable="yes">Logging</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="network.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="label6">
    + <object class="GtkLabel" id="label8">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Open link in:</property>
    + <property name="label" translatable="yes">ST_UN server:</property>
    <property name="use_underline">True</property>
    - <property name="mnemonic_widget">browser.place.combo</property>
    + <property name="mnemonic_widget">network.stun_server</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="network.stun_server">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="placeholder_text" translatable="yes">Example: stunserver.org</property>
    + <property name="input_purpose">url</property>
    + <signal name="focus-out-event" handler="network_stun_server_changed_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="network.auto_ip">
    + <property name="label" translatable="yes">Use _automatically detected IP address</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + <signal name="toggled" handler="auto_ip_button_clicked_cb" after="yes" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="network.public_ip_hbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label9">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Public _IP:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">network.public_ip</property>
    <property name="xalign">0</property>
    </object>
    <packing>
    @@ -861,56 +1786,10 @@
    </packing>
    </child>
    <child>
    - <object class="GtkComboBox" id="browser.place.combo">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="model">browser.place.store</property>
    - <child>
    - <object class="GtkCellRendererText"/>
    - <attributes>
    - <attribute name="text">0</attribute>
    - </attributes>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="browser.manual_command_hbox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="label7">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Manual:
    -(%s for URL)</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">browser.manual_command</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="browser.manual_command">
    + <object class="GtkEntry" id="network.public_ip">
    <property name="visible">True</property>
    <property name="can_focus">True</property>
    - <signal name="focus-out-event" handler="manual_browser_set" swapped="no"/>
    + <signal name="changed" handler="network_ip_changed" swapped="no"/>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -926,1208 +1805,37 @@
    </packing>
    </child>
    </object>
    - <packing>
    - <property name="name">nongnome</property>
    - <property name="position">1</property>
    - </packing>
    </child>
    </object>
    </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Browser Selection</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child type="tab">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Browser</property>
    - </object>
    - <packing>
    - <property name="position">1</property>
    - <property name="tab_fill">False</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="conversations.page">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="margin_left">12</property>
    - <property name="margin_right">12</property>
    - <property name="margin_top">12</property>
    - <property name="margin_bottom">12</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">18</property>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox">
    + <child type="label">
    + <object class="GtkLabel">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Chat notification:</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkComboBox" id="conversations.notification_chat.combo">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="model">conversations.notification_chat.store</property>
    - <child>
    - <object class="GtkCellRendererText"/>
    - <attributes>
    - <attribute name="text">0</attribute>
    - </attributes>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.show_incoming_formatting">
    - <property name="label" translatable="yes">Show _formatting on incoming messages</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.im.close_immediately">
    - <property name="label" translatable="yes">Close IMs immediately when the tab is closed</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.im.show_buddy_icons">
    - <property name="label" translatable="yes">Show _detailed information</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">3</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.im.animate_buddy_icons">
    - <property name="label" translatable="yes">Enable buddy ic_on animation</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">4</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.im.send_typing">
    - <property name="label" translatable="yes">_Notify buddies that you are typing to them</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">5</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.spellcheck">
    - <property name="label" translatable="yes">Highlight _misspelled words</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">6</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.use_smooth_scrolling">
    - <property name="label" translatable="yes">Use smooth-scrolling</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">7</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="conversations.win32.blink_im">
    - <property name="label" translatable="yes">F_lash window when IMs are received</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">8</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox">
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkCheckButton" id="conversations.resize_custom_smileys">
    - <property name="label" translatable="yes">Resize incoming custom smileys</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Maximum size:</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkSpinButton" id="conversations.custom_smileys_size">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="input_purpose">number</property>
    - <property name="adjustment">conversations.custom_smiley_size.adjustment</property>
    - <property name="numeric">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">9</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Minimum input area height in lines:</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkSpinButton" id="conversations.minimum_entry_lines">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="input_purpose">digits</property>
    - <property name="adjustment">conversations.minimum_entry_lines.adjustment</property>
    - <property name="numeric">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">10</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Conversations</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFrame" id="conversations.font_frame">
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkCheckButton" id="conversations.use_theme_font">
    - <property name="label" translatable="yes">Use font from _theme</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - <signal name="toggled" handler="apply_custom_font" object="PidginPrefsWindow" swapped="no"/>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="conversations.custom_font_hbox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Conversation _font:</property>
    - <property name="use_underline">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFontButton" id="conversations.custom_font">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">True</property>
    - <property name="font">Sans 12</property>
    - <property name="preview_text"/>
    - <signal name="font-set" handler="pidgin_custom_font_set" object="PidginPrefsWindow" swapped="no"/>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Font</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox" id="conversations.sample_box">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <child>
    - <object class="TalkatuEditor">
    - <property name="orientation">vertical</property>
    - <property name="visible">True</property>
    - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
    - <child internal-child="toolbar">
    - <object class="TalkatuToolbar">
    - <property name="visible">True</property>
    - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
    - </object>
    - <packing>
    - <property name="fill">False</property>
    - </packing>
    - </child>
    - <child internal-child="view">
    - <object class="TalkatuView" id="conversations.format_view">
    - <property name="width_request">450</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
    - <property name="wrap_mode">word</property>
    - <property name="buffer">conversations.format_buffer</property>
    - </object>
    - <packing>
    - <property name="fill">False</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    + <property name="label" translatable="yes">IP Address</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    </object>
    </child>
    </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Default Formatting</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - <child type="tab">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Conversations</property>
    - </object>
    - <packing>
    - <property name="position">2</property>
    - <property name="tab_fill">False</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="logging.page">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="margin_left">12</property>
    - <property name="margin_right">12</property>
    - <property name="margin_top">12</property>
    - <property name="margin_bottom">12</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">18</property>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Log _format:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">logging.format.combo</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkComboBox" id="logging.format.combo">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="model">logging.format.store</property>
    - <child>
    - <object class="GtkCellRendererText"/>
    - <attributes>
    - <attribute name="text">0</attribute>
    - </attributes>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="logging.log_ims">
    - <property name="label" translatable="yes">Log all _instant messages</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="logging.log_chats">
    - <property name="label" translatable="yes">Log all c_hats</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="logging.log_system">
    - <property name="label" translatable="yes">Log all _status changes to system log</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">3</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    + <object class="GtkFrame">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Logging</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="position">3</property>
    - </packing>
    - </child>
    - <child type="tab">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Logging</property>
    - </object>
    - <packing>
    - <property name="position">3</property>
    - <property name="tab_fill">False</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="network.page">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="margin_left">12</property>
    - <property name="margin_right">12</property>
    - <property name="margin_top">12</property>
    - <property name="margin_bottom">12</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">18</property>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="label8">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">ST_UN server:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.stun_server</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="network.stun_server">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="placeholder_text" translatable="yes">Example: stunserver.org</property>
    - <property name="input_purpose">url</property>
    - <signal name="focus-out-event" handler="network_stun_server_changed_cb" swapped="no"/>
    - </object>
    - <packing>
    - <property name="expand">True</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkCheckButton" id="network.auto_ip">
    - <property name="label" translatable="yes">Use _automatically detected IP address</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - <signal name="toggled" handler="auto_ip_button_clicked_cb" after="yes" swapped="no"/>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="network.public_ip_hbox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="label9">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Public _IP:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.public_ip</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="network.public_ip">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <signal name="changed" handler="network_ip_changed" swapped="no"/>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">IP Address</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox">
    + <object class="GtkAlignment">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkCheckButton" id="network.map_ports">
    - <property name="label" translatable="yes">_Enable automatic router port forwarding</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkCheckButton" id="network.ports_range_use">
    - <property name="label" translatable="yes">_Manually specify range of ports to listen on:</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="network.ports_range_hbox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Start:</property>
    - <property name="use_underline">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkSpinButton" id="network.ports_range_start">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="text" translatable="yes">0</property>
    - <property name="adjustment">network.ports_range_start.adjustment</property>
    - <property name="numeric">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_End:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.ports_range_end</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkSpinButton" id="network.ports_range_end">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="text" translatable="yes">0</property>
    - <property name="adjustment">network.ports_range_end.adjustment</property>
    - <property name="numeric">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">3</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Ports</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="label10">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_TURN server:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.turn_server</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="network.turn_server">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <signal name="focus-out-event" handler="network_turn_server_changed_cb" swapped="no"/>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_UDP Port:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.turn_port_udp</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkSpinButton" id="network.turn_port_udp">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="adjustment">network.turn_port_udp.adjustment</property>
    - <property name="numeric">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">3</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">T_CP Port:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.turn_port_tcp</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">4</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkSpinButton" id="network.turn_port_tcp">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="adjustment">network.turn_port_tcp.adjustment</property>
    - <property name="numeric">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">5</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="label11">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Use_rname:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.turn_username</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="network.turn_username">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Pass_word:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">network.turn_password</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="network.turn_password">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="visibility">False</property>
    - <property name="invisible_char">●</property>
    - <property name="input_purpose">password</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">3</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Relay Server (TURN)</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="position">4</property>
    - </packing>
    - </child>
    - <child type="tab">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Network</property>
    - </object>
    - <packing>
    - <property name="position">4</property>
    - <property name="tab_fill">False</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="proxy.page">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="margin_left">12</property>
    - <property name="margin_right">12</property>
    - <property name="margin_top">12</property>
    - <property name="margin_bottom">12</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">18</property>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkStack" id="proxy.stack">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="hhomogeneous">False</property>
    - <property name="vhomogeneous">False</property>
    + <property name="left_padding">12</property>
    <child>
    <object class="GtkBox">
    <property name="visible">True</property>
    @@ -2135,74 +1843,8 @@
    <property name="orientation">vertical</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Proxy preferences are configured in GNOME preferences</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="proxy.gnome_not_found">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Proxy configuration program was not found.</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkButton" id="proxy.gnome_program">
    - <property name="label" translatable="yes">Configure _Proxy</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">True</property>
    - <property name="use_underline">True</property>
    - <signal name="clicked" handler="proxy_button_clicked_cb" object="PidginPrefsWindow" swapped="no"/>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="name">gnome</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkCheckButton" id="proxy.socks4_remotedns">
    - <property name="label" translatable="yes">Use remote _DNS with SOCKS4 proxies</property>
    + <object class="GtkCheckButton" id="network.map_ports">
    + <property name="label" translatable="yes">_Enable automatic router port forwarding</property>
    <property name="visible">True</property>
    <property name="can_focus">True</property>
    <property name="receives_default">False</property>
    @@ -2221,12 +1863,355 @@
    <property name="can_focus">False</property>
    <property name="spacing">6</property>
    <child>
    + <object class="GtkCheckButton" id="network.ports_range_use">
    + <property name="label" translatable="yes">_Manually specify range of ports to listen on:</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="network.ports_range_hbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Start:</property>
    + <property name="use_underline">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="network.ports_range_start">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="text" translatable="yes">0</property>
    + <property name="adjustment">network.ports_range_start.adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_End:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">network.ports_range_end</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="network.ports_range_end">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="text" translatable="yes">0</property>
    + <property name="adjustment">network.ports_range_end.adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Ports</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label10">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_TURN server:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">network.turn_server</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="network.turn_server">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <signal name="focus-out-event" handler="network_turn_server_changed_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    <object class="GtkLabel">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Proxy t_ype:</property>
    + <property name="label" translatable="yes">_UDP Port:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">network.turn_port_udp</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="network.turn_port_udp">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="adjustment">network.turn_port_udp.adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">T_CP Port:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">network.turn_port_tcp</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="network.turn_port_tcp">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="adjustment">network.turn_port_tcp.adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">5</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label11">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Use_rname:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">network.turn_username</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="network.turn_username">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Pass_word:</property>
    <property name="use_underline">True</property>
    - <property name="mnemonic_widget">proxy.type.combo</property>
    + <property name="mnemonic_widget">network.turn_password</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="network.turn_password">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="visibility">False</property>
    + <property name="invisible_char">●</property>
    + <property name="input_purpose">password</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Relay Server (TURN)</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">network</property>
    + <property name="title" translatable="yes">Network</property>
    + <property name="position">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="proxy.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkStack" id="proxy.stack">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="hhomogeneous">False</property>
    + <property name="vhomogeneous">False</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Proxy preferences are configured in GNOME preferences</property>
    + <property name="xalign">0</property>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -2235,10 +2220,422 @@
    </packing>
    </child>
    <child>
    - <object class="GtkComboBox" id="proxy.type.combo">
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="proxy.gnome_not_found">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Proxy configuration program was not found.</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="proxy.gnome_program">
    + <property name="label" translatable="yes">Configure _Proxy</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="proxy_button_clicked_cb" object="PidginPrefsWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">gnome</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkCheckButton" id="proxy.socks4_remotedns">
    + <property name="label" translatable="yes">Use remote _DNS with SOCKS4 proxies</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Proxy t_ype:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">proxy.type.combo</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="proxy.type.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">proxy.type.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkGrid" id="proxy.options">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="model">proxy.type.store</property>
    + <property name="row_spacing">10</property>
    + <property name="column_spacing">5</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Host:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">proxy.host</property>
    + <property name="xalign">1</property>
    + <accessibility>
    + <relation type="label-for" target="proxy.host"/>
    + </accessibility>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="proxy.host">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">P_ort:</property>
    + <property name="use_underline">True</property>
    + <property name="xalign">1</property>
    + <accessibility>
    + <relation type="label-for" target="proxy.port"/>
    + </accessibility>
    + </object>
    + <packing>
    + <property name="left_attach">2</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="proxy.port">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="text" translatable="yes">0</property>
    + <property name="adjustment">proxy.port.adjustment</property>
    + <property name="numeric">True</property>
    + <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="left_attach">3</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">User_name:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">proxy.username</property>
    + <property name="xalign">1</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="proxy.username">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Pa_ssword:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">proxy.password</property>
    + <property name="xalign">1</property>
    + <accessibility>
    + <relation type="label-for" target="proxy.password"/>
    + </accessibility>
    + </object>
    + <packing>
    + <property name="left_attach">2</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="proxy.password">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="visibility">False</property>
    + <property name="invisible_char">●</property>
    + <property name="input_purpose">password</property>
    + <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="left_attach">3</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">nongnome</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Proxy Server</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">proxy</property>
    + <property name="title" translatable="yes">Proxy</property>
    + <property name="position">5</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="keyring.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox" id="keyring.vbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Keyring:</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="keyring.active.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">keyring.active.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="margin_bottom">6</property>
    + <property name="label" translatable="yes">Keyring</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">keyring</property>
    + <property name="title" translatable="yes">Password Storage</property>
    + <property name="position">6</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="sound.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label15">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Method:</property>
    + <property name="use_underline">True</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="sound.method.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">sound.method.store</property>
    + <property name="id_column">1</property>
    <child>
    <object class="GtkCellRendererText"/>
    <attributes>
    @@ -2256,128 +2653,454 @@
    <packing>
    <property name="expand">False</property>
    <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="sound.method_vbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox" id="sound.command_hbox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label17">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Sound c_ommand:
    +(%s for filename)</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">sound.command</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkEntry" id="sound.command">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <signal name="changed" handler="sound_cmd_yeah" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="sound.mute">
    + <property name="label" translatable="yes">M_ute sounds</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="sound.conv_focus">
    + <property name="label" translatable="yes">Sounds when conversation has _focus</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label16">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Enable sounds:</property>
    + <property name="use_underline">True</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="sound.while_status.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">sound.while_status.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="margin_bottom">6</property>
    + <property name="label" translatable="yes">Sound Options</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkScrolledWindow">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="hscrollbar_policy">never</property>
    + <property name="shadow_type">in</property>
    + <property name="min_content_height">100</property>
    + <child>
    + <object class="GtkTreeView" id="sound.event.view">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="model">sound.event.store</property>
    + <child internal-child="selection">
    + <object class="GtkTreeSelection">
    + <signal name="changed" handler="prefs_sound_sel" object="PidginPrefsWindow" swapped="no"/>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="title" translatable="yes">Play</property>
    + <child>
    + <object class="GtkCellRendererToggle">
    + <signal name="toggled" handler="event_toggled" object="sound.event.store" swapped="no"/>
    + </object>
    + <attributes>
    + <attribute name="active">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="title" translatable="yes">Event</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">1</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkEntry" id="sound.entry">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="editable">False</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">_Browse...</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="select_sound" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">Pre_view</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="test_sound" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">_Reset</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="reset_sound" object="PidginPrefsWindow" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="margin_bottom">6</property>
    + <property name="label" translatable="yes">Sound Events</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="name">sound</property>
    + <property name="title" translatable="yes">Sounds</property>
    + <property name="position">7</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox" id="away.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label12">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Report idle time:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">away.idle_reporting.combo</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="away.idle_reporting.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">away.idle_reporting.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label13">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Minutes before becoming idle:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">away.mins_before_away</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkSpinButton" id="away.mins_before_away">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="adjustment">away.mins_before_away.adjustment</property>
    + <property name="numeric">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    <property name="position">1</property>
    </packing>
    </child>
    <child>
    - <object class="GtkGrid" id="proxy.options">
    + <object class="GtkBox" id="away.idle_hbox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="row_spacing">10</property>
    - <property name="column_spacing">5</property>
    + <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel">
    + <object class="GtkCheckButton" id="away.away_when_idle">
    + <property name="label" translatable="yes">Change to this status when _idle:</property>
    <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Host:</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    <property name="use_underline">True</property>
    - <property name="mnemonic_widget">proxy.host</property>
    - <property name="xalign">1</property>
    - <accessibility>
    - <relation type="label-for" target="proxy.host"/>
    - </accessibility>
    + <property name="draw_indicator">True</property>
    </object>
    <packing>
    - <property name="left_attach">0</property>
    - <property name="top_attach">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="proxy.host">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    - </object>
    - <packing>
    - <property name="left_attach">1</property>
    - <property name="top_attach">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">P_ort:</property>
    - <property name="use_underline">True</property>
    - <property name="xalign">1</property>
    - <accessibility>
    - <relation type="label-for" target="proxy.port"/>
    - </accessibility>
    - </object>
    - <packing>
    - <property name="left_attach">2</property>
    - <property name="top_attach">0</property>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    </packing>
    </child>
    <child>
    - <object class="GtkSpinButton" id="proxy.port">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="text" translatable="yes">0</property>
    - <property name="adjustment">proxy.port.adjustment</property>
    - <property name="numeric">True</property>
    - <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    - </object>
    - <packing>
    - <property name="left_attach">3</property>
    - <property name="top_attach">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">User_name:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">proxy.username</property>
    - <property name="xalign">1</property>
    - </object>
    - <packing>
    - <property name="left_attach">0</property>
    - <property name="top_attach">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="proxy.username">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    - </object>
    - <packing>
    - <property name="left_attach">1</property>
    - <property name="top_attach">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Pa_ssword:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">proxy.password</property>
    - <property name="xalign">1</property>
    - <accessibility>
    - <relation type="label-for" target="proxy.password"/>
    - </accessibility>
    - </object>
    - <packing>
    - <property name="left_attach">2</property>
    - <property name="top_attach">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkEntry" id="proxy.password">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="visibility">False</property>
    - <property name="invisible_char">●</property>
    - <property name="input_purpose">password</property>
    - <signal name="changed" handler="proxy_print_option" object="PidginPrefsWindow" swapped="no"/>
    - </object>
    - <packing>
    - <property name="left_attach">3</property>
    - <property name="top_attach">1</property>
    - </packing>
    + <placeholder/>
    </child>
    </object>
    <packing>
    @@ -2387,88 +3110,80 @@
    </packing>
    </child>
    </object>
    - <packing>
    - <property name="name">nongnome</property>
    - <property name="position">1</property>
    - </packing>
    </child>
    </object>
    </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Idle</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    </child>
    - <child type="label">
    - <object class="GtkLabel">
    + <child>
    + <object class="GtkFrame">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Proxy Server</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="position">5</property>
    - </packing>
    - </child>
    - <child type="tab">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Proxy</property>
    - </object>
    - <packing>
    - <property name="position">5</property>
    - <property name="tab_fill">False</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="away.page">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="margin_left">12</property>
    - <property name="margin_right">12</property>
    - <property name="margin_top">12</property>
    - <property name="margin_bottom">12</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">18</property>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    <child>
    - <object class="GtkBox">
    + <object class="GtkAlignment">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    + <property name="left_padding">12</property>
    <child>
    <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    + <property name="orientation">vertical</property>
    <child>
    - <object class="GtkLabel" id="label12">
    + <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Report idle time:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">away.idle_reporting.combo</property>
    - <property name="xalign">0</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label14">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">_Auto-reply:</property>
    + <property name="use_underline">True</property>
    + <property name="mnemonic_widget">away.auto_reply.combo</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="away.auto_reply.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">away.auto_reply.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    </object>
    <packing>
    <property name="expand">False</property>
    @@ -2476,79 +3191,47 @@
    <property name="position">0</property>
    </packing>
    </child>
    - <child>
    - <object class="GtkComboBox" id="away.idle_reporting.combo">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="model">away.idle_reporting.store</property>
    - <child>
    - <object class="GtkCellRendererText"/>
    - <attributes>
    - <attribute name="text">0</attribute>
    - </attributes>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Away</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    <child>
    <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="label13">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Minutes before becoming idle:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">away.mins_before_away</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkSpinButton" id="away.mins_before_away">
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="adjustment">away.mins_before_away.adjustment</property>
    - <property name="numeric">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="away.idle_hbox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkCheckButton" id="away.away_when_idle">
    - <property name="label" translatable="yes">Change to this status when _idle:</property>
    + <object class="GtkCheckButton" id="away.startup_current_status">
    + <property name="label" translatable="yes">Use status from last _exit at startup</property>
    <property name="visible">True</property>
    <property name="can_focus">True</property>
    <property name="receives_default">False</property>
    @@ -2562,82 +3245,26 @@
    </packing>
    </child>
    <child>
    - <placeholder/>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    - </packing>
    - </child>
    - </object>
    - </child>
    - </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Idle</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    - </child>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkFrame">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    - <child>
    - <object class="GtkAlignment">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <child>
    - <object class="GtkBox">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="spacing">6</property>
    - <child>
    - <object class="GtkLabel" id="label14">
    + <object class="GtkBox" id="away.startup_hbox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">_Auto-reply:</property>
    - <property name="use_underline">True</property>
    - <property name="mnemonic_widget">away.auto_reply.combo</property>
    - <property name="xalign">0</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkComboBox" id="away.auto_reply.combo">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="model">away.auto_reply.store</property>
    + <property name="spacing">6</property>
    <child>
    - <object class="GtkCellRendererText"/>
    - <attributes>
    - <attribute name="text">0</attribute>
    - </attributes>
    + <object class="GtkLabel" id="away.startup_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Status to a_pply at startup:</property>
    + <property name="use_underline">True</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <placeholder/>
    </child>
    </object>
    <packing>
    @@ -2647,76 +3274,63 @@
    </packing>
    </child>
    </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    </child>
    </object>
    </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Status at Startup</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Away</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    </child>
    </object>
    <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    + <property name="name">away</property>
    + <property name="title" translatable="yes">Status / Idle</property>
    + <property name="position">8</property>
    </packing>
    </child>
    <child>
    - <object class="GtkFrame">
    + <object class="GtkBox" id="theme.page">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label_xalign">0</property>
    - <property name="shadow_type">none</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    <child>
    - <object class="GtkAlignment">
    + <object class="GtkFrame">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="left_padding">12</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    <child>
    - <object class="GtkBox">
    + <object class="GtkAlignment">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="orientation">vertical</property>
    - <property name="spacing">6</property>
    + <property name="left_padding">12</property>
    <child>
    - <object class="GtkCheckButton" id="away.startup_current_status">
    - <property name="label" translatable="yes">Use status from last _exit at startup</property>
    - <property name="visible">True</property>
    - <property name="can_focus">True</property>
    - <property name="receives_default">False</property>
    - <property name="use_underline">True</property>
    - <property name="draw_indicator">True</property>
    - </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">0</property>
    - </packing>
    - </child>
    - <child>
    - <object class="GtkBox" id="away.startup_hbox">
    + <object class="GtkBox">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    <property name="spacing">6</property>
    <child>
    - <object class="GtkLabel" id="away.startup_label">
    + <object class="GtkLabel">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Status to a_pply at startup:</property>
    - <property name="use_underline">True</property>
    + <property name="label" translatable="yes">Select a theme that you would like to use from the lists below.
    +New themes can be installed by dragging and dropping them onto the theme list.</property>
    <property name="xalign">0</property>
    </object>
    <packing>
    @@ -2726,57 +3340,262 @@
    </packing>
    </child>
    <child>
    - <placeholder/>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label19">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Buddy List Theme:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="theme.blist">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <signal name="changed" handler="prefs_set_blist_theme_cb" swapped="no"/>
    + <child>
    + <object class="GtkCellRendererPixbuf">
    + <property name="width">32</property>
    + <property name="height">32</property>
    + </object>
    + <attributes>
    + <attribute name="pixbuf">0</attribute>
    + </attributes>
    + </child>
    + <child>
    + <object class="GtkCellRendererText">
    + <property name="ellipsize">end</property>
    + </object>
    + <attributes>
    + <attribute name="markup">1</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label20">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Status Icon Theme:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="theme.status">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <signal name="changed" handler="prefs_set_status_icon_theme_cb" swapped="no"/>
    + <child>
    + <object class="GtkCellRendererPixbuf">
    + <property name="width">32</property>
    + <property name="height">32</property>
    + </object>
    + <attributes>
    + <attribute name="pixbuf">0</attribute>
    + </attributes>
    + </child>
    + <child>
    + <object class="GtkCellRendererText">
    + <property name="ellipsize">end</property>
    + </object>
    + <attributes>
    + <attribute name="markup">1</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label21">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Sound Theme:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="theme.sound">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <signal name="changed" handler="prefs_set_sound_theme_cb" object="PidginPrefsWindow" swapped="no"/>
    + <child>
    + <object class="GtkCellRendererPixbuf">
    + <property name="width">32</property>
    + <property name="height">32</property>
    + </object>
    + <attributes>
    + <attribute name="pixbuf">0</attribute>
    + </attributes>
    + </child>
    + <child>
    + <object class="GtkCellRendererText">
    + <property name="ellipsize">end</property>
    + </object>
    + <attributes>
    + <attribute name="markup">1</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label18">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Smiley Theme:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="theme.smiley">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <signal name="changed" handler="prefs_set_smiley_theme_cb" swapped="no"/>
    + <child>
    + <object class="GtkCellRendererPixbuf">
    + <property name="width">32</property>
    + <property name="height">32</property>
    + </object>
    + <attributes>
    + <attribute name="pixbuf">0</attribute>
    + </attributes>
    + </child>
    + <child>
    + <object class="GtkCellRendererText">
    + <property name="ellipsize">end</property>
    + </object>
    + <attributes>
    + <attribute name="markup">1</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">4</property>
    + </packing>
    </child>
    </object>
    - <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">1</property>
    - </packing>
    </child>
    </object>
    </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="margin_bottom">6</property>
    + <property name="label" translatable="yes">Theme Selections</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    </object>
    - </child>
    - <child type="label">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Status at Startup</property>
    - <attributes>
    - <attribute name="weight" value="bold"/>
    - </attributes>
    - </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    </child>
    </object>
    <packing>
    - <property name="expand">False</property>
    - <property name="fill">True</property>
    - <property name="position">2</property>
    + <property name="name">theme</property>
    + <property name="title" translatable="yes">Themes</property>
    + <property name="position">9</property>
    </packing>
    </child>
    </object>
    <packing>
    - <property name="position">6</property>
    - </packing>
    - </child>
    - <child type="tab">
    - <object class="GtkLabel">
    - <property name="visible">True</property>
    - <property name="can_focus">False</property>
    - <property name="label" translatable="yes">Status / Idle</property>
    - </object>
    - <packing>
    - <property name="position">6</property>
    - <property name="tab_fill">False</property>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    </packing>
    </child>
    </object>
    <packing>
    - <property name="expand">False</property>
    + <property name="expand">True</property>
    <property name="fill">True</property>
    - <property name="position">1</property>
    + <property name="position">0</property>
    </packing>
    </child>
    </object>
    @@ -2815,4 +3634,27 @@
    <widget name="label11"/>
    </widgets>
    </object>
    + <object class="GtkSizeGroup" id="sound.sg">
    + <widgets>
    + <widget name="label15"/>
    + <widget name="label16"/>
    + <widget name="label17"/>
    + </widgets>
    + </object>
    + <object class="GtkSizeGroup" id="theme.combo_sg">
    + <widgets>
    + <widget name="theme.blist"/>
    + <widget name="theme.status"/>
    + <widget name="theme.sound"/>
    + <widget name="theme.smiley"/>
    + </widgets>
    + </object>
    + <object class="GtkSizeGroup" id="theme.label_sg">
    + <widgets>
    + <widget name="label19"/>
    + <widget name="label20"/>
    + <widget name="label21"/>
    + <widget name="label18"/>
    + </widgets>
    + </object>
    </interface>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/resources/Prefs/vv.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,572 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!-- Generated with glade 3.22.1 -->
    +<interface>
    + <requires lib="gtk+" version="3.20"/>
    + <object class="GtkAdjustment" id="adjustment1">
    + <property name="upper">100</property>
    + <property name="step_increment">1</property>
    + <property name="page_increment">10</property>
    + </object>
    + <object class="GtkAdjustment" id="adjustment2">
    + <property name="upper">100</property>
    + <property name="step_increment">1</property>
    + <property name="page_increment">10</property>
    + </object>
    + <object class="GtkListStore" id="vv.video.input.store">
    + <columns>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name value -->
    + <column type="gchararray"/>
    + </columns>
    + </object>
    + <object class="GtkListStore" id="vv.video.output.store">
    + <columns>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name value -->
    + <column type="gchararray"/>
    + </columns>
    + </object>
    + <object class="GtkListStore" id="vv.voice.input.store">
    + <columns>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name value -->
    + <column type="gchararray"/>
    + </columns>
    + </object>
    + <object class="GtkListStore" id="vv.voice.output.store">
    + <columns>
    + <!-- column-name text -->
    + <column type="gchararray"/>
    + <!-- column-name value -->
    + <column type="gchararray"/>
    + </columns>
    + </object>
    + <object class="GtkBox" id="vv.page">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="border_width">12</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">18</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label1">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Device for Audio Input">Device</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="vv.voice.input.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">vv.voice.input.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Input for Audio">Input</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label2">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Device for Audio Output">Device</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="vv.voice.output.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">vv.voice.output.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Output for Audio">Output</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Volume:</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkVolumeButton" id="vv.voice.volume">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="focus_on_click">False</property>
    + <property name="receives_default">True</property>
    + <property name="relief">none</property>
    + <property name="orientation">vertical</property>
    + <property name="adjustment">adjustment2</property>
    + <property name="icons">audio-volume-muted-symbolic
    +audio-volume-high-symbolic
    +audio-volume-low-symbolic
    +audio-volume-medium-symbolic</property>
    + <child internal-child="plus_button">
    + <object class="GtkButton">
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="halign">center</property>
    + <property name="valign">center</property>
    + <property name="relief">none</property>
    + </object>
    + </child>
    + <child internal-child="minus_button">
    + <object class="GtkButton">
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="halign">center</property>
    + <property name="valign">center</property>
    + <property name="relief">none</property>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="vv.voice.threshold_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Silence threshold:</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkScale" id="vv.voice.threshold">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="adjustment">adjustment1</property>
    + <property name="round_digits">0</property>
    + <property name="digits">0</property>
    + <property name="draw_value">False</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToggleButton" id="vv.voice.test">
    + <property name="label" translatable="yes">Test Audio</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">5</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkProgressBar" id="vv.voice.level">
    + <property name="visible">True</property>
    + <property name="sensitive">False</property>
    + <property name="can_focus">False</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">6</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Audio</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label3">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Device for Video Input">Device</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="vv.video.input.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">vv.video.input.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Input for Video">Input</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <child>
    + <object class="GtkAlignment">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="left_padding">12</property>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="label4">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Device for Video Output">Device</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkComboBox" id="vv.video.output.combo">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="model">vv.video.output.store</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes" context="Output for Video">Output</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkAspectFrame">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label_xalign">0</property>
    + <property name="shadow_type">none</property>
    + <property name="ratio">1.3300000429153442</property>
    + <child>
    + <object class="GtkDrawingArea" id="vv.video.test_area">
    + <property name="width_request">240</property>
    + <property name="height_request">180</property>
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkToggleButton" id="vv.video.test">
    + <property name="label" translatable="yes">Test Video</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Video</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + </object>
    + <object class="GtkSizeGroup" id="vv.sg">
    + <widgets>
    + <widget name="label1"/>
    + <widget name="label2"/>
    + <widget name="label3"/>
    + <widget name="label4"/>
    + </widgets>
    + </object>
    +</interface>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/resources/Whiteboard/whiteboard.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,96 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!-- Generated with glade 3.22.1 -->
    +<interface>
    + <requires lib="gtk+" version="3.20"/>
    + <template class="PidginWhiteboard" parent="GtkWindow">
    + <property name="can_focus">False</property>
    + <property name="title" translatable="yes">Pidgin Whiteboard</property>
    + <property name="resizable">False</property>
    + <signal name="delete-event" handler="whiteboard_close_cb" swapped="no"/>
    + <child>
    + <placeholder/>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkDrawingArea" id="drawing_area">
    + <property name="width_request">300</property>
    + <property name="height_request">250</property>
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_LEAVE_NOTIFY_MASK</property>
    + <signal name="button-press-event" handler="pidgin_whiteboard_brush_down" object="PidginWhiteboard" swapped="no"/>
    + <signal name="button-release-event" handler="pidgin_whiteboard_brush_up" object="PidginWhiteboard" swapped="no"/>
    + <signal name="configure-event" handler="pidgin_whiteboard_configure_event" object="PidginWhiteboard" swapped="no"/>
    + <signal name="draw" handler="pidgin_whiteboard_draw_event" object="PidginWhiteboard" swapped="no"/>
    + <signal name="motion-notify-event" handler="pidgin_whiteboard_brush_motion" object="PidginWhiteboard" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">6</property>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">_Clear</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="pidgin_whiteboard_button_clear_press" object="PidginWhiteboard" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton">
    + <property name="label" translatable="yes">_Save</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="pidgin_whiteboard_button_save_press" object="PidginWhiteboard" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkColorButton" id="color_button">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <signal name="color-set" handler="color_selected" object="PidginWhiteboard" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </template>
    +</interface>
    --- /dev/null Thu Jan 01 00:00:00 1970 +0000
    +++ b/pidgin/resources/Xfer/xfer.ui Tue Oct 08 21:48:28 2019 -0500
    @@ -0,0 +1,534 @@
    +<?xml version="1.0" encoding="UTF-8"?>
    +<!-- Generated with glade 3.22.1 -->
    +<interface>
    + <requires lib="gtk+" version="3.20"/>
    + <object class="GtkListStore" id="model">
    + <columns>
    + <!-- column-name status -->
    + <column type="gchararray"/>
    + <!-- column-name progress -->
    + <column type="gint"/>
    + <!-- column-name filename -->
    + <column type="gchararray"/>
    + <!-- column-name size -->
    + <column type="gchararray"/>
    + <!-- column-name remaining -->
    + <column type="gchararray"/>
    + <!-- column-name xfer -->
    + <column type="PurpleXfer"/>
    + </columns>
    + </object>
    + <template class="PidginXferDialog" parent="GtkDialog">
    + <property name="can_focus">False</property>
    + <property name="title" translatable="yes">File Transfer</property>
    + <property name="role">file transfer</property>
    + <property name="default_width">450</property>
    + <property name="default_height">250</property>
    + <property name="type_hint">normal</property>
    + <signal name="delete-event" handler="delete_win_cb" swapped="no"/>
    + <child>
    + <placeholder/>
    + </child>
    + <child internal-child="vbox">
    + <object class="GtkBox">
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <property name="spacing">2</property>
    + <child internal-child="action_area">
    + <object class="GtkButtonBox">
    + <property name="can_focus">False</property>
    + <property name="layout_style">end</property>
    + <child>
    + <object class="GtkButton" id="open_button">
    + <property name="label" translatable="yes">_Open</property>
    + <property name="visible">True</property>
    + <property name="sensitive">False</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="open_button_cb" object="PidginXferDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="remove_button">
    + <property name="label" translatable="yes">_Remove</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="remove_button_cb" object="PidginXferDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="stop_button">
    + <property name="label" translatable="yes">_Stop</property>
    + <property name="visible">True</property>
    + <property name="sensitive">False</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="stop_button_cb" object="PidginXferDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkButton" id="close_button">
    + <property name="label" translatable="yes">_Close</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">True</property>
    + <property name="use_underline">True</property>
    + <signal name="clicked" handler="close_button_cb" object="PidginXferDialog" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkBox">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="orientation">vertical</property>
    + <child>
    + <object class="GtkScrolledWindow">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="shadow_type">in</property>
    + <property name="min_content_height">140</property>
    + <child>
    + <object class="GtkTreeView" id="tree">
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="model">model</property>
    + <child internal-child="selection">
    + <object class="GtkTreeSelection">
    + <signal name="changed" handler="selection_changed_cb" object="PidginXferDialog" swapped="no"/>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="resizable">True</property>
    + <property name="sizing">fixed</property>
    + <property name="fixed_width">25</property>
    + <child>
    + <object class="GtkCellRendererPixbuf"/>
    + <attributes>
    + <attribute name="icon-name">0</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="resizable">True</property>
    + <property name="title" translatable="yes">Progress</property>
    + <child>
    + <object class="GtkCellRendererProgress"/>
    + <attributes>
    + <attribute name="value">1</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="resizable">True</property>
    + <property name="title" translatable="yes">Filename</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">2</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="resizable">True</property>
    + <property name="title" translatable="yes">Size</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">3</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + <child>
    + <object class="GtkTreeViewColumn">
    + <property name="resizable">True</property>
    + <property name="title" translatable="yes">Remaining</property>
    + <child>
    + <object class="GtkCellRendererText"/>
    + <attributes>
    + <attribute name="text">4</attribute>
    + </attributes>
    + </child>
    + </object>
    + </child>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">True</property>
    + <property name="fill">True</property>
    + <property name="position">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="keep_open">
    + <property name="label" translatable="yes">Close this window when all transfers _finish</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + <signal name="toggled" handler="toggle_keep_open_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkCheckButton" id="auto_clear">
    + <property name="label" translatable="yes">C_lear finished transfers</property>
    + <property name="visible">True</property>
    + <property name="can_focus">True</property>
    + <property name="receives_default">False</property>
    + <property name="use_underline">True</property>
    + <property name="draw_indicator">True</property>
    + <signal name="toggled" handler="toggle_clear_finished_cb" swapped="no"/>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkExpander" id="expander">
    + <property name="visible">True</property>
    + <property name="sensitive">False</property>
    + <property name="can_focus">True</property>
    + <property name="resize_toplevel">True</property>
    + <child>
    + <object class="GtkGrid">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="margin_left">20</property>
    + <property name="row_spacing">6</property>
    + <property name="column_spacing">6</property>
    + <child>
    + <object class="GtkLabel" id="local_user_desc_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="remote_user_desc_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Protocol:</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Filename:</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Local File:</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Status:</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">5</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Speed:</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">6</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Time Elapsed:</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">7</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">Time Remaining:</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + <attributes>
    + <attribute name="weight" value="bold"/>
    + </attributes>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">8</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="local_user_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="hexpand">True</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">0</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="remote_user_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="hexpand">True</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">1</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="protocol_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">2</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="filename_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">3</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="localfile_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">4</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="status_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">5</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="speed_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">6</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="time_elapsed_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">7</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkLabel" id="time_remaining_label">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="justify">right</property>
    + <property name="xalign">0</property>
    + </object>
    + <packing>
    + <property name="left_attach">1</property>
    + <property name="top_attach">8</property>
    + </packing>
    + </child>
    + <child>
    + <object class="GtkProgressBar" id="progress">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="show_text">True</property>
    + </object>
    + <packing>
    + <property name="left_attach">0</property>
    + <property name="top_attach">9</property>
    + <property name="width">2</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + <child type="label">
    + <object class="GtkLabel">
    + <property name="visible">True</property>
    + <property name="can_focus">False</property>
    + <property name="label" translatable="yes">File transfer _details</property>
    + <property name="use_underline">True</property>
    + </object>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">False</property>
    + <property name="position">3</property>
    + </packing>
    + </child>
    + </object>
    + <packing>
    + <property name="expand">False</property>
    + <property name="fill">True</property>
    + <property name="position">1</property>
    + </packing>
    + </child>
    + </object>
    + </child>
    + </template>
    +</interface>
    --- a/pidgin/resources/pidgin.gresource.xml Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/resources/pidgin.gresource.xml Tue Oct 08 21:48:28 2019 -0500
    @@ -5,11 +5,14 @@
    <file compressed="true">About/about.ui</file>
    <file compressed="true">About/about.md</file>
    <file compressed="true">About/credits.json</file>
    + <file compressed="true">Accounts/chooser.ui</file>
    <file compressed="true">Conversations/invite_dialog.ui</file>
    <file compressed="true">Debug/debug.ui</file>
    - <file compressed="true">Debug/filter-popover.ui</file>
    <file compressed="true">Debug/plugininfo.ui</file>
    <file compressed="true">Log/log-viewer.ui</file>
    <file compressed="true">Prefs/prefs.ui</file>
    + <file compressed="true">Prefs/vv.ui</file>
    + <file compressed="true">Whiteboard/whiteboard.ui</file>
    + <file compressed="true">Xfer/xfer.ui</file>
    </gresource>
    </gresources>
    --- a/pidgin/themes/Contents/Info.plist Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,16 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8"?>
    -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    -<plist version="1.0">
    -<dict>
    - <key>MessageViewVersion</key>
    - <integer>4</integer>
    - <key>CFBundleName</key>
    - <string>Default</string>
    - <key>CFBundleIdentifier</key>
    - <string>im.pidgin.Default.style</string>
    - <key>DefaultFontFamily</key>
    - <string>sans-serif</string>
    - <key>DefaultFontSize</key>
    - <integer>11</integer>
    -</dict>
    -</plist>
    --- a/pidgin/themes/Contents/Resources/Content.html Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,6 +0,0 @@
    -<div class="x-container %messageClasses% %messageDirection%">
    - <abbr class="x-time" title="%time{yyyy-MM-ddTHH:mm:ssZZ}%">(%time%)</abbr>
    - <span class="x-sender" title="%senderScreenName%">%sender%:</span>
    - <span class="x-message">%message%</span>
    -</div>
    -<div id="insert"></div>
    --- a/pidgin/themes/Contents/Resources/Incoming/Content.html Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,8 +0,0 @@
    -<div class="x-container %messageClasses% %messageDirection%">
    - <span class="x-header" style="color: %senderColor%;">
    - <abbr class="x-time" title="%time{yyyy-MM-ddTHH:mm:ssZZ}%">(%time%)</abbr>
    - <span class="x-sender" title="%senderScreenName%">%sender%:</span>
    - </span>
    - <span class="x-message">%message%</span>
    -</div>
    -<div id="insert"></div>
    --- a/pidgin/themes/Contents/Resources/Status.html Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,7 +0,0 @@
    -<div class="x-status_container %messageClasses% %messageDirection% %status%">
    - <span class="x-header">
    - <abbr class="x-time" title="%time{yyyy-MM-ddTHH:mm:ssZZ}%">%time%</abbr>
    - </span>
    - <span class="x-message">%message%</span>
    -</div>
    -<div id="insert"></div>
    --- a/pidgin/themes/Contents/Resources/Variants/Default.css Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2 +0,0 @@
    -@import ../main.css;
    -
    --- a/pidgin/themes/Contents/Resources/Variants/No-Timestamps.css Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,8 +0,0 @@
    -@import ../main.css;
    -
    -.x-container .x-time,
    -.x-status_container .x-time
    -{
    - display: none;
    -}
    -
    --- a/pidgin/themes/Contents/Resources/main.css Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,95 +0,0 @@
    -body {
    - word-wrap: break-word;
    - word-break: break-word;
    -
    - font-size: 13px;
    - margin: 2px;
    - overflow-y: scroll;
    -}
    -
    -.x-container,
    -.x-status_container
    -{
    - clear: left;
    - line-height: 1.3em;
    - margin-bottom: 4px;
    - height: 100%;
    - overflow: hidden;
    -}
    -
    -.x-container .x-time,
    -.x-status_container .x-time
    -{
    - display: inline;
    - font-size: 12px;
    -}
    -
    -.x-container .x-sender
    -{
    - font-weight: bold;
    -}
    -
    -.x-message p
    -{
    - margin: 0;
    -}
    -
    -.x-message p:first-child
    -{
    - display: inline;
    -}
    -
    -/* Don't let inline images overflow the window */
    -img
    -{
    - max-width: 100%
    -}
    -
    -/* Colour-ify things */
    -
    -#Chat:not(.groupchat) .x-container.incoming .x-header
    -{
    - color: #cc0000 ! important;
    -}
    -
    -.x-container.outgoing .x-time,
    -.x-container.outgoing .x-sender
    -{
    - color: #204a87;
    -}
    -
    -.x-container.mention .x-time,
    -.x-container.mention .x-sender
    -{
    - color: #AF7F00;
    -}
    -
    -.x-status_container
    -{
    - color: #777777;
    -}
    -
    -.x-status_container.error
    -{
    - color: #ff0000;
    - font-weight: bold;
    -}
    -
    -/* Inline images */
    -
    -.pending-image
    -{
    - cursor: progress;
    -}
    -
    -.pending-image img
    -{
    - display: none;
    -}
    -
    -/* Emoticons */
    -
    -.emoticon
    -{
    - vertical-align: bottom;
    -}
    --- a/pidgin/themes/Template.html Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,376 +0,0 @@
    -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    -<html>
    -<head>
    - <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    - <base href="%@">
    - <script type="text/javascript" defer="defer">
    - // NOTE:
    - // Any percent signs in this file must be escaped!
    - // Use two escape signs (%%) to display it, this is passed through a format call!
    -
    - var PURPLE_IMAGE_STORE_PROTOCOL = 'purple-image:';
    -
    - function appendHTML(html) {
    - var node = document.getElementById("Chat");
    - var range = document.createRange();
    - range.selectNode(node);
    - var documentFragment = range.createContextualFragment(html);
    - node.appendChild(documentFragment);
    - }
    -
    - // a coalesced HTML object buffers and outputs DOM objects en masse.
    - // saves A LOT of CSS recalculation time when loading many messages.
    - // (ex. a long twitter timeline)
    - function CoalescedHTML() {
    - var self = this;
    - this.fragment = document.createDocumentFragment();
    - this.timeoutID = 0;
    - this.coalesceRounds = 0;
    - this.isCoalescing = false;
    - this.isConsecutive = undefined;
    - this.shouldScroll = undefined;
    -
    - var appendElement = function (elem) {
    - document.getElementById("Chat").appendChild(elem);
    - };
    -
    - function outputHTML() {
    - var insert = document.getElementById("insert");
    - if(!!insert && self.isConsecutive) {
    - insert.parentNode.replaceChild(self.fragment, insert);
    - } else {
    - if(insert)
    - insert.parentNode.removeChild(insert);
    - // insert the documentFragment into the live DOM
    - appendElement(self.fragment);
    - }
    - alignChat(self.shouldScroll);
    -
    - // reset state to empty/non-coalescing
    - self.shouldScroll = undefined;
    - self.isConsecutive = undefined;
    - self.isCoalescing = false;
    - self.coalesceRounds = 0;
    - }
    -
    - // creates and returns a new documentFragment, containing all content nodes
    - // which can be inserted as a single node.
    - function createHTMLNode(html) {
    - var range = document.createRange();
    - range.selectNode(document.getElementById("Chat"));
    - return range.createContextualFragment(html);
    - }
    -
    - // removes first insert node from the internal fragment.
    - function rmInsertNode() {
    - var insert = self.fragment.querySelector("#insert");
    - if(insert)
    - insert.parentNode.removeChild(insert);
    - }
    -
    - function setShouldScroll(flag) {
    - if(flag && undefined === self.shouldScroll)
    - self.shouldScroll = flag;
    - }
    -
    - // hook in a custom method to append new data
    - // to the chat.
    - this.setAppendElementMethod = function (func) {
    - if(typeof func === 'function')
    - appendElement = func;
    - }
    -
    - // (re)start the coalescing timer.
    - // we wait 25ms for a new message to come in.
    - // If we get one, restart the timer and wait another 10ms.
    - // If not, run outputHTML()
    - // We do this a maximum of 400 times, for 10s max that can be spent
    - // coalescing input, since this will block display.
    - this.coalesce = function() {
    - window.clearTimeout(self.timeoutID);
    - self.timeoutID = window.setTimeout(outputHTML, 25);
    - self.isCoalescing = true;
    - self.coalesceRounds += 1;
    - if(400 < self.coalesceRounds)
    - self.cancel();
    - }
    -
    - // if we need to append content into an insertion div,
    - // we need to clear the buffer and cancel the timeout.
    - this.cancel = function() {
    - if(self.isCoalescing) {
    - window.clearTimeout(self.timeoutID);
    - outputHTML();
    - }
    - }
    -
    -
    - // coalased analogs to the global functions
    -
    - this.append = function(html, shouldScroll) {
    - // if we started this fragment with a consecuative message,
    - // cancel and output before we continue
    - if(self.isConsecutive) {
    - self.cancel();
    - }
    - self.isConsecutive = false;
    - rmInsertNode();
    - var node = createHTMLNode(html);
    - self.fragment.appendChild(node);
    -
    - node = null;
    -
    - setShouldScroll(shouldScroll);
    - self.coalesce();
    - }
    -
    - this.appendNext = function(html, shouldScroll) {
    - if(undefined === self.isConsecutive)
    - self.isConsecutive = true;
    - var node = createHTMLNode(html);
    - var insert = self.fragment.querySelector("#insert");
    - if(insert) {
    - insert.parentNode.replaceChild(node, insert);
    - } else {
    - self.fragment.appendChild(node);
    - }
    - node = null;
    - setShouldScroll(shouldScroll);
    - self.coalesce();
    - }
    -
    - this.replaceLast = function (html, shouldScroll) {
    - rmInsertNode();
    - var node = createHTMLNode(html);
    - var lastMessage = self.fragment.lastChild;
    - lastMessage.parentNode.replaceChild(node, lastMessage);
    - node = null;
    - setShouldScroll(shouldScroll);
    - }
    - }
    - var coalescedHTML;
    -
    - //Appending new content to the message view
    - function appendMessage(html) {
    - var shouldScroll;
    -
    - // Only call nearBottom() if should scroll is undefined.
    - if(undefined === coalescedHTML.shouldScroll) {
    - shouldScroll = nearBottom();
    - } else {
    - shouldScroll = coalescedHTML.shouldScroll;
    - }
    - appendMessageNoScroll(html, shouldScroll);
    - }
    -
    - function appendMessageNoScroll(html, shouldScroll) {
    - shouldScroll = shouldScroll || false;
    - // always try to coalesce new, non-griuped, messages
    - coalescedHTML.append(html, shouldScroll)
    - }
    -
    - function appendNextMessage(html){
    - var shouldScroll;
    - if(undefined === coalescedHTML.shouldScroll) {
    - shouldScroll = nearBottom();
    - } else {
    - shouldScroll = coalescedHTML.shouldScroll;
    - }
    - appendNextMessageNoScroll(html, shouldScroll);
    - }
    -
    - function appendNextMessageNoScroll(html, shouldScroll){
    - shouldScroll = shouldScroll || false;
    - // only group next messages if we're already coalescing input
    - coalescedHTML.appendNext(html, shouldScroll);
    - }
    -
    - function replaceLastMessage(html){
    - var shouldScroll;
    - // only replace messages if we're already coalescing
    - if(coalescedHTML.isCoalescing){
    - if(undefined === coalescedHTML.shouldScroll) {
    - shouldScroll = nearBottom();
    - } else {
    - shouldScroll = coalescedHTML.shouldScroll;
    - }
    - coalescedHTML.replaceLast(html, shouldScroll);
    - } else {
    - shouldScroll = nearBottom();
    - //Retrieve the current insertion point, then remove it
    - //This requires that there have been an insertion point... is there a better way to retrieve the last element? -evands
    - var insert = document.getElementById("insert");
    - if(insert){
    - var parentNode = insert.parentNode;
    - parentNode.removeChild(insert);
    - var lastMessage = document.getElementById("Chat").lastChild;
    - document.getElementById("Chat").removeChild(lastMessage);
    - }
    -
    - //Now append the message itself
    - appendHTML(html);
    -
    - alignChat(shouldScroll);
    - }
    - }
    -
    - var SCROLLMODE_UNKNOWN = 0;
    - var SCROLLMODE_WEBKIT1 = 1;
    - var SCROLLMODE_WEBKIT2 = 2;
    - var scroll_mode = SCROLLMODE_UNKNOWN;
    -
    - function detectWebkitScrolling() {
    - if (scroll_mode != SCROLLMODE_UNKNOWN)
    - return scroll_mode;
    - if (document.body.scrollTop > 0)
    - scroll_mode = SCROLLMODE_WEBKIT1;
    - if (document.documentElement.scrollTop > 0)
    - scroll_mode = SCROLLMODE_WEBKIT2;
    - return scroll_mode;
    - }
    -
    - var stickyscroll_just_scrolled = false;
    - var stickyscroll_just_scrolled_more = false;
    - var stickyscroll_to_bottom = true;
    - var stickyscroll_to_bottom_new = true;
    -
    - function windowDidScroll(ev) {
    - if (stickyscroll_just_scrolled) {
    - stickyscroll_just_scrolled_more = true;
    - return;
    - }
    -
    - stickyscroll_just_scrolled = true;
    - var update_to_bottom = function() {
    - stickyscroll_to_bottom = stickyscroll_to_bottom_new;
    -
    - var mode = detectWebkitScrolling();
    - if (mode == SCROLLMODE_UNKNOWN || mode == SCROLLMODE_WEBKIT1) {
    - stickyscroll_to_bottom_new = ( document.body.scrollTop >=
    - ( document.body.offsetHeight - (window.innerHeight + 20) ) );
    - } else { /* SCROLLMODE_WEBKIT2 */
    - stickyscroll_to_bottom_new = ( document.documentElement.scrollTop >=
    - ( document.documentElement.offsetHeight - (window.innerHeight + 20) ) );
    - }
    -
    - if (stickyscroll_just_scrolled_more) {
    - stickyscroll_just_scrolled_more = false;
    - setTimeout(update_to_bottom, 10);
    - } else {
    - stickyscroll_just_scrolled = false;
    - }
    - };
    - setTimeout(update_to_bottom, 10);
    - }
    -
    - //Auto-scroll to bottom. Use nearBottom to determine if a scrollToBottom is desired.
    - function nearBottom() {
    - return stickyscroll_to_bottom;
    - }
    - function scrollToBottom() {
    - var mode = detectWebkitScrolling();
    -
    - var scrollfunc;
    - if (mode == SCROLLMODE_UNKNOWN || mode == SCROLLMODE_WEBKIT1) {
    - scrollfunc = function() {
    - document.body.scrollTop = document.body.offsetHeight;
    - };
    - } else { /* SCROLLMODE_WEBKIT2 */
    - scrollfunc = function() {
    - document.documentElement.scrollTop =
    - document.documentElement.offsetHeight;
    - window.scrollTo(0, document.body.scrollHeight);
    - };
    - }
    -
    - scrollfunc();
    - /* wait for content to load and scroll again */
    - setTimeout(scrollfunc, 10);
    - }
    -
    - //Dynamically exchange the active stylesheet
    - function setStylesheet( id, url ) {
    - var code = "<style id=\"" + id + "\" type=\"text/css\" media=\"screen,print\">";
    - if( url.length )
    - code += "@import url( \"" + url + "\" );";
    - code += "</style>";
    - var range = document.createRange();
    - var head = document.getElementsByTagName( "head" ).item(0);
    - range.selectNode( head );
    - var documentFragment = range.createContextualFragment( code );
    - head.removeChild( document.getElementById( id ) );
    - head.appendChild( documentFragment );
    - }
    -
    - //If true is passed, view will be scrolled down
    - function alignChat(shouldScroll) {
    - if (!shouldScroll)
    - return;
    -
    - scrollToBottom();
    - }
    -
    - function remoteImageIsReady(id) {
    - var shouldScroll = nearBottom();
    - var emoticons;
    -
    - /* There is a possible race condition: if we call this
    - * before the span.emoticon.pending is added, the latter
    - * won't be converted.
    - *
    - * We could avoid this, by calling it again using
    - * setTimeout, but it may affect performance. So, we
    - * won't do it until anyone complains.
    - */
    -
    - emoticons = document.getElementsByClassName(
    - 'pending-image-id-' + id);
    - for (var i = 0; i < emoticons.length; i++) {
    - var node = emoticons[i];
    - var img = node.getElementsByTagName('img')[0];
    - img.setAttribute('src', PURPLE_IMAGE_STORE_PROTOCOL + id);
    - node.parentNode.replaceChild(img, node);
    - }
    -
    - alignChat(shouldScroll);
    - }
    -
    - window.onresize = function windowDidResize(){
    - alignChat(nearBottom());
    - }
    -
    - function initStyle() {
    - alignChat(true);
    - if(!coalescedHTML)
    - coalescedHTML = new CoalescedHTML();
    -
    - window.addEventListener('scroll', windowDidScroll);
    - }
    - </script>
    -
    - <style type="text/css">
    - .actionMessageUserName { display:none; }
    - .actionMessageBody:before { content:"*"; }
    - .actionMessageBody:after { content:"*"; }
    - * { word-wrap:break-word }
    - img.scaledToFitImage { height: auto; max-width: 100%%; }
    - </style>
    -
    - <!-- This style is shared by all variants. !-->
    - <style id="baseStyle" type="text/css" media="screen,print">
    - %@
    - </style>
    -
    - <!-- Although we call this mainStyle for legacy reasons, it's actually the variant style !-->
    - <style id="mainStyle" type="text/css" media="screen,print">
    - @import url( "%@" );
    - </style>
    -
    -</head>
    -<body onload="initStyle();" style="==bodyBackground==">
    -%@
    -<div id="Chat">
    -</div>
    -%@
    -</body>
    -</html>
    --- a/pidgin/themes/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,6 +0,0 @@
    -themetemplatedir = get_option('datadir') + '/pidgin/theme'
    -
    -install_data('Template.html',
    - install_dir : themetemplatedir)
    -install_subdir('Contents',
    - install_dir : themetemplatedir)
    --- a/pidgin/win32/gtkwin32dep.c Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/win32/gtkwin32dep.c Tue Oct 08 21:48:28 2019 -0500
    @@ -416,7 +416,7 @@
    if (MySetLogFile) {
    gchar *debug_dir, *locale_debug_dir;
    - debug_dir = g_build_filename(purple_user_dir(), "pidgin.RPT", NULL);
    + debug_dir = g_build_filename(purple_cache_dir(), "pidgin.RPT", NULL);
    locale_debug_dir = g_locale_from_utf8(debug_dir, -1, NULL, NULL, NULL);
    purple_debug_info("winpidgin", "Setting exchndl.dll LogFile to %s\n", debug_dir);
    --- a/pidgin/win32/nsis/generate_gtk_zip.sh Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/win32/nsis/generate_gtk_zip.sh Tue Oct 08 21:48:28 2019 -0500
    @@ -273,9 +273,6 @@
    ARC_VV_LIBVORBIS="${DOWNLOAD_HOST}mingw32-libvorbis-1.3.3-1.8.noarch.rpm libvorbis 1.3.3-1.8 sha1sum:c9efd698ed62c26cf62442dafc2d9d2dcbcd651c"
    ALL+="ARC_VV_LIBVORBIS "
    -ARC_WEBKITGTK="${DOWNLOAD_HOST}mingw32-libwebkitgtk-1.10.2-9.2.noarch.rpm WebKitGTK+ 1.10.2-9.2 sha1sum:010dbad413f824696cd1e32fe70046c9a1cb425f"
    -ALL+="ARC_WEBKITGTK "
    -
    ARC_ZLIB="${DOWNLOAD_HOST}mingw32-zlib-1.2.8-2.6.noarch.rpm zlib 1.2.8-2.6 sha1sum:bb75b2a341309eb75daacb93d43d6c072c71923c"
    ALL+="ARC_ZLIB "
    --- a/pidgin/win32/nsis/pidgin-installer.nsi Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/win32/nsis/pidgin-installer.nsi Tue Oct 08 21:48:28 2019 -0500
    @@ -543,7 +543,6 @@
    Delete "$INSTDIR\plugins\gtkbuddynote.dll"
    Delete "$INSTDIR\plugins\history.dll"
    Delete "$INSTDIR\plugins\internalkeyring.dll"
    - Delete "$INSTDIR\plugins\webkit.dll"
    Delete "$INSTDIR\plugins\wincred.dll"
    Delete "$INSTDIR\plugins\iconaway.dll"
    Delete "$INSTDIR\plugins\idle.dll"
    --- a/pidgin/win32/prepare-workspace.sh Tue Oct 08 21:47:58 2019 -0500
    +++ b/pidgin/win32/prepare-workspace.sh Tue Oct 08 21:48:28 2019 -0500
    @@ -119,9 +119,6 @@
    ARC_XML="${DOWNLOAD_HOST}mingw32-libxml2-devel-2.9.0-2.1.noarch.rpm;libxml2;2.9.0-2.1;bd63823e0be2436ee7d2369aa254e7214a0dd692;${OBS_SKIP};libxml2-2.9"
    ARCHIVES+="ARC_XML "
    -ARC_WKG="${DOWNLOAD_HOST}mingw32-libwebkitgtk-devel-1.10.2-9.2.noarch.rpm;WebKitGTK+;1.10.2-9.2;02cd5de75e3b4269bc1a31320e95f455d5804be9;${OBS_SKIP};libwebkitgtk-1.10"
    -ARCHIVES+="ARC_WKG "
    -
    ARC_SOU="${DOWNLOAD_HOST}mingw32-libsoup-devel-2.42.2-1.1.noarch.rpm;libsoup;2.42.2-1.1;cb4e520f1bb17c83230f28bb225420dce54c8d80;${OBS_SKIP};libsoup-2.42"
    ARCHIVES+="ARC_SOU "
    --- a/po/POTFILES.in Tue Oct 08 21:47:58 2019 -0500
    +++ b/po/POTFILES.in Tue Oct 08 21:48:28 2019 -0500
    @@ -1,12 +1,13 @@
    -[encoding: UTF-8]
    finch/finch.c
    finch/gntaccount.c
    finch/gntblist.c
    finch/gntconn.c
    finch/gntconv.c
    finch/gntdebug.c
    +finch/gntidle.c
    finch/gntlog.c
    finch/gntmedia.c
    +finch/gntmenuutil.c
    finch/gntnotify.c
    finch/gntplugin.c
    finch/gntpounce.c
    @@ -25,23 +26,56 @@
    finch/plugins/grouping.c
    finch/plugins/lastlog.c
    libpurple/account.c
    +libpurple/accountopt.c
    libpurple/accounts.c
    +libpurple/action.c
    +libpurple/attention.c
    +libpurple/blistnode.c
    +libpurple/buddy.c
    +libpurple/buddyicon.c
    libpurple/buddylist.c
    +libpurple/chat.c
    +libpurple/circularbuffer.c
    +libpurple/cmds.c
    libpurple/connection.c
    +libpurple/contact.c
    libpurple/conversation.c
    +libpurple/conversations.c
    libpurple/conversationtypes.c
    -libpurple/data/purple-url-handler.desktop.in.in
    +libpurple/core.c
    +libpurple/countingnode.c
    +libpurple/debug.c
    +libpurple/e2ee.c
    +libpurple/eventloop.c
    +libpurple/example/nullclient.c
    +libpurple/group.c
    libpurple/http.c
    +libpurple/idle.c
    +libpurple/image.c
    +libpurple/image-store.c
    libpurple/keyring.c
    libpurple/log.c
    +libpurple/media/backend-fs2.c
    +libpurple/media/backend-iface.c
    +libpurple/media.c
    +libpurple/media/candidate.c
    +libpurple/media/codec.c
    +libpurple/media/enum-types.c
    libpurple/mediamanager.c
    -libpurple/media/backend-fs2.c
    +libpurple/memorypool.c
    libpurple/message.c
    +libpurple/nat-pmp.c
    +libpurple/network.c
    +libpurple/notify.c
    libpurple/options.c
    -libpurple/plugins.c
    +libpurple/pluginpref.c
    libpurple/plugins/autoaccept.c
    libpurple/plugins/buddynote.c
    +libpurple/plugins.c
    +libpurple/plugins/codeinline.c
    +libpurple/plugins/debug_example.c
    libpurple/plugins/filectl.c
    +libpurple/plugins/helloworld.c
    libpurple/plugins/idle.c
    libpurple/plugins/joinpart.c
    libpurple/plugins/keyrings/internalkeyring.c
    @@ -49,35 +83,63 @@
    libpurple/plugins/keyrings/secretservice.c
    libpurple/plugins/keyrings/wincred.c
    libpurple/plugins/log_reader.c
    +libpurple/plugins/notify_example.c
    libpurple/plugins/offlinemsg.c
    libpurple/plugins/one_time_password.c
    +libpurple/plugins/pluginpref_example.c
    libpurple/plugins/psychic.c
    libpurple/plugins/signals-test.c
    libpurple/plugins/simple.c
    libpurple/plugins/statenotify.c
    +libpurple/plugins/test-request-input.c
    +libpurple/pounce.c
    +libpurple/prefs.c
    libpurple/presence.c
    -libpurple/protocols.c
    +libpurple/protocol.c
    libpurple/protocols/bonjour/bonjour.c
    -libpurple/protocols/bonjour/bonjour.h
    +libpurple/protocols/bonjour/bonjour_ft.c
    +libpurple/protocols/bonjour/buddy.c
    +libpurple/protocols/bonjour/dns_sd_proxy.c
    libpurple/protocols/bonjour/jabber.c
    +libpurple/protocols/bonjour/mdns_avahi.c
    +libpurple/protocols/bonjour/mdns_common.c
    libpurple/protocols/bonjour/mdns_dns_sd.c
    +libpurple/protocols/bonjour/parser.c
    +libpurple/protocols.c
    libpurple/protocols/facebook/api.c
    +libpurple/protocols/facebook/data.c
    libpurple/protocols/facebook/facebook.c
    libpurple/protocols/facebook/http.c
    libpurple/protocols/facebook/json.c
    libpurple/protocols/facebook/mqtt.c
    +libpurple/protocols/facebook/thrift.c
    libpurple/protocols/facebook/util.c
    libpurple/protocols/gg/account.c
    +libpurple/protocols/gg/avatar.c
    +libpurple/protocols/gg/blist.c
    libpurple/protocols/gg/chat.c
    libpurple/protocols/gg/edisc.c
    libpurple/protocols/gg/gg.c
    +libpurple/protocols/gg/html.c
    libpurple/protocols/gg/image-prpl.c
    +libpurple/protocols/gg/keymapper.c
    +libpurple/protocols/gg/libgadu-events.c
    +libpurple/protocols/gg/libgaduw.c
    libpurple/protocols/gg/message-prpl.c
    libpurple/protocols/gg/multilogon.c
    +libpurple/protocols/gg/oauth/oauth.c
    +libpurple/protocols/gg/oauth/oauth-parameter.c
    +libpurple/protocols/gg/oauth/oauth-purple.c
    libpurple/protocols/gg/pubdir-prpl.c
    libpurple/protocols/gg/purplew.c
    +libpurple/protocols/gg/resolver-purple.c
    +libpurple/protocols/gg/roster.c
    +libpurple/protocols/gg/servconn.c
    libpurple/protocols/gg/status.c
    +libpurple/protocols/gg/tcpsocket.c
    +libpurple/protocols/gg/utils.c
    libpurple/protocols/gg/validator.c
    +libpurple/protocols/gg/xml.c
    libpurple/protocols/irc/cmds.c
    libpurple/protocols/irc/dcc_send.c
    libpurple/protocols/irc/irc.c
    @@ -89,43 +151,105 @@
    libpurple/protocols/jabber/auth_digest_md5.c
    libpurple/protocols/jabber/auth_plain.c
    libpurple/protocols/jabber/auth_scram.c
    +libpurple/protocols/jabber/auth_webex.c
    libpurple/protocols/jabber/bosh.c
    libpurple/protocols/jabber/buddy.c
    +libpurple/protocols/jabber/caps.c
    libpurple/protocols/jabber/chat.c
    +libpurple/protocols/jabber/data.c
    +libpurple/protocols/jabber/disco.c
    +libpurple/protocols/jabber/google/gmail.c
    +libpurple/protocols/jabber/google/google.c
    +libpurple/protocols/jabber/google/google_p2p.c
    +libpurple/protocols/jabber/google/google_presence.c
    +libpurple/protocols/jabber/google/google_roster.c
    +libpurple/protocols/jabber/google/google_session.c
    +libpurple/protocols/jabber/google/jingleinfo.c
    +libpurple/protocols/jabber/google/relay.c
    libpurple/protocols/jabber/gtalk.c
    +libpurple/protocols/jabber/ibb.c
    +libpurple/protocols/jabber/iq.c
    libpurple/protocols/jabber/jabber.c
    +libpurple/protocols/jabber/jingle/content.c
    +libpurple/protocols/jabber/jingle/iceudp.c
    +libpurple/protocols/jabber/jingle/jingle.c
    +libpurple/protocols/jabber/jingle/rawudp.c
    +libpurple/protocols/jabber/jingle/rtp.c
    +libpurple/protocols/jabber/jingle/session.c
    +libpurple/protocols/jabber/jingle/transport.c
    libpurple/protocols/jabber/jutil.c
    libpurple/protocols/jabber/message.c
    +libpurple/protocols/jabber/oob.c
    libpurple/protocols/jabber/parser.c
    +libpurple/protocols/jabber/pep.c
    +libpurple/protocols/jabber/ping.c
    libpurple/protocols/jabber/presence.c
    libpurple/protocols/jabber/roster.c
    libpurple/protocols/jabber/si.c
    +libpurple/protocols/jabber/tests/test_jabber_caps.c
    +libpurple/protocols/jabber/tests/test_jabber_digest_md5.c
    +libpurple/protocols/jabber/tests/test_jabber_jutil.c
    +libpurple/protocols/jabber/tests/test_jabber_scram.c
    +libpurple/protocols/jabber/useravatar.c
    libpurple/protocols/jabber/usermood.c
    libpurple/protocols/jabber/usernick.c
    +libpurple/protocols/jabber/usertune.c
    libpurple/protocols/jabber/xdata.c
    libpurple/protocols/jabber/xmpp.c
    +libpurple/protocols/novell/nmconference.c
    +libpurple/protocols/novell/nmconn.c
    +libpurple/protocols/novell/nmcontact.c
    +libpurple/protocols/novell/nmevent.c
    +libpurple/protocols/novell/nmfield.c
    +libpurple/protocols/novell/nmmessage.c
    +libpurple/protocols/novell/nmrequest.c
    +libpurple/protocols/novell/nmrtf.c
    libpurple/protocols/novell/nmuser.c
    +libpurple/protocols/novell/nmuserrecord.c
    libpurple/protocols/novell/novell.c
    +libpurple/protocols/null/nullprpl.c
    libpurple/protocols/oscar/aim.c
    libpurple/protocols/oscar/authorization.c
    +libpurple/protocols/oscar/bstream.c
    libpurple/protocols/oscar/clientlogin.c
    libpurple/protocols/oscar/encoding.c
    +libpurple/protocols/oscar/family_admin.c
    +libpurple/protocols/oscar/family_alert.c
    libpurple/protocols/oscar/family_auth.c
    +libpurple/protocols/oscar/family_bart.c
    +libpurple/protocols/oscar/family_bos.c
    +libpurple/protocols/oscar/family_buddy.c
    +libpurple/protocols/oscar/family_chat.c
    libpurple/protocols/oscar/family_chatnav.c
    libpurple/protocols/oscar/family_feedbag.c
    libpurple/protocols/oscar/family_icbm.c
    +libpurple/protocols/oscar/family_icq.c
    libpurple/protocols/oscar/family_locate.c
    +libpurple/protocols/oscar/family_oservice.c
    +libpurple/protocols/oscar/family_popup.c
    +libpurple/protocols/oscar/family_stats.c
    +libpurple/protocols/oscar/family_userlookup.c
    libpurple/protocols/oscar/flap_connection.c
    libpurple/protocols/oscar/icq.c
    libpurple/protocols/oscar/kerberos.c
    +libpurple/protocols/oscar/misc.c
    +libpurple/protocols/oscar/msgcookie.c
    libpurple/protocols/oscar/odc.c
    libpurple/protocols/oscar/oft.c
    libpurple/protocols/oscar/oscar.c
    +libpurple/protocols/oscar/oscar_data.c
    libpurple/protocols/oscar/peer.c
    +libpurple/protocols/oscar/peer_proxy.c
    +libpurple/protocols/oscar/rxhandlers.c
    +libpurple/protocols/oscar/snac.c
    +libpurple/protocols/oscar/tests/test_oscar_util.c
    +libpurple/protocols/oscar/tlv.c
    libpurple/protocols/oscar/userinfo.c
    libpurple/protocols/oscar/util.c
    libpurple/protocols/oscar/visibility.c
    +libpurple/protocols/sametime/im_mime.c
    libpurple/protocols/sametime/sametime.c
    +libpurple/protocols/sametime/tests/test_sametime_im_mime.c
    libpurple/protocols/silc/buddy.c
    libpurple/protocols/silc/chat.c
    libpurple/protocols/silc/ft.c
    @@ -134,88 +258,194 @@
    libpurple/protocols/silc/silc.c
    libpurple/protocols/silc/util.c
    libpurple/protocols/silc/wb.c
    +libpurple/protocols/simple/ntlm.c
    libpurple/protocols/simple/simple.c
    +libpurple/protocols/simple/sipmsg.c
    +libpurple/protocols/zephyr/error_message.c
    +libpurple/protocols/zephyr/et_name.c
    +libpurple/protocols/zephyr/init_et.c
    +libpurple/protocols/zephyr/ZAsyncLocate.c
    +libpurple/protocols/zephyr/ZCkAuth.c
    +libpurple/protocols/zephyr/ZCkIfNot.c
    +libpurple/protocols/zephyr/ZClosePort.c
    +libpurple/protocols/zephyr/ZCmpUID.c
    +libpurple/protocols/zephyr/ZCmpUIDP.c
    libpurple/protocols/zephyr/zephyr.c
    +libpurple/protocols/zephyr/zephyr_err.c
    +libpurple/protocols/zephyr/ZFlsLocs.c
    +libpurple/protocols/zephyr/ZFlsSubs.c
    +libpurple/protocols/zephyr/ZFmtAuth.c
    +libpurple/protocols/zephyr/ZFmtList.c
    +libpurple/protocols/zephyr/ZFmtNotice.c
    +libpurple/protocols/zephyr/ZFmtRaw.c
    +libpurple/protocols/zephyr/ZFmtRawLst.c
    +libpurple/protocols/zephyr/ZFmtSmRaw.c
    +libpurple/protocols/zephyr/ZFreeNot.c
    +libpurple/protocols/zephyr/ZGetLocs.c
    +libpurple/protocols/zephyr/ZGetSender.c
    +libpurple/protocols/zephyr/ZGetSubs.c
    +libpurple/protocols/zephyr/ZGetWGPort.c
    +libpurple/protocols/zephyr/ZhmStat.c
    +libpurple/protocols/zephyr/ZIfNotice.c
    +libpurple/protocols/zephyr/ZInit.c
    +libpurple/protocols/zephyr/Zinternal.c
    +libpurple/protocols/zephyr/ZLocations.c
    +libpurple/protocols/zephyr/ZMakeAscii.c
    +libpurple/protocols/zephyr/ZMkAuth.c
    +libpurple/protocols/zephyr/ZNewLocU.c
    +libpurple/protocols/zephyr/ZOpenPort.c
    +libpurple/protocols/zephyr/ZParseNot.c
    +libpurple/protocols/zephyr/ZPeekNot.c
    +libpurple/protocols/zephyr/ZPeekPkt.c
    +libpurple/protocols/zephyr/ZPending.c
    +libpurple/protocols/zephyr/ZReadAscii.c
    +libpurple/protocols/zephyr/ZRecvNot.c
    +libpurple/protocols/zephyr/ZRecvPkt.c
    +libpurple/protocols/zephyr/ZRetSubs.c
    +libpurple/protocols/zephyr/ZSendList.c
    +libpurple/protocols/zephyr/ZSendNot.c
    +libpurple/protocols/zephyr/ZSendPkt.c
    +libpurple/protocols/zephyr/ZSetDest.c
    +libpurple/protocols/zephyr/ZSetFD.c
    +libpurple/protocols/zephyr/ZSetSrv.c
    +libpurple/protocols/zephyr/ZSubs.c
    +libpurple/protocols/zephyr/ZVariables.c
    +libpurple/protocols/zephyr/ZWait4Not.c
    libpurple/proxy.c
    +libpurple/purple-gio.c
    +libpurple/queuedoutputstream.c
    libpurple/request.c
    -libpurple/request.h
    +libpurple/request-datasheet.c
    +libpurple/roomlist.c
    libpurple/savedstatuses.c
    libpurple/server.c
    +libpurple/signals.c
    libpurple/smiley.c
    +libpurple/smiley-custom.c
    +libpurple/smiley-list.c
    +libpurple/smiley-parser.c
    +libpurple/smiley-theme.c
    +libpurple/sound.c
    +libpurple/sound-theme.c
    +libpurple/sound-theme-loader.c
    libpurple/sslconn.c
    libpurple/status.c
    +libpurple/stringref.c
    +libpurple/stun.c
    +libpurple/tests/test_attention_type.c
    +libpurple/tests/test_image.c
    +libpurple/tests/test_protocol_action.c
    +libpurple/tests/test_protocol_attention.c
    +libpurple/tests/test_protocol_xfer.c
    +libpurple/tests/test_queued_output_stream.c
    +libpurple/tests/test_smiley.c
    +libpurple/tests/test_smiley_list.c
    +libpurple/tests/test_trie.c
    +libpurple/tests/test_ui.c
    +libpurple/tests/test_util.c
    +libpurple/tests/test_xmlnode.c
    +libpurple/theme.c
    +libpurple/theme-loader.c
    +libpurple/theme-manager.c
    +libpurple/trie.c
    +libpurple/upnp.c
    libpurple/util.c
    +libpurple/version.c
    +libpurple/whiteboard.c
    libpurple/win32/libc_interface.c
    +libpurple/win32/win32dep.c
    libpurple/xfer.c
    libpurple/xmlnode.c
    -[type: gettext/glade]pidgin/about.ui
    -pidgin/credits.json
    -pidgin/data/im.pidgin.Pidgin.appdata.xml.in
    -pidgin/data/im.pidgin.Pidgin.desktop.in.in
    pidgin/gtkaccount.c
    +pidgin/gtkblist.c
    pidgin/gtkblist-theme.c
    -pidgin/gtkblist.c
    +pidgin/gtkblist-theme-loader.c
    +pidgin/gtkcellrendererexpander.c
    pidgin/gtkconn.c
    pidgin/gtkconv.c
    -pidgin/gtkdebug.c
    pidgin/gtkdialogs.c
    +pidgin/gtkdnd-hints.c
    pidgin/gtkdocklet.c
    -pidgin/gtklog.c
    +pidgin/gtkicon-theme.c
    +pidgin/gtkicon-theme-loader.c
    +pidgin/gtkidle.c
    pidgin/gtkmedia.c
    +pidgin/gtkmenutray.c
    pidgin/gtknotify.c
    pidgin/gtkplugin.c
    +pidgin/gtkpluginpref.c
    pidgin/gtkpounce.c
    pidgin/gtkprefs.c
    pidgin/gtkprivacy.c
    pidgin/gtkrequest.c
    pidgin/gtkroomlist.c
    pidgin/gtksavedstatuses.c
    +pidgin/gtkscrollbook.c
    pidgin/gtksmiley-manager.c
    +pidgin/gtksmiley-theme.c
    pidgin/gtksound.c
    pidgin/gtkstatusbox.c
    +pidgin/gtkstatus-icon-theme.c
    +pidgin/gtkstyle.c
    pidgin/gtkutils.c
    -pidgin/gtkwebview.c
    -pidgin/gtkwebviewtoolbar.c
    pidgin/gtkwhiteboard.c
    pidgin/gtkxfer.c
    pidgin/libpidgin.c
    -pidgin/pidgin.h
    +pidgin/minidialog.c
    pidgin/pidginabout.c
    +pidgin/pidgin.c
    +pidgin/pidgincontactcompletion.c
    +pidgin/pidgindebug.c
    +pidgin/pidgindebugplugininfo.c
    +pidgin/pidgingdkpixbuf.c
    +pidgin/pidgininvitedialog.c
    +pidgin/pidginlog.c
    +pidgin/pidginmessage.c
    pidgin/pidginstock.c
    +pidgin/pidgintalkatu.c
    pidgin/pidgintooltip.c
    -pidgin/pixmaps/emotes/default/24/default.theme.in
    -pidgin/pixmaps/emotes/small/16/small.theme.in
    pidgin/plugins/cap/cap.c
    pidgin/plugins/contact_priority.c
    pidgin/plugins/disco/gtkdisco.c
    pidgin/plugins/disco/xmppdisco.c
    pidgin/plugins/extplacement.c
    pidgin/plugins/gestures/gestures.c
    +pidgin/plugins/gestures/stroke.c
    +pidgin/plugins/gestures/stroke-draw.c
    pidgin/plugins/gevolution/add_buddy_dialog.c
    pidgin/plugins/gevolution/assoc-buddy.c
    pidgin/plugins/gevolution/eds-utils.c
    +pidgin/plugins/gevolution/gevolution.c
    pidgin/plugins/gevolution/gevo-util.c
    -pidgin/plugins/gevolution/gevolution.c
    pidgin/plugins/gevolution/new_person_dialog.c
    +pidgin/plugins/gtkbuddynote.c
    pidgin/plugins/gtk-signals-test.c
    -pidgin/plugins/gtkbuddynote.c
    pidgin/plugins/history.c
    pidgin/plugins/iconaway.c
    pidgin/plugins/imgupload.c
    pidgin/plugins/mailchk.c
    -pidgin/plugins/markerline.c
    pidgin/plugins/musicmessaging/musicmessaging.c
    pidgin/plugins/notify.c
    pidgin/plugins/pidgininc.c
    pidgin/plugins/raw.c
    pidgin/plugins/relnot.c
    pidgin/plugins/screencap.c
    -pidgin/plugins/sendbutton.c
    pidgin/plugins/spellchk.c
    +pidgin/plugins/ticker/gtkticker.c
    pidgin/plugins/ticker/ticker.c
    pidgin/plugins/transparency.c
    pidgin/plugins/unity.c
    -pidgin/plugins/webkit.c
    +pidgin/plugins/win32/winprefs/gtkappbar.c
    pidgin/plugins/win32/winprefs/winprefs.c
    pidgin/plugins/xmppconsole.c
    -pidgin/win32/nsis/nsis_translations.desktop.in
    +pidgin/resources/About/about.ui
    +pidgin/resources/Conversations/invite_dialog.ui
    +pidgin/resources/Debug/debug.ui
    +pidgin/resources/Debug/plugininfo.ui
    +pidgin/resources/Log/log-viewer.ui
    +pidgin/resources/Prefs/prefs.ui
    +pidgin/win32/gtkdocklet-win32.c
    +pidgin/win32/gtkwin32dep.c
    +pidgin/win32/MinimizeToTray.c
    +pidgin/win32/untar.c
    +pidgin/win32/winpidgin.c
    --- a/po/meson.build Tue Oct 08 21:47:58 2019 -0500
    +++ b/po/meson.build Tue Oct 08 21:48:28 2019 -0500
    @@ -1,1 +1,1 @@
    -i18n.gettext('pidgin')
    +i18n.gettext('pidgin', args : '--from-code=utf-8')
    --- a/subprojects/gplugin.wrap Tue Oct 08 21:47:58 2019 -0500
    +++ b/subprojects/gplugin.wrap Tue Oct 08 21:48:28 2019 -0500
    @@ -1,4 +1,4 @@
    [wrap-hg]
    directory = gplugin
    url = https://bitbucket.com/gplugin/gplugin
    -revision = default
    +revision = develop
    --- a/valgrind-suppressions Tue Oct 08 21:47:58 2019 -0500
    +++ b/valgrind-suppressions Tue Oct 08 21:48:28 2019 -0500
    @@ -159,40 +159,6 @@
    ...
    }
    {
    - webkitgtk uninitialized values
    - Memcheck:Cond
    - ...
    - obj:/usr/lib/libwebkitgtk-*
    - ...
    -}
    -{
    - webkitgtk uninitialized values 2
    - Memcheck:Value4
    - ...
    - obj:/usr/lib/libwebkitgtk-*
    - ...
    -}
    -{
    - webkitgtk uninitialized values 3
    - Memcheck:Cond
    - ...
    - fun:_ZN3JSC17ConservativeRoots14genericAddSpanINS_13DummyMarkHookEEEvPvS3_RT_
    - ...
    -}
    -{
    - webkitgtk uninitialized values 4
    - Memcheck:Value4
    - ...
    - fun:_ZN3JSC17ConservativeRoots14genericAddSpanINS_13DummyMarkHookEEEvPvS3_RT_
    - ...
    -}
    -{
    - webkitgtk uninitialized values 5
    - Memcheck:Value4
    - ...
    - obj:/usr/lib/libjavascriptcoregtk-
    -}
    -{
    wcslen_sse2 optimization
    Memcheck:Addr8
    fun:__wcslen_sse2